summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:07:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:07:13 +0000
commit636c7dc17286d93d788c741d15fd756aeda066d5 (patch)
treee7ae158cc54f591041a061b9865bcae51854f15c
parentInitial commit. (diff)
downloadapt-636c7dc17286d93d788c741d15fd756aeda066d5.tar.xz
apt-636c7dc17286d93d788c741d15fd756aeda066d5.zip
Adding upstream version 1.8.2.3.upstream/1.8.2.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--.clang-format25
-rw-r--r--.gitlab-ci.yml36
-rw-r--r--.travis.yml23
-rw-r--r--AUTHORS63
-rw-r--r--CMake/CheckCxxTarget.cmake35
-rw-r--r--CMake/Documentation.cmake328
-rw-r--r--CMake/FindBerkeleyDB.cmake59
-rw-r--r--CMake/FindIconv.cmake20
-rw-r--r--CMake/FindLFS.cmake148
-rw-r--r--CMake/FindLZ4.cmake25
-rw-r--r--CMake/FindLZMA.cmake25
-rw-r--r--CMake/FindSeccomp.cmake25
-rw-r--r--CMake/FindSystemd.cmake24
-rw-r--r--CMake/FindUdev.cmake25
-rw-r--r--CMake/FindZstd.cmake25
-rw-r--r--CMake/Misc.cmake101
-rw-r--r--CMake/Translations.cmake185
-rw-r--r--CMake/apti18n.h.in29
-rw-r--r--CMake/config.h.in90
-rw-r--r--CMake/endian.h.in9
-rwxr-xr-xCMake/run_if_exists.sh16
-rw-r--r--CMake/statvfs.h.in13
-rw-r--r--CMake/vendor_substitute.cmake8
-rw-r--r--CMakeLists.txt260
-rw-r--r--COPYING22
-rw-r--r--COPYING.GPL339
-rw-r--r--Dockerfile12
-rw-r--r--README.md215
-rw-r--r--abicheck/apt_build.xml.in12
-rw-r--r--abicheck/apt_installed.xml.in11
-rwxr-xr-xabicheck/run_abi_test22
-rw-r--r--apt-inst/CMakeLists.txt28
-rw-r--r--apt-inst/contrib/arfile.cc179
-rw-r--r--apt-inst/contrib/arfile.h69
-rw-r--r--apt-inst/contrib/extracttar.cc329
-rw-r--r--apt-inst/contrib/extracttar.h61
-rw-r--r--apt-inst/deb/debfile.cc265
-rw-r--r--apt-inst/deb/debfile.h95
-rw-r--r--apt-inst/dirstream.cc118
-rw-r--r--apt-inst/dirstream.h57
-rw-r--r--apt-inst/dpkg-diffs.txt5
-rw-r--r--apt-inst/extract.cc514
-rw-r--r--apt-inst/extract.h49
-rw-r--r--apt-inst/filelist.cc586
-rw-r--r--apt-inst/filelist.h312
-rw-r--r--apt-pkg/CMakeLists.txt76
-rw-r--r--apt-pkg/acquire-item.cc4078
-rw-r--r--apt-pkg/acquire-item.h1250
-rw-r--r--apt-pkg/acquire-method.cc565
-rw-r--r--apt-pkg/acquire-method.h140
-rw-r--r--apt-pkg/acquire-worker.cc964
-rw-r--r--apt-pkg/acquire-worker.h339
-rw-r--r--apt-pkg/acquire.cc1486
-rw-r--r--apt-pkg/acquire.h886
-rw-r--r--apt-pkg/algorithms.cc1418
-rw-r--r--apt-pkg/algorithms.h162
-rw-r--r--apt-pkg/aptconfiguration.cc483
-rw-r--r--apt-pkg/aptconfiguration.h128
-rw-r--r--apt-pkg/cachefile.cc363
-rw-r--r--apt-pkg/cachefile.h95
-rw-r--r--apt-pkg/cachefilter.cc264
-rw-r--r--apt-pkg/cachefilter.h150
-rw-r--r--apt-pkg/cacheiterators.h531
-rw-r--r--apt-pkg/cacheset.cc934
-rw-r--r--apt-pkg/cacheset.h1229
-rw-r--r--apt-pkg/cdrom.cc1000
-rw-r--r--apt-pkg/cdrom.h135
-rw-r--r--apt-pkg/clean.cc151
-rw-r--r--apt-pkg/clean.h49
-rw-r--r--apt-pkg/contrib/cdromutl.cc293
-rw-r--r--apt-pkg/contrib/cdromutl.h25
-rw-r--r--apt-pkg/contrib/cmndline.cc445
-rw-r--r--apt-pkg/contrib/cmndline.h118
-rw-r--r--apt-pkg/contrib/configuration.cc1209
-rw-r--r--apt-pkg/contrib/configuration.h155
-rw-r--r--apt-pkg/contrib/crc-16.cc77
-rw-r--r--apt-pkg/contrib/crc-16.h19
-rw-r--r--apt-pkg/contrib/error.cc244
-rw-r--r--apt-pkg/contrib/error.h365
-rw-r--r--apt-pkg/contrib/fileutl.cc3443
-rw-r--r--apt-pkg/contrib/fileutl.h305
-rw-r--r--apt-pkg/contrib/gpgv.cc580
-rw-r--r--apt-pkg/contrib/gpgv.h92
-rw-r--r--apt-pkg/contrib/hashes.cc408
-rw-r--r--apt-pkg/contrib/hashes.h250
-rw-r--r--apt-pkg/contrib/hashsum.cc52
-rw-r--r--apt-pkg/contrib/hashsum_template.h141
-rw-r--r--apt-pkg/contrib/macros.h173
-rw-r--r--apt-pkg/contrib/md5.cc279
-rw-r--r--apt-pkg/contrib/md5.h58
-rw-r--r--apt-pkg/contrib/mmap.cc502
-rw-r--r--apt-pkg/contrib/mmap.h120
-rw-r--r--apt-pkg/contrib/netrc.cc195
-rw-r--r--apt-pkg/contrib/netrc.h39
-rw-r--r--apt-pkg/contrib/progress.cc221
-rw-r--r--apt-pkg/contrib/progress.h89
-rw-r--r--apt-pkg/contrib/proxy.cc97
-rw-r--r--apt-pkg/contrib/proxy.h16
-rw-r--r--apt-pkg/contrib/sha1.cc273
-rw-r--r--apt-pkg/contrib/sha1.h47
-rw-r--r--apt-pkg/contrib/sha2.h108
-rw-r--r--apt-pkg/contrib/sha256.h8
-rw-r--r--apt-pkg/contrib/sha2_internal.cc1089
-rw-r--r--apt-pkg/contrib/sha2_internal.h188
-rw-r--r--apt-pkg/contrib/sptr.h74
-rw-r--r--apt-pkg/contrib/srvrec.cc204
-rw-r--r--apt-pkg/contrib/srvrec.h54
-rw-r--r--apt-pkg/contrib/string_view.h132
-rw-r--r--apt-pkg/contrib/strutl.cc1840
-rw-r--r--apt-pkg/contrib/strutl.h245
-rw-r--r--apt-pkg/contrib/weakptr.h64
-rw-r--r--apt-pkg/deb/debindexfile.cc431
-rw-r--r--apt-pkg/deb/debindexfile.h197
-rw-r--r--apt-pkg/deb/deblistparser.cc1066
-rw-r--r--apt-pkg/deb/deblistparser.h159
-rw-r--r--apt-pkg/deb/debmetaindex.cc1316
-rw-r--r--apt-pkg/deb/debmetaindex.h78
-rw-r--r--apt-pkg/deb/debrecords.cc227
-rw-r--r--apt-pkg/deb/debrecords.h92
-rw-r--r--apt-pkg/deb/debsrcrecords.cc324
-rw-r--r--apt-pkg/deb/debsrcrecords.h68
-rw-r--r--apt-pkg/deb/debsystem.cc485
-rw-r--r--apt-pkg/deb/debsystem.h61
-rw-r--r--apt-pkg/deb/debversion.cc279
-rw-r--r--apt-pkg/deb/debversion.h41
-rw-r--r--apt-pkg/deb/dpkgpm.cc2491
-rw-r--r--apt-pkg/deb/dpkgpm.h144
-rw-r--r--apt-pkg/depcache.cc2040
-rw-r--r--apt-pkg/depcache.h528
-rw-r--r--apt-pkg/edsp.cc1505
-rw-r--r--apt-pkg/edsp.h275
-rw-r--r--apt-pkg/edsp/edspindexfile.cc129
-rw-r--r--apt-pkg/edsp/edspindexfile.h62
-rw-r--r--apt-pkg/edsp/edsplistparser.cc178
-rw-r--r--apt-pkg/edsp/edsplistparser.h64
-rw-r--r--apt-pkg/edsp/edspsystem.cc166
-rw-r--r--apt-pkg/edsp/edspsystem.h67
-rw-r--r--apt-pkg/indexcopy.cc796
-rw-r--r--apt-pkg/indexcopy.h129
-rw-r--r--apt-pkg/indexfile.cc412
-rw-r--r--apt-pkg/indexfile.h229
-rw-r--r--apt-pkg/init.cc289
-rw-r--r--apt-pkg/init.h32
-rw-r--r--apt-pkg/install-progress.cc425
-rw-r--r--apt-pkg/install-progress.h176
-rw-r--r--apt-pkg/metaindex.cc176
-rw-r--r--apt-pkg/metaindex.h131
-rw-r--r--apt-pkg/orderlist.cc1138
-rw-r--r--apt-pkg/orderlist.h127
-rw-r--r--apt-pkg/packagemanager.cc1186
-rw-r--r--apt-pkg/packagemanager.h153
-rw-r--r--apt-pkg/pkgcache.cc1108
-rw-r--r--apt-pkg/pkgcache.h831
-rw-r--r--apt-pkg/pkgcachegen.cc1860
-rw-r--r--apt-pkg/pkgcachegen.h252
-rw-r--r--apt-pkg/pkgrecords.cc79
-rw-r--r--apt-pkg/pkgrecords.h115
-rw-r--r--apt-pkg/pkgsystem.cc113
-rw-r--r--apt-pkg/pkgsystem.h141
-rw-r--r--apt-pkg/policy.cc442
-rw-r--r--apt-pkg/policy.h96
-rw-r--r--apt-pkg/prettyprinters.cc123
-rw-r--r--apt-pkg/prettyprinters.h37
-rw-r--r--apt-pkg/sourcelist.cc643
-rw-r--r--apt-pkg/sourcelist.h144
-rw-r--r--apt-pkg/srcrecords.cc182
-rw-r--r--apt-pkg/srcrecords.h120
-rw-r--r--apt-pkg/statechanges.cc227
-rw-r--r--apt-pkg/statechanges.h58
-rw-r--r--apt-pkg/tagfile-compat.cc75
-rw-r--r--apt-pkg/tagfile-keys.list83
-rw-r--r--apt-pkg/tagfile-order.c113
-rw-r--r--apt-pkg/tagfile.cc1210
-rw-r--r--apt-pkg/tagfile.h241
-rw-r--r--apt-pkg/update.cc133
-rw-r--r--apt-pkg/update.h22
-rw-r--r--apt-pkg/upgrade.cc301
-rw-r--r--apt-pkg/upgrade.h35
-rw-r--r--apt-pkg/version.cc44
-rw-r--r--apt-pkg/version.h60
-rw-r--r--apt-pkg/versionmatch.cc290
-rw-r--r--apt-pkg/versionmatch.h80
-rw-r--r--apt-private/CMakeLists.txt26
-rw-r--r--apt-private/acqprogress.cc366
-rw-r--r--apt-private/acqprogress.h45
-rw-r--r--apt-private/private-cachefile.cc118
-rw-r--r--apt-private/private-cachefile.h72
-rw-r--r--apt-private/private-cacheset.cc372
-rw-r--r--apt-private/private-cacheset.h126
-rw-r--r--apt-private/private-cmndline.cc577
-rw-r--r--apt-private/private-cmndline.h41
-rw-r--r--apt-private/private-depends.cc151
-rw-r--r--apt-private/private-depends.h11
-rw-r--r--apt-private/private-download.cc378
-rw-r--r--apt-private/private-download.h38
-rw-r--r--apt-private/private-install.cc1111
-rw-r--r--apt-private/private-install.h75
-rw-r--r--apt-private/private-json-hooks.cc431
-rw-r--r--apt-private/private-json-hooks.h14
-rw-r--r--apt-private/private-list.cc155
-rw-r--r--apt-private/private-list.h11
-rw-r--r--apt-private/private-main.cc90
-rw-r--r--apt-private/private-main.h16
-rw-r--r--apt-private/private-moo.cc200
-rw-r--r--apt-private/private-moo.h10
-rw-r--r--apt-private/private-output.cc801
-rw-r--r--apt-private/private-output.h116
-rw-r--r--apt-private/private-search.cc349
-rw-r--r--apt-private/private-search.h12
-rw-r--r--apt-private/private-show.cc580
-rw-r--r--apt-private/private-show.h22
-rw-r--r--apt-private/private-source.cc820
-rw-r--r--apt-private/private-source.h11
-rw-r--r--apt-private/private-sources.cc105
-rw-r--r--apt-private/private-sources.h10
-rw-r--r--apt-private/private-unmet.cc120
-rw-r--r--apt-private/private-unmet.h10
-rw-r--r--apt-private/private-update.cc147
-rw-r--r--apt-private/private-update.h10
-rw-r--r--apt-private/private-upgrade.cc66
-rw-r--r--apt-private/private-upgrade.h13
-rw-r--r--apt-private/private-utils.cc98
-rw-r--r--apt-private/private-utils.h10
-rw-r--r--cmdline/CMakeLists.txt59
-rw-r--r--cmdline/apt-cache.cc1151
-rw-r--r--cmdline/apt-cdrom.cc235
-rw-r--r--cmdline/apt-config.cc144
-rw-r--r--cmdline/apt-dump-solver.cc185
-rw-r--r--cmdline/apt-extracttemplates.cc335
-rw-r--r--cmdline/apt-extracttemplates.h49
-rw-r--r--cmdline/apt-get.cc446
-rw-r--r--cmdline/apt-helper.cc271
-rw-r--r--cmdline/apt-internal-planner.cc195
-rw-r--r--cmdline/apt-internal-solver.cc213
-rw-r--r--cmdline/apt-key.in827
-rw-r--r--cmdline/apt-mark.cc447
-rwxr-xr-xcmdline/apt-report-mirror-failure29
-rw-r--r--cmdline/apt-sortpkgs.cc165
-rw-r--r--cmdline/apt.cc121
-rw-r--r--completions/CMakeLists.txt4
-rw-r--r--completions/bash/apt227
-rw-r--r--doc/CMakeLists.txt98
-rw-r--r--doc/Doxyfile.in2313
-rw-r--r--doc/acquire-additional-files.md325
-rw-r--r--doc/apt-cache.8.xml392
-rw-r--r--doc/apt-cdrom.8.xml161
-rw-r--r--doc/apt-config.8.xml118
-rw-r--r--doc/apt-extracttemplates.1.xml76
-rw-r--r--doc/apt-ftparchive.1.xml627
-rw-r--r--doc/apt-get.8.xml648
-rw-r--r--doc/apt-key.8.xml216
-rw-r--r--doc/apt-mark.8.xml163
-rw-r--r--doc/apt-secure.8.xml272
-rw-r--r--doc/apt-sortpkgs.1.xml69
-rw-r--r--doc/apt-transport-http.1.xml138
-rw-r--r--doc/apt-transport-https.1.xml133
-rw-r--r--doc/apt-transport-mirror.1.xml150
-rw-r--r--doc/apt-verbatim.ent490
-rw-r--r--doc/apt.8.xml177
-rw-r--r--doc/apt.conf.5.xml1272
-rw-r--r--doc/apt.ent243
-rw-r--r--doc/apt_auth.conf.5.xml138
-rw-r--r--doc/apt_preferences.5.xml723
-rw-r--r--doc/design.dbk439
-rw-r--r--doc/docbook-html-style.xsl.cmake.in40
-rw-r--r--doc/docbook-text-style.xsl.cmake.in70
-rw-r--r--doc/dpkg-tech.dbk870
-rw-r--r--doc/examples/CMakeLists.txt4
-rw-r--r--doc/examples/apt-ftparchive.conf46
-rw-r--r--doc/examples/apt.conf31
-rw-r--r--doc/examples/configure-index824
-rw-r--r--doc/examples/ftp-archive.conf81
-rw-r--r--doc/examples/preferences11
-rw-r--r--doc/external-dependency-solver-protocol.md377
-rw-r--r--doc/external-installation-planner-protocol.md301
-rw-r--r--doc/files.dbk393
-rw-r--r--doc/guide.dbk561
-rw-r--r--doc/json-hooks-protocol.md159
-rw-r--r--doc/libapt-pkg2_to_3.txt90
-rw-r--r--doc/manpage-style.xsl.cmake.in18
-rw-r--r--doc/method.dbk732
-rw-r--r--doc/offline.dbk248
-rw-r--r--doc/po/apt-doc.pot8805
-rw-r--r--doc/po/de.po12701
-rw-r--r--doc/po/es.po12771
-rw-r--r--doc/po/fr.po13672
-rw-r--r--doc/po/header.pot.txt3
-rw-r--r--doc/po/it.po14510
-rw-r--r--doc/po/ja.po12001
-rw-r--r--doc/po/nl.po13482
-rw-r--r--doc/po/pl.po11783
-rw-r--r--doc/po/pt.po13093
-rw-r--r--doc/po/pt_BR.po9319
-rw-r--r--doc/po4a.conf48
-rw-r--r--doc/progress-reporting.md76
-rw-r--r--doc/sources.list.5.xml586
-rw-r--r--doc/srv-records-support.md22
-rw-r--r--doc/style.txt75
-rw-r--r--doc/xml.add5
-rw-r--r--dselect/CMakeLists.txt4
-rw-r--r--dselect/desc.apt9
-rwxr-xr-xdselect/install111
-rw-r--r--dselect/names1
-rwxr-xr-xdselect/setup286
-rwxr-xr-xdselect/update48
-rw-r--r--ftparchive/CMakeLists.txt13
-rw-r--r--ftparchive/apt-ftparchive.cc1058
-rw-r--r--ftparchive/apt-ftparchive.h26
-rw-r--r--ftparchive/byhash.cc61
-rw-r--r--ftparchive/byhash.h25
-rw-r--r--ftparchive/cachedb.cc582
-rw-r--r--ftparchive/cachedb.h195
-rw-r--r--ftparchive/contents.cc409
-rw-r--r--ftparchive/contents.h92
-rw-r--r--ftparchive/multicompress.cc363
-rw-r--r--ftparchive/multicompress.h62
-rw-r--r--ftparchive/override.cc288
-rw-r--r--ftparchive/override.h48
-rw-r--r--ftparchive/sources.cc60
-rw-r--r--ftparchive/sources.h24
-rw-r--r--ftparchive/writer.cc1205
-rw-r--r--ftparchive/writer.h208
-rwxr-xr-xgit-clang-format.sh10
-rw-r--r--methods/CMakeLists.txt33
-rw-r--r--methods/aptmethod.h547
-rw-r--r--methods/basehttp.cc909
-rw-r--r--methods/basehttp.h173
-rw-r--r--methods/cdrom.cc290
-rw-r--r--methods/connect.cc1052
-rw-r--r--methods/connect.h49
-rw-r--r--methods/copy.cc94
-rw-r--r--methods/file.cc134
-rw-r--r--methods/ftp.cc1188
-rw-r--r--methods/ftp.h91
-rw-r--r--methods/gpgv.cc522
-rw-r--r--methods/http.cc1051
-rw-r--r--methods/http.h142
-rw-r--r--methods/mirror.cc406
-rw-r--r--methods/rfc2553emu.cc244
-rw-r--r--methods/rfc2553emu.h112
-rw-r--r--methods/rred.cc788
-rw-r--r--methods/rsh.cc547
-rw-r--r--methods/rsh.h75
-rw-r--r--methods/store.cc147
-rw-r--r--mirror-failure.py23
-rw-r--r--po/CMakeLists.txt52
-rw-r--r--po/apt-all.pot3613
-rw-r--r--po/ar.po3849
-rw-r--r--po/ast.po4345
-rw-r--r--po/bg.po4467
-rw-r--r--po/bs.po3724
-rw-r--r--po/ca.po4042
-rw-r--r--po/cs.po4484
-rw-r--r--po/cy.po4371
-rw-r--r--po/da.po3971
-rw-r--r--po/de.po4625
-rw-r--r--po/dz.po4301
-rw-r--r--po/el.po4334
-rw-r--r--po/es.po5062
-rw-r--r--po/eu.po4264
-rw-r--r--po/fi.po4290
-rw-r--r--po/fr.po4679
-rw-r--r--po/gl.po4368
-rw-r--r--po/he.po2836
-rw-r--r--po/hu.po4476
-rw-r--r--po/it.po4027
-rw-r--r--po/ja.po3981
-rw-r--r--po/km.po4301
-rw-r--r--po/ko.po4261
-rw-r--r--po/ku.po3789
-rw-r--r--po/lt.po3987
-rw-r--r--po/mr.po4259
-rw-r--r--po/nb.po4409
-rw-r--r--po/ne.po4268
-rw-r--r--po/nl.po4543
-rw-r--r--po/nn.po4292
-rw-r--r--po/pl.po4495
-rw-r--r--po/pt.po4600
-rw-r--r--po/pt_BR.po4403
-rw-r--r--po/ro.po4346
-rw-r--r--po/ru.po4565
-rw-r--r--po/sk.po4376
-rw-r--r--po/sl.po4382
-rw-r--r--po/sv.po4511
-rw-r--r--po/th.po4317
-rw-r--r--po/tl.po4341
-rw-r--r--po/tr.po4368
-rw-r--r--po/uk.po4498
-rw-r--r--po/vi.po4488
-rw-r--r--po/zh_CN.po4364
-rw-r--r--po/zh_TW.po4197
-rwxr-xr-xprepare-release378
-rw-r--r--shippable.yml18
-rw-r--r--test/CMakeLists.txt2
-rw-r--r--test/integration/Packages-bug-590438-broken-provides-thanks-to-remove-order67
-rw-r--r--test/integration/Packages-bug-591882-conkeror1787
-rw-r--r--test/integration/Packages-bug-593360-modifiers-in-names42
-rw-r--r--test/integration/Packages-bug-598669-install-postfix-gets-exim-heavy30
-rw-r--r--test/integration/Packages-bug-605394-versioned-or-groups71
-rw-r--r--test/integration/Packages-bug-723705-tagfile-truncates-fields167
-rw-r--r--test/integration/Packages-bug-lp1347721-dpkg-ordering445
-rw-r--r--test/integration/Packages-github-23-too-long-dependency-line17
-rw-r--r--test/integration/Packages-hashsum-verification18
-rw-r--r--test/integration/Packages-pdiff-usage36
-rw-r--r--test/integration/Packages-pdiff-usage-new39
-rw-r--r--test/integration/Packages-policy-pinning12
-rw-r--r--test/integration/Packages-releasefile-verification20
-rw-r--r--test/integration/Packages-releasefile-verification-new23
-rw-r--r--test/integration/Packages-ubuntu-bug-6149931819
-rw-r--r--test/integration/Packages-xorg-break-providers52
-rw-r--r--test/integration/apt.pem49
-rw-r--r--test/integration/cachedb-lp1274466-old-format.dbbin0 -> 8192 bytes
-rwxr-xr-xtest/integration/create-test-data60
-rw-r--r--test/integration/deb-bug-330162-encoded-tar-header.debbin0 -> 544 bytes
-rw-r--r--test/integration/deb-lp1274466-cachedb.debbin0 -> 1270 bytes
-rw-r--r--test/integration/framework2156
-rwxr-xr-xtest/integration/header-compilation26
-rw-r--r--test/integration/joesixpack.pubbin0 -> 639 bytes
-rw-r--r--test/integration/joesixpack.secbin0 -> 1290 bytes
-rw-r--r--test/integration/keyrings/test-archive-keyring.pubbin0 -> 979 bytes
-rw-r--r--test/integration/keyrings/test-archive-keyring.secbin0 -> 1642 bytes
-rw-r--r--test/integration/keyrings/test-master-keyring.pubbin0 -> 1199 bytes
-rw-r--r--test/integration/keyrings/test-master-keyring.secbin0 -> 2501 bytes
-rw-r--r--test/integration/marvinparanoid.pubbin0 -> 624 bytes
-rw-r--r--test/integration/marvinparanoid.secbin0 -> 1275 bytes
-rw-r--r--test/integration/rexexpired.pubbin0 -> 1191 bytes
-rw-r--r--test/integration/rexexpired.secbin0 -> 2493 bytes
-rwxr-xr-xtest/integration/run-tests164
-rw-r--r--test/integration/sebastiansubkey.master.secbin0 -> 4829 bytes
-rw-r--r--test/integration/sebastiansubkey.pubbin0 -> 2567 bytes
-rw-r--r--test/integration/sebastiansubkey.secbin0 -> 3546 bytes
-rwxr-xr-xtest/integration/skip-acquire-same-repository-multiple-times85
-rwxr-xr-xtest/integration/skip-apt-download-progress48
-rwxr-xr-xtest/integration/skip-apt-dropprivs32
-rwxr-xr-xtest/integration/skip-avoid-avoiding-breaks-predepends21
-rwxr-xr-xtest/integration/skip-bug-601016-description-translation133
-rwxr-xr-xtest/integration/skip-bug-711456-apt-cdrom-multiple-cds-multiarch47
-rwxr-xr-xtest/integration/skip-method-http-socks-client181
-rw-r--r--test/integration/status-bug-590438-broken-provides-thanks-to-remove-order97
-rw-r--r--test/integration/status-bug-591882-conkeror1357
-rw-r--r--test/integration/status-bug-598669-install-postfix-gets-exim-heavy30
-rw-r--r--test/integration/status-bug-601961-install-info42
-rw-r--r--test/integration/status-bug-605394-versioned-or-groups34
-rw-r--r--test/integration/status-bug-612557-garbage-upgrade34
-rw-r--r--test/integration/status-bug-613420-new-garbage-dependency22
-rw-r--r--test/integration/status-bug-723705-tagfile-truncates-fields62
-rw-r--r--test/integration/status-bug-lp1347721-dpkg-ordering1942
-rw-r--r--test/integration/status-github-23-too-long-dependency-line12
-rw-r--r--test/integration/status-ubuntu-bug-6149935894
-rw-r--r--test/integration/status-xorg-break-providers22
-rwxr-xr-xtest/integration/test-00-commands-have-help101
-rwxr-xr-xtest/integration/test-acquire-binary-all142
-rwxr-xr-xtest/integration/test-acquire-same-file-multiple-times90
-rwxr-xr-xtest/integration/test-allow98
-rwxr-xr-xtest/integration/test-allow-scores-for-all-dependency-types179
-rwxr-xr-xtest/integration/test-apt-acquire-additional-files246
-rwxr-xr-xtest/integration/test-apt-acquire-additional-files-duplicates105
-rwxr-xr-xtest/integration/test-apt-by-hash-update107
-rwxr-xr-xtest/integration/test-apt-cache183
-rwxr-xr-xtest/integration/test-apt-cache-remapping47
-rwxr-xr-xtest/integration/test-apt-cache-showsrc31
-rwxr-xr-xtest/integration/test-apt-cdrom158
-rwxr-xr-xtest/integration/test-apt-cli-json-hooks209
-rwxr-xr-xtest/integration/test-apt-cli-list93
-rwxr-xr-xtest/integration/test-apt-cli-search79
-rwxr-xr-xtest/integration/test-apt-cli-show111
-rwxr-xr-xtest/integration/test-apt-cli-update23
-rwxr-xr-xtest/integration/test-apt-cli-upgrade32
-rwxr-xr-xtest/integration/test-apt-config50
-rwxr-xr-xtest/integration/test-apt-edit-sources39
-rwxr-xr-xtest/integration/test-apt-extracttemplates78
-rwxr-xr-xtest/integration/test-apt-ftparchive64
-rwxr-xr-xtest/integration/test-apt-ftparchive-by-hash70
-rwxr-xr-xtest/integration/test-apt-ftparchive-cachedb103
-rwxr-xr-xtest/integration/test-apt-ftparchive-cachedb-lp127446659
-rwxr-xr-xtest/integration/test-apt-ftparchive-notautomatic54
-rwxr-xr-xtest/integration/test-apt-ftparchive-src-cachedb191
-rwxr-xr-xtest/integration/test-apt-get-autoremove238
-rwxr-xr-xtest/integration/test-apt-get-build-dep149
-rwxr-xr-xtest/integration/test-apt-get-build-dep-file209
-rwxr-xr-xtest/integration/test-apt-get-changelog139
-rwxr-xr-xtest/integration/test-apt-get-clean88
-rwxr-xr-xtest/integration/test-apt-get-download113
-rwxr-xr-xtest/integration/test-apt-get-install-deb245
-rwxr-xr-xtest/integration/test-apt-get-install-virtual-pkgs64
-rwxr-xr-xtest/integration/test-apt-get-source-arch68
-rwxr-xr-xtest/integration/test-apt-get-source-authenticated33
-rwxr-xr-xtest/integration/test-apt-get-source-multisources29
-rwxr-xr-xtest/integration/test-apt-get-update-sourceslist-warning45
-rwxr-xr-xtest/integration/test-apt-get-update-unauth-warning94
-rwxr-xr-xtest/integration/test-apt-get-upgrade144
-rwxr-xr-xtest/integration/test-apt-helper132
-rwxr-xr-xtest/integration/test-apt-helper-cat-file46
-rwxr-xr-xtest/integration/test-apt-https-no-redirect48
-rwxr-xr-xtest/integration/test-apt-install-file-reltag94
-rwxr-xr-xtest/integration/test-apt-keep-downloaded-pkgs54
-rwxr-xr-xtest/integration/test-apt-key428
-rwxr-xr-xtest/integration/test-apt-key-net-update41
-rwxr-xr-xtest/integration/test-apt-key-used-in-maintainerscript43
-rwxr-xr-xtest/integration/test-apt-mark154
-rwxr-xr-xtest/integration/test-apt-move-and-forget-manual-sections31
-rwxr-xr-xtest/integration/test-apt-never-markauto-sections86
-rwxr-xr-xtest/integration/test-apt-progress-fd80
-rwxr-xr-xtest/integration/test-apt-progress-fd-conffile42
-rwxr-xr-xtest/integration/test-apt-progress-fd-deb82258
-rwxr-xr-xtest/integration/test-apt-progress-fd-error25
-rwxr-xr-xtest/integration/test-apt-redirect-loop20
-rwxr-xr-xtest/integration/test-apt-showlist-orgroup-in-recommends200
-rwxr-xr-xtest/integration/test-apt-source-and-build-dep250
-rwxr-xr-xtest/integration/test-apt-sources-deb822361
-rwxr-xr-xtest/integration/test-apt-tagfile-fields-order82
-rwxr-xr-xtest/integration/test-apt-translation-has-no-packages35
-rwxr-xr-xtest/integration/test-apt-update-disappeared-component45
-rwxr-xr-xtest/integration/test-apt-update-empty-files47
-rwxr-xr-xtest/integration/test-apt-update-expected-size99
-rwxr-xr-xtest/integration/test-apt-update-failure-propagation90
-rwxr-xr-xtest/integration/test-apt-update-file79
-rwxr-xr-xtest/integration/test-apt-update-filesize-mismatch54
-rwxr-xr-xtest/integration/test-apt-update-hashsum-mismatch39
-rwxr-xr-xtest/integration/test-apt-update-hooks49
-rwxr-xr-xtest/integration/test-apt-update-ims192
-rwxr-xr-xtest/integration/test-apt-update-nofallback274
-rwxr-xr-xtest/integration/test-apt-update-not-modified183
-rwxr-xr-xtest/integration/test-apt-update-releaseinfo-changes94
-rwxr-xr-xtest/integration/test-apt-update-reporting35
-rwxr-xr-xtest/integration/test-apt-update-rollback219
-rwxr-xr-xtest/integration/test-apt-update-simple33
-rwxr-xr-xtest/integration/test-apt-update-stale44
-rwxr-xr-xtest/integration/test-apt-update-transactions85
-rwxr-xr-xtest/integration/test-apt-update-unauth80
-rwxr-xr-xtest/integration/test-apt-update-weak-hashes202
-rwxr-xr-xtest/integration/test-architecture-specification-parsing120
-rwxr-xr-xtest/integration/test-authentication-basic127
-rwxr-xr-xtest/integration/test-bug-254770-segfault-if-cache-not-buildable64
-rwxr-xr-xtest/integration/test-bug-330162-encoded-tar-header11
-rwxr-xr-xtest/integration/test-bug-407511-fail-invalid-default-release66
-rwxr-xr-xtest/integration/test-bug-470115-new-and-tighten-recommends174
-rwxr-xr-xtest/integration/test-bug-507998-dist-upgrade-recommends26
-rwxr-xr-xtest/integration/test-bug-543966-downgrade-below-1000-pin90
-rwxr-xr-xtest/integration/test-bug-549968-install-depends-of-not-installed28
-rwxr-xr-xtest/integration/test-bug-590041-prefer-non-virtual-packages54
-rwxr-xr-xtest/integration/test-bug-590438-broken-provides-thanks-to-remove-order96
-rwxr-xr-xtest/integration/test-bug-591882-conkeror77
-rwxr-xr-xtest/integration/test-bug-593360-modifiers-in-names69
-rwxr-xr-xtest/integration/test-bug-595691-empty-and-broken-archive-files64
-rwxr-xr-xtest/integration/test-bug-596498-trusted-unsigned-repo75
-rwxr-xr-xtest/integration/test-bug-598669-install-postfix-gets-exim-heavy19
-rwxr-xr-xtest/integration/test-bug-601961-install-info38
-rwxr-xr-xtest/integration/test-bug-602412-dequote-redirect64
-rwxr-xr-xtest/integration/test-bug-604222-new-and-autoremove121
-rwxr-xr-xtest/integration/test-bug-604401-files-are-directories67
-rwxr-xr-xtest/integration/test-bug-605394-versioned-or-groups30
-rwxr-xr-xtest/integration/test-bug-611729-mark-as-manual96
-rwxr-xr-xtest/integration/test-bug-612099-multiarch-conflicts225
-rwxr-xr-xtest/integration/test-bug-612557-garbage-upgrade60
-rwxr-xr-xtest/integration/test-bug-612958-use-dpkg-multiarch-config83
-rwxr-xr-xtest/integration/test-bug-613420-new-garbage-dependency56
-rwxr-xr-xtest/integration/test-bug-617690-allow-unauthenticated-makes-all-untrusted68
-rwxr-xr-xtest/integration/test-bug-618288-multiarch-same-lockstep41
-rwxr-xr-xtest/integration/test-bug-618848-always-respect-user-requests32
-rwxr-xr-xtest/integration/test-bug-623443-fail-on-bad-proxies34
-rwxr-xr-xtest/integration/test-bug-624218-Translation-file-handling171
-rwxr-xr-xtest/integration/test-bug-632221-cross-dependency-satisfaction348
-rwxr-xr-xtest/integration/test-bug-633350-do-not-kill-last-char-in-Release16
-rwxr-xr-xtest/integration/test-bug-64141-install-dependencies-for-on-hold46
-rwxr-xr-xtest/integration/test-bug-657695-resolver-breaks-on-virtuals52
-rwxr-xr-xtest/integration/test-bug-661537-build-profiles-support161
-rwxr-xr-xtest/integration/test-bug-673536-pre-depends-breaks-loop36
-rwxr-xr-xtest/integration/test-bug-675449-essential-are-protected103
-rwxr-xr-xtest/integration/test-bug-679371-apt-get-autoclean-multiarch27
-rwxr-xr-xtest/integration/test-bug-680041-apt-mark-holds-correctly76
-rwxr-xr-xtest/integration/test-bug-683786-build-dep-on-virtual-packages107
-rwxr-xr-xtest/integration/test-bug-686346-package-missing-architecture113
-rwxr-xr-xtest/integration/test-bug-689582-100-char-long-path-names35
-rwxr-xr-xtest/integration/test-bug-691453-apt-cache-search-multi-pattern33
-rwxr-xr-xtest/integration/test-bug-709560-set-candidate-release36
-rwxr-xr-xtest/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch152
-rwxr-xr-xtest/integration/test-bug-712435-missing-descriptions162
-rwxr-xr-xtest/integration/test-bug-717891-abolute-uris-for-proxies53
-rwxr-xr-xtest/integration/test-bug-718329-support-data.tar-uncompressed34
-rwxr-xr-xtest/integration/test-bug-719263-print-uris-removes-authentication42
-rwxr-xr-xtest/integration/test-bug-720597-build-dep-purge65
-rwxr-xr-xtest/integration/test-bug-722207-print-uris-even-if-very-quiet31
-rwxr-xr-xtest/integration/test-bug-723586-any-stripped-in-single-arch54
-rwxr-xr-xtest/integration/test-bug-723705-tagfile-truncates-fields33
-rwxr-xr-xtest/integration/test-bug-728500-tempdir23
-rwxr-xr-xtest/integration/test-bug-732746-preferences32
-rwxr-xr-xtest/integration/test-bug-733028-gpg-resource-limit25
-rwxr-xr-xtest/integration/test-bug-734922-apt-showsrc-duplicate25
-rwxr-xr-xtest/integration/test-bug-735967-lib32-to-i386-unavailable91
-rwxr-xr-xtest/integration/test-bug-737130-multicompress-file-modes30
-rwxr-xr-xtest/integration/test-bug-738785-switch-protocol50
-rwxr-xr-xtest/integration/test-bug-740843-versioned-up-down-breaks57
-rwxr-xr-xtest/integration/test-bug-745036-new-foreign-invalidates-cache27
-rwxr-xr-xtest/integration/test-bug-745046-candidate-propagation-fails39
-rwxr-xr-xtest/integration/test-bug-747261-arch-specific-conflicts46
-rwxr-xr-xtest/integration/test-bug-753297-upgradable35
-rwxr-xr-xtest/integration/test-bug-758153-versioned-provides-support387
-rwxr-xr-xtest/integration/test-bug-762160-relpath17
-rwxr-xr-xtest/integration/test-bug-767891-force-essential-important100
-rwxr-xr-xtest/integration/test-bug-769609-triggers-still-pending-after-run75
-rwxr-xr-xtest/integration/test-bug-770291-reinstall98
-rwxr-xr-xtest/integration/test-bug-778375-server-has-no-reason-phrase43
-rwxr-xr-xtest/integration/test-bug-782777-single-arch-weirdness72
-rwxr-xr-xtest/integration/test-bug-796070-downgrade-realpkg23
-rwxr-xr-xtest/integration/test-bug-796070-downgrade-simulate67
-rwxr-xr-xtest/integration/test-bug-807012-mixed-case-package-names21
-rwxr-xr-xtest/integration/test-bug-814139-pickup-of-different-cache-states60
-rwxr-xr-xtest/integration/test-bug-818628-unreadable-source94
-rwxr-xr-xtest/integration/test-bug-82965127
-rwxr-xr-xtest/integration/test-bug-835094-configure-before-purge44
-rwxr-xr-xtest/integration/test-bug-838779-untrusted-to-trusted-Release-hit48
-rwxr-xr-xtest/integration/test-bug-841874-warning-for-mismatching-distribution49
-rwxr-xr-xtest/integration/test-bug-869859-retry-downloads55
-rwxr-xr-xtest/integration/test-bug-870675-hang-on-unsupported-method26
-rwxr-xr-xtest/integration/test-bug-872963-apt-ftparchive-disable-hashes40
-rwxr-xr-xtest/integration/test-bug-879591-dont-warn-for-hidden-but-good-components25
-rwxr-xr-xtest/integration/test-bug-921685-binary-detached-signature22
-rwxr-xr-xtest/integration/test-bug-lp1347721-dpkg-ordering11
-rwxr-xr-xtest/integration/test-bug-lp1445239-download-loop28
-rwxr-xr-xtest/integration/test-bug-lp1550741-heisestate48
-rwxr-xr-xtest/integration/test-bug-lp1562402-nomark-removals-as-keep39
-rwxr-xr-xtest/integration/test-bug-lp1694697-build-dep-architecture-limited-alternative58
-rwxr-xr-xtest/integration/test-bug-multiarch-upgrade29
-rwxr-xr-xtest/integration/test-cachecontainer-architecture-specification123
-rwxr-xr-xtest/integration/test-compressed-indexes180
-rwxr-xr-xtest/integration/test-conflicts-loop33
-rwxr-xr-xtest/integration/test-conflicts-real-multiarch-same50
-rwxr-xr-xtest/integration/test-crossgrades82
-rwxr-xr-xtest/integration/test-cve-2013-1051-InRelease-parsing71
-rwxr-xr-xtest/integration/test-cve-2018-0501-mirror-alternatives31
-rwxr-xr-xtest/integration/test-cve-2019-3462-Release.gpg-payload43
-rwxr-xr-xtest/integration/test-cve-2019-3462-dequote-injection66
-rwxr-xr-xtest/integration/test-cve-2020-2735025
-rwxr-xr-xtest/integration/test-different-methods-for-same-source35
-rwxr-xr-xtest/integration/test-disappearing-packages92
-rwxr-xr-xtest/integration/test-dpkg-assert-multi-arch104
-rwxr-xr-xtest/integration/test-dpkg-normalization44
-rwxr-xr-xtest/integration/test-dpkg-path35
-rwxr-xr-xtest/integration/test-essential-force-loopbreak54
-rwxr-xr-xtest/integration/test-external-dependency-solver-protocol294
-rwxr-xr-xtest/integration/test-external-installation-planner-protocol93
-rwxr-xr-xtest/integration/test-failing-maintainer-scripts91
-rwxr-xr-xtest/integration/test-frontend-lock80
-rwxr-xr-xtest/integration/test-github-111-invalid-armember88
-rwxr-xr-xtest/integration/test-github-23-too-long-dependency-line17
-rwxr-xr-xtest/integration/test-handle-redirect-as-used-mirror-change94
-rwxr-xr-xtest/integration/test-handling-broken-orgroups108
-rwxr-xr-xtest/integration/test-hashsum-verification82
-rwxr-xr-xtest/integration/test-http-pipeline-messup56
-rwxr-xr-xtest/integration/test-ignore-provides-if-versioned-breaks150
-rwxr-xr-xtest/integration/test-ignore-provides-if-versioned-conflicts150
-rwxr-xr-xtest/integration/test-implicit-conflicts-real-not-virtual56
-rwxr-xr-xtest/integration/test-kernel-helper-autoremove181
-rwxr-xr-xtest/integration/test-method-connect13
-rwxr-xr-xtest/integration/test-method-gpgv185
-rwxr-xr-xtest/integration/test-method-mirror255
-rwxr-xr-xtest/integration/test-method-rred197
-rwxr-xr-xtest/integration/test-multiarch-allowed302
-rwxr-xr-xtest/integration/test-multiarch-foreign287
-rwxr-xr-xtest/integration/test-no-fds-leaked-to-maintainer-scripts179
-rwxr-xr-xtest/integration/test-package-reinstallation23
-rwxr-xr-xtest/integration/test-packages-require-authorization61
-rwxr-xr-xtest/integration/test-parse-all-archs-into-cache93
-rwxr-xr-xtest/integration/test-partial-file-support157
-rwxr-xr-xtest/integration/test-pdiff-usage417
-rwxr-xr-xtest/integration/test-pin-non-existent-package75
-rwxr-xr-xtest/integration/test-policy-pinning433
-rwxr-xr-xtest/integration/test-prefer-higher-priority-providers106
-rwxr-xr-xtest/integration/test-prefer-native-architecture-over-higher-priority25
-rwxr-xr-xtest/integration/test-prevent-markinstall-multiarch-same-versionscrew130
-rwxr-xr-xtest/integration/test-provides-gone-with-upgrade27
-rwxr-xr-xtest/integration/test-release-candidate-switching443
-rwxr-xr-xtest/integration/test-releasefile-date46
-rwxr-xr-xtest/integration/test-releasefile-date-older104
-rwxr-xr-xtest/integration/test-releasefile-valid-until57
-rwxr-xr-xtest/integration/test-releasefile-verification505
-rwxr-xr-xtest/integration/test-releasefile-verification-noflat25
-rwxr-xr-xtest/integration/test-resolve-by-keep-new-recommends21
-rwxr-xr-xtest/integration/test-security-no-remote-status30
-rwxr-xr-xtest/integration/test-signed-by-option33
-rwxr-xr-xtest/integration/test-sourceslist-arch-plusminus-options94
-rwxr-xr-xtest/integration/test-sourceslist-lang-plusminus-options88
-rwxr-xr-xtest/integration/test-sourceslist-target-plusminus-options128
-rwxr-xr-xtest/integration/test-sourceslist-trusted-options218
-rwxr-xr-xtest/integration/test-specific-architecture-dependencies324
-rwxr-xr-xtest/integration/test-srcrecord35
-rwxr-xr-xtest/integration/test-suggest-installed-multiarch-silbing79
-rwxr-xr-xtest/integration/test-ubuntu-bug-1078697-missing-source-hashes38
-rwxr-xr-xtest/integration/test-ubuntu-bug-1098738-apt-get-source-md5sum444
-rwxr-xr-xtest/integration/test-ubuntu-bug-1130419-prefer-installed-ma-same-siblings104
-rwxr-xr-xtest/integration/test-ubuntu-bug-1304403-obsolete-priority-standard50
-rwxr-xr-xtest/integration/test-ubuntu-bug-1549819-empty-arch-list13
-rwxr-xr-xtest/integration/test-ubuntu-bug-1651923-requote-https-uri19
-rwxr-xr-xtest/integration/test-ubuntu-bug-346386-apt-get-update-paywall102
-rwxr-xr-xtest/integration/test-ubuntu-bug-365611-long-package-names15
-rwxr-xr-xtest/integration/test-ubuntu-bug-61499359
-rwxr-xr-xtest/integration/test-ubuntu-bug-761175-remove-purge88
-rwxr-xr-xtest/integration/test-ubuntu-bug-784473-InRelease-one-message-only36
-rwxr-xr-xtest/integration/test-ubuntu-bug-802901-multiarch-early-remove24
-rwxr-xr-xtest/integration/test-ubuntu-bug-806274-install-suggests81
-rwxr-xr-xtest/integration/test-ubuntu-bug-835625-multiarch-lockstep-installed-first45
-rwxr-xr-xtest/integration/test-ubuntu-bug-859188-multiarch-reinstall27
-rwxr-xr-xtest/integration/test-ubuntu-bug-985852-pre-depends-or-group-ordering22
-rwxr-xr-xtest/integration/test-unpack-different-version-unpacked121
-rwxr-xr-xtest/integration/test-very-tight-loop-configure-with-unpacking-new-packages63
-rwxr-xr-xtest/integration/test-xorg-break-providers45
-rw-r--r--test/interactive-helper/CMakeLists.txt17
-rw-r--r--test/interactive-helper/aptdropprivs.cc27
-rw-r--r--test/interactive-helper/aptwebserver.cc1083
-rw-r--r--test/interactive-helper/createdeb-cve-2020-27350.cc325
-rw-r--r--test/interactive-helper/extract-control.cc45
-rw-r--r--test/interactive-helper/libnoprofile.c44
-rw-r--r--test/interactive-helper/mthdcat.cc22
-rw-r--r--test/interactive-helper/networkless-install-fixes/README5
-rw-r--r--test/interactive-helper/networkless-install-fixes/sources.test.list25
-rwxr-xr-xtest/interactive-helper/networkless-install-fixes/test.sh25
-rw-r--r--test/interactive-helper/rpmver.cc118
-rw-r--r--test/interactive-helper/teestream.h62
-rw-r--r--test/interactive-helper/test_fileutl.cc44
-rw-r--r--test/interactive-helper/test_udevcdrom.cc23
-rw-r--r--test/interactive-helper/testdeb.cc51
-rw-r--r--test/libapt/CMakeLists.txt44
-rw-r--r--test/libapt/acqprogress_test.cc178
-rwxr-xr-xtest/libapt/apt-proxy-script9
-rw-r--r--test/libapt/authconf_test.cc223
-rw-r--r--test/libapt/cachefilter_test.cc119
-rw-r--r--test/libapt/cdrom_test.cc114
-rw-r--r--test/libapt/cdromfindpackages_test.cc125
-rw-r--r--test/libapt/commandline_test.cc238
-rw-r--r--test/libapt/compareversion_test.cc182
-rw-r--r--test/libapt/configuration_test.cc234
-rw-r--r--test/libapt/extracttar_test.cc41
-rw-r--r--test/libapt/file-helpers.cc83
-rw-r--r--test/libapt/file-helpers.h29
-rw-r--r--test/libapt/fileutl_test.cc408
-rw-r--r--test/libapt/getarchitectures_test.cc77
-rw-r--r--test/libapt/getlanguages_test.cc241
-rw-r--r--test/libapt/getlistoffilesindir_test.cc114
-rw-r--r--test/libapt/globalerror_test.cc169
-rw-r--r--test/libapt/gtest_runner.cc26
-rw-r--r--test/libapt/hashsums_test.cc392
-rw-r--r--test/libapt/indexcopytosourcelist_test.cc109
-rw-r--r--test/libapt/install_progress_test.cc25
-rw-r--r--test/libapt/openmaybeclearsignedfile_test.cc485
-rw-r--r--test/libapt/parsedepends_test.cc280
-rw-r--r--test/libapt/priority_test.cc16
-rw-r--r--test/libapt/sourcelist_test.cc39
-rw-r--r--test/libapt/srvrecs_test.cc85
-rw-r--r--test/libapt/stringview_test.cc90
-rw-r--r--test/libapt/strutil_test.cc363
-rw-r--r--test/libapt/tagfile_test.cc287
-rw-r--r--test/libapt/tagsection_test.cc270
-rw-r--r--test/libapt/teestream_test.cc39
-rw-r--r--test/libapt/uri_test.cc216
-rwxr-xr-xtest/thread-safety/not-thread-safe.sh7
-rw-r--r--test/thread-safety/thread-check-internal.txt3
-rw-r--r--test/thread-safety/thread-check.txt91
-rw-r--r--triehash/.travis.yml14
-rw-r--r--triehash/LICENSE.md17
-rw-r--r--triehash/README.md58
-rw-r--r--triehash/tests/framework.sh84
-rwxr-xr-xtriehash/tests/run-tests.sh22
-rwxr-xr-xtriehash/tests/test-basic245
-rwxr-xr-xtriehash/tests/test-case-insensitive109
-rwxr-xr-xtriehash/tests/test-enum-include-name-flags129
-rwxr-xr-xtriehash/tests/test-multi-byte-level427
-rwxr-xr-xtriehash/triehash.pl691
-rw-r--r--vendor/CMakeLists.txt37
-rw-r--r--vendor/README72
-rw-r--r--vendor/blankon/apt-vendor.ent17
-rw-r--r--vendor/blankon/sources.list.in10
-rw-r--r--vendor/debian/apt-vendor.ent19
-rw-r--r--vendor/debian/sources.list.in7
-rwxr-xr-xvendor/getinfo83
-rw-r--r--vendor/raspbian/apt-vendor.ent13
-rw-r--r--vendor/raspbian/sources.list.in6
-rw-r--r--vendor/steamos/apt-vendor.ent15
-rw-r--r--vendor/steamos/sources.list.in3
-rw-r--r--vendor/ubuntu/apt-vendor.ent20
-rw-r--r--vendor/ubuntu/apt.conf-01-vendor-ubuntu1
-rw-r--r--vendor/ubuntu/sources.list.in10
782 files changed, 470759 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..c2989f8
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,25 @@
+Language: Cpp
+# BasedOnStyle
+
+TabWidth: 8
+UseTab: Always
+IndentWidth: 3
+ContinuationIndentWidth: 3
+ColumnLimit: 0
+BreakBeforeBraces: Allman
+AccessModifierOffset: 0
+IncludeCategories:
+ - Regex: 'apti18n.h'
+ Priority: 9999
+ - Regex: 'apt-[^/]*/'
+ Priority: 20
+ - Regex: '^"'
+ Priority: 10
+ - Regex: 'config.h'
+ Priority: 0
+ - Regex: '(zlib|bzlib|lzma|lz4frame|gtest/gtest|db|gnutls/.*)\.h'
+ Priority: 30
+ - Regex: '\.h'
+ Priority: 100
+ - Regex: '.*'
+ Priority: 99
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..2126c3d
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,36 @@
+image: debian:buster
+variables:
+ DEBIAN_FRONTEND: noninteractive
+
+test as root:
+ stage: test
+ script:
+ - adduser --home /home/travis travis --quiet --disabled-login --gecos "" --uid 1000
+ - rm -f /etc/dpkg/dpkg.cfg.d/excludes
+ - apt-get update
+ - apt-get install -qq build-essential expect gcovr sudo
+ - chmod -R o+rwX $PWD
+ - ./prepare-release travis-ci
+ - sudo -u travis mkdir build
+ - sudo -u travis env -C build cmake -DCMAKE_BUILD_TYPE=Coverage -G Ninja ..
+ - sudo -u travis ninja -C build
+ - CTEST_OUTPUT_ON_FAILURE=1 ninja -C build test
+ - unbuffer ./test/integration/run-tests -q -j 4
+ - gcovr
+
+test as user:
+ stage: test
+ script:
+ - adduser --home /home/travis travis --quiet --disabled-login --gecos "" --uid 1000
+ - rm -f /etc/dpkg/dpkg.cfg.d/excludes
+ - apt-get update
+ - apt-get install -qq build-essential expect gcovr sudo
+ - chmod 755 /root
+ - chmod -R o+rwX $PWD
+ - ./prepare-release travis-ci
+ - sudo -u travis mkdir build
+ - sudo -u travis env -C build cmake -DCMAKE_BUILD_TYPE=Coverage -G Ninja ..
+ - sudo -u travis ninja -C build
+ - sudo -u travis CTEST_OUTPUT_ON_FAILURE=1 ninja -C build test
+ - sudo -u travis unbuffer ./test/integration/run-tests -q -j 4
+ - sudo -u travis gcovr
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c010e13
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,23 @@
+language: cpp
+cache: ccache
+sudo: required
+services:
+ - docker
+env:
+ global:
+ - DEBIAN_FRONTEND=noninteractive
+ matrix:
+ - USER=travis CMAKE_FLAGS=
+ - USER=root CMAKE_FLAGS=-DWITH_DOC=OFF
+install:
+ - sed -i -e "s#1000#$(id -u)#g" Dockerfile
+ - docker build --tag=apt-ci .
+before_script:
+ - docker run --rm -w $PWD -v $HOME/.ccache:$HOME/.ccache -v $PWD:$PWD --user=travis apt-ci sh -e -c "mkdir build && cd build && env PATH=/usr/lib/ccache:\$PATH cmake -DCMAKE_BUILD_TYPE=Coverage -G Ninja $CMAKE_FLAGS .."
+ - docker run --rm -w $PWD -v $HOME/.ccache:$HOME/.ccache -v $PWD:$PWD --user=travis apt-ci ninja -C build
+script:
+ - docker run --rm -w $PWD -v $PWD:$PWD --user=travis apt-ci env CTEST_OUTPUT_ON_FAILURE=1 ninja -C build test
+ - docker run --rm -w $PWD -v $PWD:$PWD --user=travis apt-ci env DESTDIR=$PWD/rootdir chronic ninja -C build install
+ - docker run --rm -w $PWD -v $PWD:$PWD --user=$USER --tmpfs /tmp:suid,exec apt-ci unbuffer ./test/integration/run-tests -qq -j 4
+after_script:
+ - docker run --rm -w $PWD/build -v $PWD:$PWD --user=$USER `bash <(curl -s https://codecov.io/env)` apt-ci bash -c 'bash <(curl -s https://codecov.io/bash)'
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..cce31e6
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,63 @@
+The project contributors:
+
+Michael Vogt <mvo@debian.org>
+- Development, bug fixes
+
+David Kalnischkies <kalnischkies+debian@gmail.com>
+- Development, bug fixes
+
+Julian Andres Klode
+- Development, bug fixes
+
+Past Contributors:
+
+Robert Collins <robert.collins@canonical.com>
+- Change the package index Info methods to allow apt-cache policy to be useful
+ when using several different archives on the same host.
+
+Christian Perrier <bubulle@debian.org>
+- Translations hero/coordinator
+
+Eugene V. Lyubimkin
+- Development, bug fixes
+
+Otavio Salvador
+- Development, bug fixes
+
+Luca Bruno
+- Development, bug fixes
+
+CVS:jgg Jason Gunthorpe <jgg@debian.org>
+- The Mad Cow incarnate
+
+CVS:mdz Matt Zimmerman <mdz@debian.org>
+- Ongoing maintenance and coordination of development
+
+CVS:piefel Michael Piefel <piefel@debian.org>
+- i18n and l10n
+
+CVS:che Ben Gertzfield <che@debian.org>
+- Packaging and Releases
+
+CVS:bod Brendan O'Dea <bod@debian.org>
+- Perl Bindings
+
+CVS:tausq Randolph Chung <tausq@debian.org>
+- Patches, Fixes, Debugging, GUIs and Releases
+
+Isaac Jones <ijones@syntaxpolice.org> and Colin Walters <walters@debian.org>
+ Initial implementation of authentication support (Release.gpg)
+
+Brian White <bcwhite@verisim.com> - Project originator
+Tom Lees <tom@lpsg.demon.co.uk> - DPKG documentation and ideas
+Behan Webster <behanw@verisim.com> - Original GUI design
+Scott Ellis <storm@gate.net> - Original packaging and beta releases
+Branden Robinson <branden@purdue.edu> - Man Page Documentation
+Manoj Srivastava <srivasta@datasync.com> - 1st Generation FTP method and
+ dselect setup script
+Adam Heath <doogie@debian.org> - 2nd Generation FTP method author
+Ben Collins <bcollins@debian.org> - Initial RSH method
+Many other bug reports through the Debian Bug system
+
+NOTE: The ChangeLog generator will parse for names and email addresses. The
+'CVS:<name>' tag should indicate who this pair refers to.
diff --git a/CMake/CheckCxxTarget.cmake b/CMake/CheckCxxTarget.cmake
new file mode 100644
index 0000000..373c0be
--- /dev/null
+++ b/CMake/CheckCxxTarget.cmake
@@ -0,0 +1,35 @@
+# CMake support for target-based function multiversioning
+#
+# Copyright (C) 2019 Canonical Ltd
+#
+# Author: Julian Andres Klode <jak@debian.org>.
+#
+# 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.
+
+
+function(check_cxx_target var target code)
+ check_cxx_source_compiles(
+ "
+ __attribute__((target(\"${target}\"))) static int foo() { ${code} return 1; }
+ __attribute__((target(\"default\"))) static int foo() { ${code} return 0; }
+ int main() { return foo(); }
+ " ${var})
+endfunction()
diff --git a/CMake/Documentation.cmake b/CMake/Documentation.cmake
new file mode 100644
index 0000000..d8a2d2c
--- /dev/null
+++ b/CMake/Documentation.cmake
@@ -0,0 +1,328 @@
+# po4a/docbook documentation support for CMake
+# - see documentation of add_docbook()
+#
+# Copyright (C) 2016 Julian Andres Klode <jak@debian.org>.
+#
+# 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.
+
+
+find_path(DOCBOOK_XSL manpages/docbook.xsl
+ # Debian
+ /usr/share/xml/docbook/stylesheet/docbook-xsl
+ /usr/share/xml/docbook/stylesheet/nwalsh
+ # OpenSUSE
+ /usr/share/xml/docbook/stylesheet/nwalsh/current
+ # Arch
+ /usr/share/xml/docbook/xsl-stylesheets
+ # Fedora
+ /usr/share/sgml/docbook/xsl-stylesheets
+ # Fink
+ ${CMAKE_INSTALL_PREFIX}/share/xml/xsl/docbook-xsl
+ # FreeBSD
+ ${CMAKE_INSTALL_PREFIX}/share/xsl/docbook/
+ NO_DEFAULT_PATH)
+
+if(NOT DOCBOOK_XSL)
+ message(FATAL_ERROR "Could not find docbook xsl")
+endif()
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docbook-text-style.xsl.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/docbook-text-style.xsl)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docbook-html-style.xsl.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/docbook-html-style.xsl)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/manpage-style.xsl.cmake.in
+ ${CMAKE_CURRENT_BINARY_DIR}/manpage-style.xsl)
+
+
+# Split up a string of the form DOCUMENT[.DOCUMENT][.LANGUAGE][.SECTION].EXTENSION
+#
+# There might be up to two parts in the document name. The language must be
+# a two char language code like de, or a 5 char code of the form de_DE.
+function(po4a_components doc lang sec ext translated_full_document)
+ get_filename_component(name ${translated_full_document} NAME)
+ string(REPLACE "." ";" name "${name}") # Make it a list
+
+ list(GET name 0 _doc) # First element is always the document
+ list(GET name 1 _lang) # Second *might* be a language
+ list(GET name -2 _sec) # Second-last *might* be a section
+ list(GET name -1 _ext) # Last element is always the file type
+
+ # If the language code is neither a file type, nor a section, nor a language
+ # assume it is part of the file name and use the next component as the lang.
+ if(_lang AND NOT _lang MATCHES "^(xml|dbk|[0-9]|[a-z][a-z]|[a-z][a-z]_[A-Z][A-Z])$")
+ set(_doc "${_doc}.${_lang}")
+ list(GET name 2 _lang)
+ endif()
+ # If no language is present, we get a section; both not present => type
+ if(_lang MATCHES "xml|dbk|[0-9]")
+ set(_lang "")
+ endif()
+ if(NOT _sec MATCHES "^[0-9]$") # A (manpage) section must be a number
+ set(_sec "")
+ endif()
+
+ set(${doc} ${_doc} PARENT_SCOPE)
+ set(${lang} ${_lang} PARENT_SCOPE)
+ set(${sec} ${_sec} PARENT_SCOPE)
+ set(${ext} ${_ext} PARENT_SCOPE)
+endfunction()
+
+
+# Process one document
+function(po4a_one stamp_out out full_document language deps)
+ path_join(full_path "${CMAKE_CURRENT_SOURCE_DIR}" "${full_document}")
+ if (full_document MATCHES "\.ent$")
+ set(dest "${language}/${full_document}")
+ set(full_dest "${dest}")
+ else()
+ po4a_components(document _ section ext "${full_document}")
+
+ # Calculate target file name
+ set(dest "${language}/${document}.${language}")
+ if(section)
+ set(dest "${dest}.${section}")
+ endif()
+ set(full_dest "${dest}.${ext}")
+ endif()
+
+ # po4a might drop files not translated enough, so build a stamp file
+ set(stamp ${CMAKE_CURRENT_BINARY_DIR}/${dest}.po4a-stamp)
+ add_custom_command(
+ OUTPUT ${stamp}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${language}
+ COMMAND po4a --previous --no-backups
+ --package-name='${PROJECT_NAME}-doc'
+ --package-version='${PACKAGE_VERSION}'
+ --msgid-bugs-address='${PACKAGE_MAIL}'
+ --translate-only ${full_dest}
+ --srcdir ${CMAKE_CURRENT_SOURCE_DIR}
+ --destdir ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/po4a.conf
+ COMMAND ${CMAKE_COMMAND} -E touch ${stamp}
+ COMMENT "Generating ${full_dest} (or dropping it)"
+ DEPENDS ${full_document} ${deps} po/${language}.po
+ )
+ # Return result
+ set(${stamp_out} ${stamp} PARENT_SCOPE)
+ set(${out} ${CMAKE_CURRENT_BINARY_DIR}/${full_dest} PARENT_SCOPE)
+endfunction()
+
+function(xsltproc_one)
+ set(generated "")
+ set(options HTML TEXT MANPAGE)
+ set(oneValueArgs STAMP STAMP_OUT FULL_DOCUMENT)
+ set(multiValueArgs INSTALL DEPENDS)
+ cmake_parse_arguments(DOC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ po4a_components(document language section ext "${DOC_FULL_DOCUMENT}")
+
+ # Default parameters
+ set(params
+ --nonet
+ --xinclude
+ --stringparam chunk.quietly yes
+ --stringparam man.output.quietly yes
+ --path ${PROJECT_SOURCE_DIR}/vendor/${CURRENT_VENDOR}/
+ --path ${CMAKE_CURRENT_SOURCE_DIR}/
+ )
+
+ # Parameters if localized
+ if(language)
+ list(APPEND params -stringparam l10n.gentext.default.language ${language})
+ endif()
+
+ path_join(full_input_path ${CMAKE_CURRENT_SOURCE_DIR} ${DOC_FULL_DOCUMENT})
+
+ if (DOC_MANPAGE)
+ if (language)
+ set(manpage_output "${CMAKE_CURRENT_BINARY_DIR}/${language}/${document}.${section}")
+ else()
+ set(manpage_output "${CMAKE_CURRENT_BINARY_DIR}/${document}.${section}")
+ endif()
+ set(manpage_stylesheet "${CMAKE_CURRENT_BINARY_DIR}/manpage-style.xsl")
+ set(manpage_params)
+
+ install(FILES ${manpage_output}
+ DESTINATION ${CMAKE_INSTALL_MANDIR}/${language}/man${section}
+ OPTIONAL)
+ endif()
+ if (DOC_HTML)
+ if (language)
+ set(html_output "${CMAKE_CURRENT_BINARY_DIR}/${language}/${document}.${language}.html")
+ else()
+ set(html_output "${CMAKE_CURRENT_BINARY_DIR}/${document}.html")
+ endif()
+ set(html_params --stringparam base.dir ${html_output})
+ set(html_stylesheet "${CMAKE_CURRENT_BINARY_DIR}/docbook-html-style.xsl")
+ install(DIRECTORY ${html_output}
+ DESTINATION ${DOC_INSTALL}
+ OPTIONAL)
+
+ endif()
+ if (DOC_TEXT)
+ if (language)
+ set(text_output "${CMAKE_CURRENT_BINARY_DIR}/${language}/${document}.${language}.text")
+ else()
+ set(text_output "${CMAKE_CURRENT_BINARY_DIR}/${document}.text")
+ endif()
+ set(text_params --stringparam base.dir ${text_output})
+ set(text_stylesheet "${CMAKE_CURRENT_BINARY_DIR}/docbook-text-style.xsl")
+
+ file(RELATIVE_PATH text_output_relative ${CMAKE_CURRENT_BINARY_DIR} ${text_output})
+
+ add_custom_command(OUTPUT ${text_output}.w3m-stamp
+ COMMAND ${PROJECT_SOURCE_DIR}/CMake/run_if_exists.sh
+ --stdout ${text_output}
+ ${text_output}.html
+ env LC_ALL=C.UTF-8 w3m -cols 78 -dump
+ -o display_charset=UTF-8
+ -no-graph -T text/html ${text_output}.html
+ COMMAND ${CMAKE_COMMAND} -E touch ${text_output}.w3m-stamp
+ COMMENT "Generating ${text_output_relative} (if not dropped by po4a)"
+ DEPENDS "${text_output}.html.xsltproc-stamp"
+ )
+ list(APPEND generated ${text_output}.w3m-stamp)
+
+ install(FILES ${text_output}
+ DESTINATION ${DOC_INSTALL}
+ OPTIONAL)
+ set(text_output "${text_output}.html")
+ endif()
+
+ foreach(type in manpage html text)
+ if (NOT ${type}_output)
+ continue()
+ endif()
+
+ set(output ${${type}_output})
+ set(stylesheet ${${type}_stylesheet})
+ set(type_params ${${type}_params})
+ file(RELATIVE_PATH output_relative ${CMAKE_CURRENT_BINARY_DIR} ${output})
+
+ add_custom_command(OUTPUT ${output}.xsltproc-stamp
+ COMMAND ${PROJECT_SOURCE_DIR}/CMake/run_if_exists.sh
+ ${full_input_path}
+ xsltproc ${params} ${type_params} -o ${output}
+ ${stylesheet}
+ ${full_input_path}
+ COMMAND ${CMAKE_COMMAND} -E touch ${output}.xsltproc-stamp
+ COMMENT "Generating ${output_relative} (if not dropped by po4a)"
+ DEPENDS ${DOC_STAMP} ${DOC_DEPENDS})
+
+ list(APPEND generated ${output}.xsltproc-stamp)
+ endforeach()
+
+ set(${DOC_STAMP_OUT} ${generated} PARENT_SCOPE)
+endfunction()
+
+
+# add_docbook(Name [ALL] [HTML] [TEXT] [MANPAGE]
+# [INSTALL install dir]
+# [DEPENDS depend ...]
+# [DOCUMENTS documents ...]
+# [LINGUAS lingua ...])
+#
+# Generate a target called name with all the documents being converted to
+# the chosen output formats and translated to the chosen languages using po4a.
+#
+# For the translation support, the po4a.conf must be written so that
+# translations for a document guide.xml are written to LANG/guide.LANG.xml,
+# and for a manual page man.5.xml to a file called LANG/man.LANG.5.xml.
+#
+# The guide and manual page names may also contain a second component separated
+# by a dot, it must however not be a valid language code.
+#
+# Note that po4a might chose not to generate a translated manual page for a
+# given language if the translation rate is not high enough. We deal with this
+# by creating stamp files.
+function(add_docbook target)
+ set(generated "")
+ set(options HTML TEXT MANPAGE ALL)
+ set(oneValueArgs)
+ set(multiValueArgs INSTALL DOCUMENTS LINGUAS TRANSLATED_ENTITIES DEPENDS)
+ cmake_parse_arguments(DOC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if (DOC_HTML)
+ list(APPEND formats HTML)
+ endif()
+ if (DOC_TEXT)
+ list(APPEND formats TEXT)
+ endif()
+ if (DOC_MANPAGE)
+ list(APPEND formats MANPAGE)
+ endif()
+
+ foreach(document ${DOC_TRANSLATED_ENTITIES})
+ foreach(lang ${DOC_LINGUAS})
+ po4a_one(po4a_stamp po4a_out ${document} "${lang}" "")
+ list(APPEND DOC_DEPENDS ${po4a_stamp})
+ endforeach()
+ endforeach()
+
+ foreach(document ${DOC_DOCUMENTS})
+ foreach(lang ${DOC_LINGUAS})
+ po4a_one(po4a_stamp po4a_out ${document} "${lang}" "${DOC_DEPENDS}")
+ xsltproc_one(STAMP_OUT xslt_stamp
+ STAMP ${po4a_stamp}
+ FULL_DOCUMENT ${po4a_out}
+ INSTALL ${DOC_INSTALL}
+ ${formats})
+
+ list(APPEND stamps ${xslt_stamp})
+ endforeach()
+ xsltproc_one(STAMP_OUT xslt_stamp
+ STAMP ${document}
+ FULL_DOCUMENT ${document}
+ INSTALL ${DOC_INSTALL}
+ ${formats})
+
+ list(APPEND stamps ${xslt_stamp})
+ endforeach()
+
+ if (DOC_ALL)
+ add_custom_target(${target} ALL DEPENDS ${stamps})
+ else()
+ add_custom_target(${target} DEPENDS ${stamps})
+ endif()
+endfunction()
+
+# Add an update-po4a target
+function(add_update_po4a target pot header)
+ set(WRITE_HEADER "")
+
+ if (header)
+ set(WRITE_HEADER
+ COMMAND sed -n "/^\#$/,$p" ${pot} > ${pot}.headerfree
+ COMMAND cat ${header} ${pot}.headerfree > ${pot}
+ COMMAND rm ${pot}.headerfree
+ )
+ endif()
+ add_custom_target(${target}
+ COMMAND po4a --previous --no-backups --force --no-translations
+ --msgmerge-opt --add-location=file
+ --porefs noline,wrap
+ --package-name=${PROJECT_NAME}-doc --package-version=${PACKAGE_VERSION}
+ --msgid-bugs-address=${PACKAGE_MAIL} po4a.conf
+ ${WRITE_HEADER}
+ VERBATIM
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+endfunction()
diff --git a/CMake/FindBerkeleyDB.cmake b/CMake/FindBerkeleyDB.cmake
new file mode 100644
index 0000000..34bc3b0
--- /dev/null
+++ b/CMake/FindBerkeleyDB.cmake
@@ -0,0 +1,59 @@
+# - Try to find Berkeley DB
+# Once done this will define
+#
+# BERKELEY_DB_FOUND - system has Berkeley DB
+# BERKELEY_DB_INCLUDE_DIRS - the Berkeley DB include directory
+# BERKELEY_DB_LIBRARIES - Link these to use Berkeley DB
+# BERKELEY_DB_DEFINITIONS - Compiler switches required for using Berkeley DB
+
+# Copyright (c) 2006, Alexander Dymo, <adymo@kdevelop.org>
+# Copyright (c) 2016, Julian Andres Klode <jak@debian.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+# We need NO_DEFAULT_PATH here, otherwise CMake helpfully picks up the wrong
+# db.h on BSD systems instead of the Berkeley DB one.
+find_path(BERKELEY_DB_INCLUDE_DIRS db.h
+ ${CMAKE_INSTALL_FULL_INCLUDEDIR}/db5
+ /usr/local/include/db5
+ /usr/include/db5
+
+ ${CMAKE_INSTALL_FULL_INCLUDEDIR}/db4
+ /usr/local/include/db4
+ /usr/include/db4
+
+ ${CMAKE_INSTALL_FULL_INCLUDEDIR}
+ /usr/local/include
+ /usr/include
+
+ NO_DEFAULT_PATH
+)
+
+find_library(BERKELEY_DB_LIBRARIES NAMES db db-5)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Berkeley "Could not find Berkeley DB >= 4.1" BERKELEY_DB_INCLUDE_DIRS BERKELEY_DB_LIBRARIES)
+# show the BERKELEY_DB_INCLUDE_DIRS and BERKELEY_DB_LIBRARIES variables only in the advanced view
+mark_as_advanced(BERKELEY_DB_INCLUDE_DIRS BERKELEY_DB_LIBRARIES)
diff --git a/CMake/FindIconv.cmake b/CMake/FindIconv.cmake
new file mode 100644
index 0000000..67046d9
--- /dev/null
+++ b/CMake/FindIconv.cmake
@@ -0,0 +1,20 @@
+find_path(ICONV_INCLUDE_DIR NAMES iconv.h)
+
+find_library(ICONV_LIBRARY NAMES iconv)
+if (ICONV_LIBRARY)
+ set(ICONV_SYMBOL_FOUND "${ICONV_LIBRARY}")
+else()
+ check_function_exists(iconv_open ICONV_SYMBOL_FOUND)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Iconv DEFAULT_MESSAGE ICONV_INCLUDE_DIR ICONV_SYMBOL_FOUND)
+
+if(ICONV_LIBRARY)
+ set(ICONV_LIBRARIES "${ICONV_LIBRARY}")
+else()
+ set(ICONV_LIBRARIES)
+endif()
+set(ICONV_INCLUDE_DIRS "${ICONV_INCLUDE_DIR}")
+
+mark_as_advanced(ICONV_LIBRARY ICONV_INCLUDE_DIR)
diff --git a/CMake/FindLFS.cmake b/CMake/FindLFS.cmake
new file mode 100644
index 0000000..e1fcf96
--- /dev/null
+++ b/CMake/FindLFS.cmake
@@ -0,0 +1,148 @@
+# CMake support for large files
+#
+# Copyright (C) 2016 Julian Andres Klode <jak@debian.org>.
+#
+# 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.
+#
+# This defines the following variables
+#
+# LFS_DEFINITIONS - List of definitions to pass to add_definitions()
+# LFS_COMPILE_OPTIONS - List of definitions to pass to add_compile_options()
+# LFS_LIBRARIES - List of libraries and linker flags
+# LFS_FOUND - If there is Large files support
+#
+
+include(CheckCXXSourceCompiles)
+include(FindPackageHandleStandardArgs)
+
+# Test program to check for LFS. Requires that off_t has at least 8 byte large
+set(_lfs_test_source
+ "
+ #include <sys/types.h>
+ typedef char my_static_assert[sizeof(off_t) >= 8 ? 1 : -1];
+ int main(void) { return 0; }
+ "
+)
+
+# Check if the given options are needed
+#
+# This appends to the variables _lfs_cppflags, _lfs_cflags, and _lfs_ldflags,
+# it also sets LFS_FOUND to 1 if it works.
+function(_lfs_check_compiler_option var options definitions libraries)
+ set(CMAKE_REQUIRED_QUIET 1)
+ set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} ${options})
+ set(CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} ${definitions})
+ set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_DEFINITIONS} ${libraries})
+
+ message(STATUS "Looking for LFS support using ${options} ${definitions} ${libraries}")
+ check_cxx_source_compiles("${_lfs_test_source}" ${var})
+
+ if(${var})
+ message(STATUS "Looking for LFS support using ${options} ${definitions} ${libraries} - found")
+ set(_lfs_cppflags ${_lfs_cppflags} ${definitions} PARENT_SCOPE)
+ set(_lfs_cflags ${_lfs_cflags} ${options} PARENT_SCOPE)
+ set(_lfs_ldflags ${_lfs_ldflags} ${libraries} PARENT_SCOPE)
+ set(LFS_FOUND TRUE PARENT_SCOPE)
+ else()
+ message(STATUS "Looking for LFS support using ${options} ${definitions} ${libraries} - not found")
+ endif()
+endfunction()
+
+# Check for the availability of LFS.
+# The cases handled are:
+#
+# * Native LFS
+# * Output of getconf LFS_CFLAGS; getconf LFS_LIBS; getconf LFS_LDFLAGS
+# * Preprocessor flag -D_FILE_OFFSET_BITS=64
+# * Preprocessor flag -D_LARGE_FILES
+#
+function(_lfs_check)
+ set(_lfs_cflags)
+ set(_lfs_cppflags)
+ set(_lfs_ldflags)
+ set(_lfs_libs)
+ set(CMAKE_REQUIRED_QUIET 1)
+ message(STATUS "Looking for native LFS support")
+ check_cxx_source_compiles("${_lfs_test_source}" lfs_native)
+ if (lfs_native)
+ message(STATUS "Looking for native LFS support - found")
+ set(LFS_FOUND TRUE)
+ else()
+ message(STATUS "Looking for native LFS support - not found")
+ endif()
+
+ if (NOT LFS_FOUND)
+ # Check using getconf. If getconf fails, don't worry, the check in
+ # _lfs_check_compiler_option will fail as well.
+ execute_process(COMMAND getconf LFS_CFLAGS
+ OUTPUT_VARIABLE _lfs_cflags_raw
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+ execute_process(COMMAND getconf LFS_LIBS
+ OUTPUT_VARIABLE _lfs_libs_tmp
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+ execute_process(COMMAND getconf LFS_LDFLAGS
+ OUTPUT_VARIABLE _lfs_ldflags_tmp
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+
+ separate_arguments(_lfs_cflags_raw)
+ separate_arguments(_lfs_ldflags_tmp)
+ separate_arguments(_lfs_libs_tmp)
+
+ # Move -D flags to the place they are supposed to be
+ foreach(flag ${_lfs_cflags_raw})
+ if (flag MATCHES "-D.*")
+ list(APPEND _lfs_cppflags_tmp ${flag})
+ else()
+ list(APPEND _lfs_cflags_tmp ${flag})
+ endif()
+ endforeach()
+
+ # Check if the flags we received (if any) produce working LFS support
+ _lfs_check_compiler_option(lfs_getconf_works
+ "${_lfs_cflags_tmp}"
+ "${_lfs_cppflags_tmp}"
+ "${_lfs_libs_tmp};${_lfs_ldflags_tmp}")
+ endif()
+
+ if(NOT LFS_FOUND) # IRIX stuff
+ _lfs_check_compiler_option(lfs_need_n32 "-n32" "" "")
+ endif()
+ if(NOT LFS_FOUND) # Linux and friends
+ _lfs_check_compiler_option(lfs_need_file_offset_bits "" "-D_FILE_OFFSET_BITS=64" "")
+ endif()
+ if(NOT LFS_FOUND) # AIX
+ _lfs_check_compiler_option(lfs_need_large_files "" "-D_LARGE_FILES=1" "")
+ endif()
+
+ set(LFS_DEFINITIONS ${_lfs_cppflags} CACHE STRING "Extra definitions for large file support")
+ set(LFS_COMPILE_OPTIONS ${_lfs_cflags} CACHE STRING "Extra definitions for large file support")
+ set(LFS_LIBRARIES ${_lfs_libs} ${_lfs_ldflags} CACHE STRING "Extra definitions for large file support")
+ set(LFS_FOUND ${LFS_FOUND} CACHE INTERNAL "Found LFS")
+endfunction()
+
+if (NOT LFS_FOUND)
+ _lfs_check()
+endif()
+
+find_package_handle_standard_args(LFS "Could not find LFS. Set LFS_DEFINITIONS, LFS_COMPILE_OPTIONS, LFS_LIBRARIES." LFS_FOUND)
diff --git a/CMake/FindLZ4.cmake b/CMake/FindLZ4.cmake
new file mode 100644
index 0000000..597f520
--- /dev/null
+++ b/CMake/FindLZ4.cmake
@@ -0,0 +1,25 @@
+# - Try to find LZ4
+# Once done, this will define
+#
+# LZ4_FOUND - system has LZ4
+# LZ4_INCLUDE_DIRS - the LZ4 include directories
+# LZ4_LIBRARIES - the LZ4 library
+find_package(PkgConfig)
+
+pkg_check_modules(LZ4_PKGCONF liblz4)
+
+find_path(LZ4_INCLUDE_DIRS
+ NAMES lz4frame.h
+ PATHS ${LZ4_PKGCONF_INCLUDE_DIRS}
+)
+
+
+find_library(LZ4_LIBRARIES
+ NAMES lz4
+ PATHS ${LZ4_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LZ4 DEFAULT_MSG LZ4_INCLUDE_DIRS LZ4_LIBRARIES)
+
+mark_as_advanced(LZ4_INCLUDE_DIRS LZ4_LIBRARIES)
diff --git a/CMake/FindLZMA.cmake b/CMake/FindLZMA.cmake
new file mode 100644
index 0000000..6abc4fa
--- /dev/null
+++ b/CMake/FindLZMA.cmake
@@ -0,0 +1,25 @@
+# - Try to find LZMA
+# Once done, this will define
+#
+# LZMA_FOUND - system has LZMA
+# LZMA_INCLUDE_DIRS - the LZMA include directories
+# LZMA_LIBRARIES - the LZMA library
+find_package(PkgConfig)
+
+pkg_check_modules(LZMA_PKGCONF liblzma)
+
+find_path(LZMA_INCLUDE_DIRS
+ NAMES lzma.h
+ PATHS ${LZMA_PKGCONF_INCLUDE_DIRS}
+)
+
+
+find_library(LZMA_LIBRARIES
+ NAMES lzma
+ PATHS ${LZMA_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LZMA DEFAULT_MSG LZMA_INCLUDE_DIRS LZMA_LIBRARIES)
+
+mark_as_advanced(LZMA_INCLUDE_DIRS LZMA_LIBRARIES)
diff --git a/CMake/FindSeccomp.cmake b/CMake/FindSeccomp.cmake
new file mode 100644
index 0000000..5cfd13a
--- /dev/null
+++ b/CMake/FindSeccomp.cmake
@@ -0,0 +1,25 @@
+# - Try to find SECCOMP
+# Once done, this will define
+#
+# SECCOMP_FOUND - system has SECCOMP
+# SECCOMP_INCLUDE_DIRS - the SECCOMP include directories
+# SECCOMP_LIBRARIES - the SECCOMP library
+find_package(PkgConfig)
+
+pkg_check_modules(SECCOMP_PKGCONF libseccomp)
+
+find_path(SECCOMP_INCLUDE_DIRS
+ NAMES seccomp.h
+ PATHS ${SECCOMP_PKGCONF_INCLUDE_DIRS}
+)
+
+
+find_library(SECCOMP_LIBRARIES
+ NAMES seccomp
+ PATHS ${SECCOMP_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(SECCOMP DEFAULT_MSG SECCOMP_INCLUDE_DIRS SECCOMP_LIBRARIES)
+
+mark_as_advanced(SECCOMP_INCLUDE_DIRS SECCOMP_LIBRARIES)
diff --git a/CMake/FindSystemd.cmake b/CMake/FindSystemd.cmake
new file mode 100644
index 0000000..1c7a7de
--- /dev/null
+++ b/CMake/FindSystemd.cmake
@@ -0,0 +1,24 @@
+# - Try to find SYSTEMD
+# Once done, this will define
+#
+# SYSTEMD_FOUND - system has SYSTEMD
+# SYSTEMD_INCLUDE_DIRS - the SYSTEMD include directories
+# SYSTEMD_LIBRARIES - the SYSTEMD library
+find_package(PkgConfig)
+
+pkg_check_modules(SYSTEMD_PKGCONF libsystemd)
+
+find_path(SYSTEMD_INCLUDE_DIRS
+ NAMES systemd/sd-bus.h
+ PATHS ${SYSTEMD_PKGCONF_INCLUDE_DIRS}
+)
+
+find_library(SYSTEMD_LIBRARIES
+ NAMES systemd
+ PATHS ${SYSTEMD_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Systemd DEFAULT_MSG SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
+
+mark_as_advanced(SYSTEMD_INCLUDE_DIRS SYSTEMD_LIBRARIES)
diff --git a/CMake/FindUdev.cmake b/CMake/FindUdev.cmake
new file mode 100644
index 0000000..e416c43
--- /dev/null
+++ b/CMake/FindUdev.cmake
@@ -0,0 +1,25 @@
+# - Try to find UDEV
+# Once done, this will define
+#
+# UDEV_FOUND - system has UDEV
+# UDEV_INCLUDE_DIRS - the UDEV include directories
+# UDEV_LIBRARIES - the UDEV library
+find_package(PkgConfig)
+
+pkg_check_modules(UDEV_PKGCONF libudev)
+
+find_path(UDEV_INCLUDE_DIRS
+ NAMES libudev.h
+ PATHS ${UDEV_PKGCONF_INCLUDE_DIRS}
+)
+
+
+find_library(UDEV_LIBRARIES
+ NAMES udev
+ PATHS ${UDEV_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Udev DEFAULT_MSG UDEV_INCLUDE_DIRS UDEV_LIBRARIES)
+
+mark_as_advanced(UDEV_INCLUDE_DIRS UDEV_LIBRARIES)
diff --git a/CMake/FindZstd.cmake b/CMake/FindZstd.cmake
new file mode 100644
index 0000000..6811804
--- /dev/null
+++ b/CMake/FindZstd.cmake
@@ -0,0 +1,25 @@
+# - Try to find ZSTD
+# Once done, this will define
+#
+# ZSTD_FOUND - system has ZSTD
+# ZSTD_INCLUDE_DIRS - the ZSTD include directories
+# ZSTD_LIBRARIES - the ZSTD library
+find_package(PkgConfig)
+
+pkg_check_modules(ZSTD_PKGCONF libzstd)
+
+find_path(ZSTD_INCLUDE_DIRS
+ NAMES zstd.h
+ PATHS ${ZSTD_PKGCONF_INCLUDE_DIRS}
+)
+
+
+find_library(ZSTD_LIBRARIES
+ NAMES zstd
+ PATHS ${ZSTD_PKGCONF_LIBRARY_DIRS}
+)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(ZSTD DEFAULT_MSG ZSTD_INCLUDE_DIRS ZSTD_LIBRARIES)
+
+mark_as_advanced(ZSTD_INCLUDE_DIRS ZSTD_LIBRARIES)
diff --git a/CMake/Misc.cmake b/CMake/Misc.cmake
new file mode 100644
index 0000000..6ad0b94
--- /dev/null
+++ b/CMake/Misc.cmake
@@ -0,0 +1,101 @@
+include(CheckCXXCompilerFlag)
+
+# Flatten our header structure
+function(flatify target headers)
+ foreach(header ${headers})
+ get_filename_component(tgt ${header} NAME)
+ configure_file(${header} ${target}/${tgt} COPYONLY)
+ endforeach(header ${headers})
+endfunction()
+
+
+function(add_optional_compile_options flags)
+ foreach(flag ${flags})
+ check_cxx_compiler_flag(-${flag} have-compiler-flag:-${flag})
+ if (have-compiler-flag:-${flag})
+ add_compile_options("-${flag}")
+ endif()
+ endforeach()
+endfunction()
+
+# Substitute vendor references in a file
+function(add_vendor_file)
+ set(options)
+ set(oneValueArgs OUTPUT INPUT MODE)
+ set(multiValueArgs VARIABLES)
+ cmake_parse_arguments(AVF "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(in ${CMAKE_CURRENT_SOURCE_DIR}/${AVF_INPUT})
+ set(out ${CMAKE_CURRENT_BINARY_DIR}/${AVF_OUTPUT})
+
+ add_custom_command(
+ OUTPUT ${out}
+ COMMAND ${CMAKE_COMMAND} -DPROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR}
+ "-DVARS=${AVF_VARIABLES}"
+ -DCURRENT_VENDOR=${CURRENT_VENDOR}
+ -DIN=${in}
+ -DOUT=${out}
+ -P ${PROJECT_SOURCE_DIR}/CMake/vendor_substitute.cmake
+ COMMAND chmod ${AVF_MODE} ${out}
+ DEPENDS ${in}
+ ${PROJECT_SOURCE_DIR}/doc/apt-verbatim.ent
+ ${PROJECT_SOURCE_DIR}/vendor/${CURRENT_VENDOR}/apt-vendor.ent
+ ${PROJECT_SOURCE_DIR}/vendor/getinfo
+ ${PROJECT_SOURCE_DIR}/CMake/vendor_substitute.cmake
+ VERBATIM
+ )
+
+ # Would like to use ${AVF_OUTPUT} as target name, but then ninja gets
+ # cycles.
+ add_custom_target(vendor-${AVF_OUTPUT} ALL DEPENDS ${out})
+endfunction()
+
+# Add symbolic links to a file
+function(add_slaves destination master)
+ set(slaves "")
+ foreach(slave ${ARGN})
+ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${slave}
+ COMMAND ${CMAKE_COMMAND} -E create_symlink ${master} ${CMAKE_CURRENT_BINARY_DIR}/${slave})
+ list(APPEND slaves ${CMAKE_CURRENT_BINARY_DIR}/${slave})
+ endforeach()
+
+ STRING(REPLACE "/" "-" master "${master}")
+ add_custom_target(${master}-slaves ALL DEPENDS ${slaves})
+ install(FILES ${slaves} DESTINATION ${destination})
+endfunction()
+
+# Generates a simple version script versioning everything with current SOVERSION
+function(add_version_script target)
+ get_target_property(soversion ${target} SOVERSION)
+ set(script "${CMAKE_CURRENT_BINARY_DIR}/${target}.versionscript")
+ string(REPLACE "-" "" name "${target}_${soversion}")
+ string(TOUPPER "${name}" name)
+ add_custom_command(OUTPUT "${script}"
+ COMMAND echo "${name} {global: *; };" > "${script}"
+ VERBATIM )
+ add_custom_target(${target}-versionscript DEPENDS "${script}")
+ target_link_libraries(${target} PRIVATE -Wl,-version-script="${script}")
+ add_dependencies(${target} ${target}-versionscript)
+endfunction()
+
+function(path_join out path1 path2)
+ string(SUBSTRING ${path2} 0 1 init_char)
+ if ("${init_char}" STREQUAL "/")
+ set(${out} "${path2}" PARENT_SCOPE)
+ else()
+ set(${out} "${path1}/${path2}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+# install_empty_directories(path ...)
+#
+# Creates empty directories in the install destination dir. Paths may be
+# absolute or relative; in the latter case, the value of CMAKE_INSTALL_PREFIX
+# is prepended.
+function(install_empty_directories)
+ foreach(path ${ARGN})
+ path_join(full_path "${CMAKE_INSTALL_PREFIX}" "${path}")
+ INSTALL(CODE "MESSAGE(STATUS \"Creating directory: \$ENV{DESTDIR}${full_path}\")"
+ CODE "FILE(MAKE_DIRECTORY \$ENV{DESTDIR}${full_path})")
+ endforeach()
+endfunction()
diff --git a/CMake/Translations.cmake b/CMake/Translations.cmake
new file mode 100644
index 0000000..54a635a
--- /dev/null
+++ b/CMake/Translations.cmake
@@ -0,0 +1,185 @@
+# translations.cmake - Translations using APT's translation system.
+# Copyright (C) 2009, 2016 Julian Andres Klode <jak@debian.org>
+
+function(apt_add_translation_domain)
+ set(options)
+ set(oneValueArgs DOMAIN)
+ set(multiValueArgs TARGETS SCRIPTS EXCLUDE_LANGUAGES)
+ cmake_parse_arguments(NLS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+ # Build the list of source files of the target
+ set(files "")
+ set(abs_files "")
+ set(scripts "")
+ set(abs_scripts "")
+ set(mofiles)
+ set(targets ${NLS_TARGETS})
+ set(domain ${NLS_DOMAIN})
+ set(xgettext_params
+ --add-comments
+ --foreign
+ --package-name=${PROJECT_NAME}
+ --package-version=${PACKAGE_VERSION}
+ --msgid-bugs-address=${PACKAGE_MAIL}
+ )
+ foreach(source ${NLS_SCRIPTS})
+ path_join(file "${CMAKE_CURRENT_SOURCE_DIR}" "${source}")
+ file(RELATIVE_PATH relfile ${PROJECT_SOURCE_DIR} ${file})
+ list(APPEND scripts ${relfile})
+ list(APPEND abs_scripts ${file})
+ endforeach()
+ foreach(target ${targets})
+ get_target_property(source_dir ${target} SOURCE_DIR)
+ get_target_property(sources ${target} SOURCES)
+ foreach(source ${sources})
+ if (source MATCHES TARGET_OBJECTS)
+ continue()
+ endif()
+ path_join(file "${source_dir}" "${source}")
+ file(RELATIVE_PATH relfile ${PROJECT_SOURCE_DIR} ${file})
+ set(files ${files} ${relfile})
+ set(abs_files ${abs_files} ${file})
+ endforeach()
+
+ target_compile_definitions(${target} PRIVATE -DAPT_DOMAIN="${domain}")
+ endforeach()
+
+ if("${scripts}" STREQUAL "")
+ set(sh_pot "/dev/null")
+ else()
+ set(sh_pot ${CMAKE_CURRENT_BINARY_DIR}/${domain}.sh.pot)
+ # Create the template for this specific sub-domain
+ add_custom_command (OUTPUT ${sh_pot}
+ COMMAND xgettext ${xgettext_params} -L Shell
+ -o ${sh_pot} ${scripts}
+ DEPENDS ${abs_scripts}
+ VERBATIM
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ )
+ endif()
+
+
+ add_custom_command (OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${domain}.c.pot
+ COMMAND xgettext ${xgettext_params} -k_ -kN_
+ --keyword=P_:1,2
+ -o ${CMAKE_CURRENT_BINARY_DIR}/${domain}.c.pot ${files}
+ DEPENDS ${abs_files}
+ VERBATIM
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ )
+
+ # We are building a ${domain}.pot with a header for launchpad, but we also
+ # build a ${domain.pot}-tmp as a byproduct. The msgfmt command than depend
+ # on the byproduct while their target depends on the output, so that msgfmt
+ # does not have to be rerun if nothing in the template changed.
+ #
+ # Make sure the .pot-tmp has no line numbers, to avoid useless rebuilding
+ # of .mo files.
+ add_custom_command (OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot
+ BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot-tmp
+ COMMAND msgcomm --more-than=0 --omit-header --sort-by-file --add-location=file
+ ${sh_pot}
+ ${CMAKE_CURRENT_BINARY_DIR}/${domain}.c.pot
+ --output=${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot-tmp0
+ COMMAND msgcomm --more-than=0 --sort-by-file
+ ${sh_pot}
+ ${CMAKE_CURRENT_BINARY_DIR}/${domain}.c.pot
+ --output=${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot
+ COMMAND cmake -E copy_if_different
+ ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot-tmp0
+ ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot-tmp
+ DEPENDS ${sh_pot}
+ ${CMAKE_CURRENT_BINARY_DIR}/${domain}.c.pot
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ )
+
+ # We need a target to depend on otherwise, the msgmerge might not get called
+ # with the make generator
+ add_custom_target(nls-${domain}-template DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot)
+
+ # Build .mo files
+ file(GLOB translations "${PROJECT_SOURCE_DIR}/po/*.po")
+ list(SORT translations)
+ foreach(file ${translations})
+ get_filename_component(langcode ${file} NAME_WE)
+ if ("${langcode}" IN_LIST NLS_EXCLUDE_LANGUAGES)
+ continue()
+ endif()
+ set(outdir ${CMAKE_CURRENT_BINARY_DIR}/locale/${langcode}/LC_MESSAGES)
+ file(MAKE_DIRECTORY ${outdir})
+ # Command to merge and compile the messages. As explained in the custom
+ # command for msgcomm, this depends on byproduct to avoid reruns
+ add_custom_command(OUTPUT ${outdir}/${domain}.po
+ COMMAND msgmerge -qo ${outdir}/${domain}.po ${file} ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot-tmp
+ DEPENDS ${file} ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot-tmp
+ )
+ add_custom_command(OUTPUT ${outdir}/${domain}.mo
+ COMMAND msgfmt --statistics -o ${outdir}/${domain}.mo ${outdir}/${domain}.po
+ DEPENDS ${outdir}/${domain}.po
+ )
+
+ set(mofiles ${mofiles} ${outdir}/${domain}.mo)
+ install(FILES ${outdir}/${domain}.mo
+ DESTINATION "${CMAKE_INSTALL_LOCALEDIR}/${langcode}/LC_MESSAGES")
+ endforeach(file ${translations})
+
+ add_custom_target(nls-${domain} ALL DEPENDS ${mofiles} nls-${domain}-template)
+endfunction()
+
+# Usage: apt_add_update_po(output domain [domain ...])
+function(apt_add_update_po)
+ set(options)
+ set(oneValueArgs TEMPLATE)
+ set(multiValueArgs DOMAINS EXCLUDE_LANGUAGES)
+ cmake_parse_arguments(NLS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+ set(output ${CMAKE_CURRENT_SOURCE_DIR}/${NLS_TEMPLATE}.pot)
+ foreach(domain ${NLS_DOMAINS})
+ list(APPEND potfiles ${CMAKE_CURRENT_BINARY_DIR}/${domain}.pot)
+ endforeach()
+
+ get_filename_component(master_name ${output} NAME_WE)
+ add_custom_target(nls-${master_name}
+ COMMAND msgcomm --sort-by-file --add-location=file
+ --more-than=0 --output=${output}
+ ${potfiles}
+ DEPENDS ${potfiles})
+
+ file(GLOB translations "${PROJECT_SOURCE_DIR}/po/*.po")
+ if (NOT TARGET update-po)
+ add_custom_target(update-po)
+ endif()
+ foreach(translation ${translations})
+ get_filename_component(langcode ${translation} NAME_WE)
+ if ("${langcode}" IN_LIST NLS_EXCLUDE_LANGUAGES)
+ continue()
+ endif()
+ add_custom_target(update-po-${langcode}
+ COMMAND msgmerge -q --previous --update --backup=none ${translation} ${output}
+ DEPENDS nls-${master_name}
+ )
+ add_dependencies(update-po update-po-${langcode})
+ endforeach()
+ add_dependencies(update-po nls-${master_name})
+endfunction()
+
+function(apt_add_po_statistics excluded)
+ add_custom_target(statistics)
+ file(GLOB translations "${PROJECT_SOURCE_DIR}/po/*.po")
+ foreach(translation ${translations})
+ get_filename_component(langcode ${translation} NAME_WE)
+ if ("${langcode}" IN_LIST excluded)
+ add_custom_command(
+ TARGET statistics PRE_BUILD
+ COMMAND printf "%-6s " "${langcode}:"
+ COMMAND echo "ignored"
+ VERBATIM
+ )
+ continue()
+ endif()
+ add_custom_command(
+ TARGET statistics PRE_BUILD
+ COMMAND printf "%-6s " "${langcode}:"
+ COMMAND msgfmt --statistics -o /dev/null ${translation}
+ VERBATIM
+ )
+ endforeach()
+endfunction()
diff --git a/CMake/apti18n.h.in b/CMake/apti18n.h.in
new file mode 100644
index 0000000..1929795
--- /dev/null
+++ b/CMake/apti18n.h.in
@@ -0,0 +1,29 @@
+// -*- mode: cpp; mode: fold -*-
+/* Internationalization macros for apt. This header should be included last
+ in each C file. */
+
+// Set by autoconf
+#cmakedefine USE_NLS
+
+#ifdef USE_NLS
+// apt will use the gettext implementation of the C library
+#include <libintl.h>
+#include <locale.h>
+# ifdef APT_DOMAIN
+# define _(x) dgettext(APT_DOMAIN,x)
+# define P_(msg,plural,n) dngettext(APT_DOMAIN,msg,plural,n)
+# else
+# define _(x) gettext(x)
+# define P_(msg,plural,n) ngettext(msg,plural,n)
+# endif
+# define N_(x) x
+#else
+// apt will not use any gettext
+# define setlocale(a, b)
+# define textdomain(a)
+# define bindtextdomain(a, b)
+# define _(x) x
+# define P_(msg,plural,n) (n == 1 ? msg : plural)
+# define N_(x) x
+# define dgettext(d, m) m
+#endif
diff --git a/CMake/config.h.in b/CMake/config.h.in
new file mode 100644
index 0000000..a9528cc
--- /dev/null
+++ b/CMake/config.h.in
@@ -0,0 +1,90 @@
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+#cmakedefine WORDS_BIGENDIAN
+
+/* Define if we have the timegm() function */
+#cmakedefine HAVE_TIMEGM
+
+/* Define if we have the zlib library for gzip */
+#cmakedefine HAVE_ZLIB
+
+/* Define if we have the bz2 library for bzip2 */
+#cmakedefine HAVE_BZ2
+
+/* Define if we have the lzma library for lzma/xz */
+#cmakedefine HAVE_LZMA
+
+/* Define if we have the lz4 library for lz4 */
+#cmakedefine HAVE_LZ4
+
+/* Define if we have the zstd library for zst */
+#cmakedefine HAVE_ZSTD
+
+/* Define if we have the systemd library */
+#cmakedefine HAVE_SYSTEMD
+
+/* Define if we have the udev library */
+#cmakedefine HAVE_UDEV
+
+/* Define if we have the seccomp library */
+#cmakedefine HAVE_SECCOMP
+
+/* These two are used by the statvfs shim for glibc2.0 and bsd */
+/* Define if we have sys/vfs.h */
+#cmakedefine HAVE_VFS_H
+#cmakedefine HAVE_STRUCT_STATFS_F_TYPE
+
+/* Define if we have sys/mount.h */
+#cmakedefine HAVE_MOUNT_H
+
+/* Define if we have sys/endian.h */
+#cmakedefine HAVE_SYS_ENDIAN_H
+
+/* Define if we have machine/endian.h */
+#cmakedefine HAVE_MACHINE_ENDIAN_H
+
+/* Check for getresuid() function and similar ones */
+#cmakedefine HAVE_GETRESUID
+#cmakedefine HAVE_GETRESGID
+#cmakedefine HAVE_SETRESUID
+#cmakedefine HAVE_SETRESGID
+
+/* Check for ptsname_r() */
+#cmakedefine HAVE_PTSNAME_R
+
+/* Define the arch name string */
+#define COMMON_ARCH "${COMMON_ARCH}"
+
+/* The package name string */
+#define PACKAGE "${PACKAGE}"
+
+/* The version number string */
+#define PACKAGE_VERSION "${PACKAGE_VERSION}"
+
+/* The mail address to reach upstream */
+#define PACKAGE_MAIL "${PACKAGE_MAIL}"
+
+/* Various directories */
+#cmakedefine CMAKE_INSTALL_FULL_BINDIR "${CMAKE_INSTALL_FULL_BINDIR}"
+#cmakedefine STATE_DIR "${STATE_DIR}"
+#cmakedefine CACHE_DIR "${CACHE_DIR}"
+#cmakedefine LOG_DIR "${LOG_DIR}"
+#cmakedefine CONF_DIR "${CONF_DIR}"
+#cmakedefine LIBEXEC_DIR "${LIBEXEC_DIR}"
+#cmakedefine BIN_DIR "${BIN_DIR}"
+#cmakedefine DPKG_DATADIR "${DPKG_DATADIR}"
+
+/* Group of the root user */
+#cmakedefine ROOT_GROUP "${ROOT_GROUP}"
+
+/* defined if __builtin_ia32_crc32{s,d}i() exists in an sse4.2 target */
+#cmakedefine HAVE_FMV_SSE42_AND_CRC32
+#cmakedefine HAVE_FMV_SSE42_AND_CRC32DI
+
+#define APT_8_CLEANER_HEADERS
+#define APT_9_CLEANER_HEADERS
+#define APT_10_CLEANER_HEADERS
+#define APT_15_CLEANER_HEADERS
+
+/* unrolling is faster combined with an optimizing compiler */
+#define SHA2_UNROLL_TRANSFORM
diff --git a/CMake/endian.h.in b/CMake/endian.h.in
new file mode 100644
index 0000000..1d9198c
--- /dev/null
+++ b/CMake/endian.h.in
@@ -0,0 +1,9 @@
+#include <config.h>
+
+#ifdef HAVE_MACHINE_ENDIAN_H
+#include <machine/endian.h>
+#endif
+#ifdef HAVE_SYS_ENDIAN_H
+#include <sys/types.h>
+#include <sys/endian.h>
+#endif
diff --git a/CMake/run_if_exists.sh b/CMake/run_if_exists.sh
new file mode 100755
index 0000000..97edd4c
--- /dev/null
+++ b/CMake/run_if_exists.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Small helper for running a command
+out=""
+if [ "$1" = "--stdout" ]; then
+ out="$2"
+ shift 2
+fi
+
+if [ -e "$1" ]; then
+ shift
+ if [ "$out" ]; then
+ exec "$@" > $out
+ else
+ exec "$@"
+ fi
+fi
diff --git a/CMake/statvfs.h.in b/CMake/statvfs.h.in
new file mode 100644
index 0000000..d0ec238
--- /dev/null
+++ b/CMake/statvfs.h.in
@@ -0,0 +1,13 @@
+/* Compatibility for systems with out Single Unix Spec statvfs */
+#include <config.h>
+
+#ifdef HAVE_VFS_H
+#include <sys/vfs.h>
+#endif
+
+#ifdef HAVE_MOUNT_H
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+
+#define statvfs statfs
diff --git a/CMake/vendor_substitute.cmake b/CMake/vendor_substitute.cmake
new file mode 100644
index 0000000..71449c9
--- /dev/null
+++ b/CMake/vendor_substitute.cmake
@@ -0,0 +1,8 @@
+file(READ ${IN} input)
+foreach(variable ${VARS})
+ execute_process(COMMAND ${PROJECT_SOURCE_DIR}/vendor/getinfo
+ --vendor ${CURRENT_VENDOR} ${variable}
+ OUTPUT_VARIABLE value OUTPUT_STRIP_TRAILING_WHITESPACE)
+ string(REPLACE "&${variable};" "${value}" input "${input}")
+endforeach()
+file(WRITE ${OUT} "${input}")
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..bbc39dc
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,260 @@
+# Copyright (C) 2009, 2016 Julian Andres Klode <jak@debian.org>.
+# Licensed under the same terms as APT; i.e. GPL 2 or later.
+
+# set minimum version
+project(apt)
+cmake_minimum_required(VERSION 3.4.0)
+# Generic header locations
+include_directories(${PROJECT_BINARY_DIR}/include)
+
+
+enable_testing()
+
+option(WITH_DOC "Build documentation." ON)
+option(USE_NLS "Localisation support." ON)
+
+set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMake")
+
+# Add coverage target
+set(CMAKE_CXX_FLAGS_COVERAGE "-g -fprofile-arcs -ftest-coverage")
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "-lgcov")
+set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "-lgcov")
+
+# Work around bug in GNUInstallDirs
+if (EXISTS "/etc/debian_version")
+ set(CMAKE_INSTALL_LIBEXECDIR "lib")
+endif()
+
+# Include stuff
+include(Misc)
+include(CheckIncludeFiles)
+include(CheckFunctionExists)
+include(CheckStructHasMember)
+include(GNUInstallDirs)
+include(TestBigEndian)
+find_package(Threads REQUIRED)
+find_package(LFS REQUIRED)
+find_package(Iconv REQUIRED)
+
+find_package(Perl REQUIRED)
+
+if(USE_NLS)
+ find_package(Intl REQUIRED)
+ link_libraries(${Intl_LIBRARIES})
+ include_directories(${Intl_INCLUDE_DIRS})
+endif()
+
+# Add large file support
+add_compile_options(${LFS_COMPILE_OPTIONS})
+add_definitions(${LFS_DEFINITIONS})
+link_libraries(${LFS_LIBRARIES})
+
+# Set compiler flags
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
+
+add_optional_compile_options(Wall)
+add_optional_compile_options(Wextra)
+add_optional_compile_options(Wcast-align)
+add_optional_compile_options(Wlogical-op)
+add_optional_compile_options(Wredundant-decls)
+add_optional_compile_options(Wmissing-declarations)
+add_optional_compile_options(Wunsafe-loop-optimizations)
+add_optional_compile_options(Wctor-dtor-privacy)
+add_optional_compile_options(Wdisabled-optimization)
+add_optional_compile_options(Winit-self)
+add_optional_compile_options(Wmissing-include-dirs)
+add_optional_compile_options(Wnoexcept)
+add_optional_compile_options(Wsign-promo)
+add_optional_compile_options(Wundef)
+add_optional_compile_options(Wdouble-promotion)
+
+# apt-ftparchive dependencies
+find_package(BerkeleyDB REQUIRED)
+if (BERKELEY_DB_FOUND)
+ set(HAVE_BDB 1)
+endif()
+
+find_package(GnuTLS REQUIRED)
+if (GNUTLS_FOUND)
+ set(HAVE_GNUTLS 1)
+endif()
+
+# (De)Compressor libraries
+find_package(ZLIB REQUIRED)
+if (ZLIB_FOUND)
+ set(HAVE_ZLIB 1)
+endif()
+
+
+find_package(BZip2 REQUIRED)
+if (BZIP2_FOUND)
+ set(HAVE_BZ2 1)
+endif()
+
+find_package(LZMA REQUIRED)
+if (LZMA_FOUND)
+ set(HAVE_LZMA 1)
+endif()
+
+
+find_package(LZ4 REQUIRED)
+if (LZ4_FOUND)
+ set(HAVE_LZ4 1)
+endif()
+
+find_package(Zstd)
+if (ZSTD_FOUND)
+ set(HAVE_ZSTD 1)
+endif()
+
+
+find_package(Udev)
+if (UDEV_FOUND)
+ set(HAVE_UDEV 1)
+endif()
+
+find_package(Systemd)
+if (SYSTEMD_FOUND)
+ set(HAVE_SYSTEMD 1)
+endif()
+
+find_package(Seccomp)
+if (SECCOMP_FOUND)
+ set(HAVE_SECCOMP 1)
+endif()
+
+# Mount()ing and stat()ing and friends
+check_symbol_exists(statfs sys/vfs.h HAVE_VFS_H)
+check_include_files(sys/params.h HAVE_PARAMS_H)
+check_symbol_exists(statfs sys/mount.h HAVE_MOUNT_H)
+if (NOT HAVE_VFS_H AND NOT HAVE_MOUNT_H)
+ message(FATAL_ERROR "Can find neither statvfs() nor statfs()")
+endif()
+
+check_function_exists(statvfs HAVE_STATVFS)
+if (NOT HAVE_STATVFS)
+ configure_file(CMake/statvfs.h.in ${PROJECT_BINARY_DIR}/include/sys/statvfs.h COPYONLY)
+endif()
+
+CHECK_STRUCT_HAS_MEMBER("struct statfs" f_type sys/vfs.h HAVE_STRUCT_STATFS_F_TYPE)
+
+# Other checks
+check_function_exists(getresuid HAVE_GETRESUID)
+check_function_exists(getresgid HAVE_GETRESGID)
+check_function_exists(setresuid HAVE_SETRESUID)
+check_function_exists(setresgid HAVE_SETRESGID)
+check_function_exists(ptsname_r HAVE_PTSNAME_R)
+check_function_exists(timegm HAVE_TIMEGM)
+test_big_endian(WORDS_BIGENDIAN)
+
+# FreeBSD
+add_definitions(-D_WITH_GETLINE=1)
+
+CHECK_INCLUDE_FILES(machine/endian.h HAVE_MACHINE_ENDIAN_H)
+CHECK_INCLUDE_FILES(sys/endian.h HAVE_SYS_ENDIAN_H)
+CHECK_INCLUDE_FILES(endian.h HAVE_ENDIAN_H)
+if (NOT HAVE_ENDIAN_H)
+ if (HAVE_MACHINE_ENDIAN_H OR HAVE_SYS_ENDIAN_H)
+ configure_file(CMake/endian.h.in ${PROJECT_BINARY_DIR}/include/endian.h)
+ else()
+ message(FATAL_ERROR "Cannot find endian.h")
+ endif()
+endif()
+
+
+include(CheckTypeSize)
+set(CMAKE_EXTRA_INCLUDE_FILES "signal.h")
+check_type_size("sig_t" SIG_T LANGUAGE "CXX")
+check_type_size("sighandler_t" SIGHANDLER_T LANGUAGE "CXX")
+set(CMAKE_EXTRA_INCLUDE_FILES)
+if (NOT HAVE_SIGHANDLER_T)
+ if (HAVE_SIG_T)
+ add_definitions(-Dsighandler_t=sig_t)
+ else()
+ message(FATAL_ERROR "Platform defines neither sig_t nor sighandler_t")
+ endif()
+endif()
+
+# Handle resolving
+check_function_exists(res_init HAVE_LIBC_RESOLV)
+if(HAVE_LIBC_RESOLV)
+ set(RESOLV_LIBRARIES)
+else()
+ set(RESOLV_LIBRARIES -lresolv)
+endif()
+
+# Check multiversioning
+include(CheckCxxTarget)
+check_cxx_target(HAVE_FMV_SSE42_AND_CRC32 "sse4.2" "__builtin_ia32_crc32si(0, 1llu);")
+check_cxx_target(HAVE_FMV_SSE42_AND_CRC32DI "sse4.2" "__builtin_ia32_crc32di(0, 1llu);")
+
+# Configure some variables like package, version and architecture.
+set(PACKAGE ${PROJECT_NAME})
+set(PACKAGE_MAIL "APT Development Team <deity@lists.debian.org>")
+set(PACKAGE_VERSION "1.8.2.3")
+
+if (NOT DEFINED DPKG_DATADIR)
+ execute_process(COMMAND ${PERL_EXECUTABLE} -MDpkg -e "print $Dpkg::DATADIR;"
+ OUTPUT_VARIABLE DPKG_DATADIR_CMD OUTPUT_STRIP_TRAILING_WHITESPACE)
+ message(STATUS "Found dpkg data dir: ${DPKG_DATADIR_CMD}")
+ set(DPKG_DATADIR "${DPKG_DATADIR_CMD}" CACHE PATH "dpkg data directory")
+endif()
+if (NOT DEFINED COMMON_ARCH)
+ execute_process(COMMAND dpkg-architecture -qDEB_HOST_ARCH
+ OUTPUT_VARIABLE COMMON_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE)
+endif()
+if (NOT DEFINED ROOT_GROUP)
+ execute_process(COMMAND id -gn root
+ OUTPUT_VARIABLE ROOT_GROUP OUTPUT_STRIP_TRAILING_WHITESPACE)
+ message(STATUS "Found root group: ${ROOT_GROUP}")
+endif()
+set(ROOT_GROUP "${ROOT_GROUP}" CACHE STRING "Group of root (e.g.: wheel or root)")
+
+# Set various directories
+set(STATE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/lib/apt" CACHE PATH "Your /var/lib/apt")
+set(CACHE_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/cache/apt" CACHE PATH "Your /var/cache/apt")
+set(LOG_DIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}/log/apt" CACHE PATH "Your /var/log/apt")
+set(CONF_DIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}/apt" CACHE PATH "Your /etc/apt")
+set(LIBEXEC_DIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/apt" CACHE PATH "Your /usr/libexec/apt")
+set(BIN_DIR "${CMAKE_INSTALL_FULL_BINDIR}")
+
+
+# Configure our configuration headers (config.h and apti18n.h)
+configure_file(CMake/config.h.in ${PROJECT_BINARY_DIR}/include/config.h)
+configure_file(CMake/apti18n.h.in ${PROJECT_BINARY_DIR}/include/apti18n.h)
+
+# Add our subdirectories
+add_subdirectory(vendor)
+add_subdirectory(apt-pkg)
+add_subdirectory(apt-private)
+add_subdirectory(apt-inst)
+add_subdirectory(cmdline)
+add_subdirectory(completions)
+add_subdirectory(doc)
+add_subdirectory(dselect)
+add_subdirectory(ftparchive)
+add_subdirectory(methods)
+add_subdirectory(test)
+
+if (USE_NLS)
+add_subdirectory(po)
+
+# Link update-po4a into the update-po target
+add_dependencies(update-po update-po4a)
+endif()
+
+# Create our directories.
+install_empty_directories(
+ ${CONF_DIR}/apt.conf.d
+ ${CONF_DIR}/auth.conf.d
+ ${CONF_DIR}/preferences.d
+ ${CONF_DIR}/sources.list.d
+ ${CONF_DIR}/trusted.gpg.d
+ ${CACHE_DIR}/archives/partial
+ ${STATE_DIR}/lists/partial
+ ${STATE_DIR}/mirrors/partial
+ ${STATE_DIR}/periodic
+ ${LOG_DIR}
+)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..2f9ab10
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,22 @@
+Apt is copyright 1997, 1998, 1999 Jason Gunthorpe and others.
+Apt is currently developed by APT Development Team <deity@lists.debian.org>.
+
+License: GPLv2+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+See /usr/share/common-licenses/GPL-2, or
+<http://www.gnu.org/copyleft/gpl.txt> for the terms of the latest version
+of the GNU General Public License.
diff --git a/COPYING.GPL b/COPYING.GPL
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/COPYING.GPL
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ 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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..49934e4
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:buster
+COPY . /tmp
+WORKDIR /tmp
+RUN sed -i s#://deb.debian.org#://cdn-fastly.deb.debian.org# /etc/apt/sources.list \
+ && apt-get update \
+ && adduser --home /home/travis travis --quiet --disabled-login --gecos "" --uid 1000 \
+ && env DEBIAN_FRONTEND=noninteractive apt-get install build-essential ccache ninja-build expect curl git -q -y \
+ && env DEBIAN_FRONTEND=noninteractive ./prepare-release travis-ci \
+ && dpkg-reconfigure ccache \
+ && rm -f /etc/dpkg/dpkg.cfg.d/excludes \
+ && rm -r /tmp/* \
+ && apt-get clean
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8a8b3b6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,215 @@
+APT
+===
+
+apt is the main commandline package manager for Debian and its derivatives.
+It provides commandline tools for searching and managing as well as querying
+information about packages as well as low-level access to all features
+provided by the libapt-pkg and libapt-inst libraries which higher-level
+package managers can depend upon.
+
+Included tools are:
+
+* **apt-get** for retrieval of packages and information about them
+ from authenticated sources and for installation, upgrade and
+ removal of packages together with their dependencies
+* **apt-cache** for querying available information about installed
+ as well as installable packages
+* **apt-cdrom** to use removable media as a source for packages
+* **apt-config** as an interface to the configuration settings
+* **apt-key** as an interface to manage authentication keys
+* **apt-extracttemplates** to be used by debconf to prompt for configuration
+ questions before installation
+* **apt-ftparchive** creates Packages and other index files
+ needed to publish an archive of debian packages
+* **apt-sortpkgs** is a Packages/Sources file normalizer
+* **apt** is a high-level commandline interface for better interactive usage
+
+The libraries libapt-pkg and libapt-inst are also maintained as part of this project,
+alongside various additional binaries like the acquire-methods used by them.
+Bindings for Python ([python-apt](https://tracker.debian.org/pkg/python-apt)) and
+Perl ([libapt-pkg-perl](https://tracker.debian.org/pkg/libapt-pkg-perl)) are available as separated projects.
+
+Discussion happens mostly on [the mailinglist](mailto:deity@lists.debian.org) ([archive](https://lists.debian.org/deity/)) and on [IRC](irc://irc.oftc.net/debian-apt).
+Our bugtracker as well as a general overview can be found at the [Debian Tracker page](https://tracker.debian.org/pkg/apt).
+
+
+Contributing
+------------
+APT is maintained in git, the official repository being located at
+[https://salsa.debian.org/apt-team/apt](https://salsa.debian.org/apt-team/apt),
+but also available at other locations like [GitHub](https://github.com/Debian/apt).
+
+The default branch is `master`, other branches targeted at different
+derivatives and releases being used as needed. Various topic branches in
+different stages of completion might be branched of from those, which you
+are encouraged to do as well.
+
+### Coding
+
+APT uses cmake. To start building, you need to run
+
+ cmake <path to source directory>
+
+from a build directory. For example, if you want to build in the source tree,
+run:
+
+ cmake .
+
+Then you can use make as you normally would (pass -j <count> to perform <count>
+jobs in parallel).
+
+You can also use the Ninja generator of cmake, to do that pass
+ -G Ninja
+to the cmake invocation, and then use ninja instead of make.
+
+The source code uses in most parts a relatively uncommon indent convention,
+namely 3 spaces with 8 space tab (see [doc/style.txt](https://anonscm.debian.org/git/apt/apt.git/tree/doc/style.txt) for more on this).
+Adhering to it avoids unnecessary code-churn destroying history (aka: `git blame`)
+and you are therefore encouraged to write patches in this style.
+Your editor can surely help you with this, for vim the settings would be
+`setlocal shiftwidth=3 noexpandtab tabstop=8`
+(the later two are the default configuration and could therefore be omitted).
+
+### Translations
+
+While we welcome contributions here, we highly encourage you to contact the [Debian Internationalization (i18n) team](https://wiki.debian.org/Teams/I18n).
+Various language teams have formed which can help you creating, maintaining
+and improving a translation, while we could only do a basic syntax check of the
+file format…
+
+Further more, Translating APT is split into two independent parts:
+The program translation, meaning the messages printed by the tools,
+as well as the manpages and other documentation shipped with APT.
+
+### Bug triage
+
+Software tools like APT which are used by thousands of users every
+day have a steady flow of incoming bugreports. Not all of them are really
+bugs in APT: It can be packaging bugs like failing maintainer scripts a
+user reports against apt, because apt was the command he executed leading
+to this failure or various wishlist items for new features. Given enough time
+also the occasional duplicate enters the system.
+Our bugtracker is therefore full with open bugreports which are waiting for you! ;)
+
+Testing
+-------
+
+### Manual execution
+
+When you make changes and want to run them manually, you can just do so. CMake
+automatically inserts an rpath so the binaries find the correct libraries.
+
+Note that you have to invoke CMake with the right install prefix set (e.g.
+`-DCMAKE_INSTALL_PREFIX=/usr`) to have your build find and use the right files
+by default or alternatively set the locations at runtime via an `APT_CONFIG`
+configuration file.
+
+### Integration tests
+
+There is an extensive integration testsuite available which can be run via:
+
+ $ ./test/integration/run-tests
+
+Each test can also be run individually as well. The tests are very noisy by
+default, especially so while running all of them it might be beneficial to
+enabling quiet (`-q`) or very quiet (`-qq`) mode. The tests can also be run in
+parallel via `-j X` where `X` is the number of jobs to run.
+
+While these tests are not executed at package build-time as they require
+additional dependencies, the repository contains the configuration needed to
+run them on [Travis CI](https://travis-ci.org/) and
+[Shippable](https://shippable.com/) as well as via autopkgtests e.g. on
+[Debian Continuous Integration](https://ci.debian.net/packages/a/apt/).
+
+A testcase here is a shellscript embedded in a framework creating an environment in which
+apt tools can be used naturally without root-rights to test every aspect of its behavior
+itself as well as in conjunction with dpkg and other tools while working with packages.
+
+
+### Unit tests
+
+These tests are gtest-dev based, executed by ctest, reside in `./test/libapt`
+and can be run with `make test`. They are executed at package build-time, but
+not by `make`. CTest by default does not show the output of tests, even if they
+failed, so to see more details you can also run them with `ctest --verbose`.
+
+Debugging
+---------
+
+APT does many things, so there is no central debug mode which could be
+activated. It uses instead various config-options to activate debug output
+in certain areas. The following describes some common scenarios and generally
+useful options, but is in no way exhaustive.
+
+Note that you should *NEVER* use these settings as root to avoid accidents.
+Simulation mode (`-s`) is usually sufficient to help you run apt as a non-root user.
+
+### Using different state files
+
+If a dependency solver bug is reported, but can't be reproduced by the
+triager easily, it is beneficial to ask the reporter for the
+`/var/lib/dpkg/status` file, which includes the packages installed on the
+system and in which version. Such a file can then be used via the option
+`dir::state::status`. Beware of different architecture settings!
+Bugreports usually include this information in the template. Assuming you
+already have the `Packages` files for the architecture (see `sources.list`
+manpage for the `arch=` option) you can change to a different architecture
+with a config file like:
+
+ APT::Architecture "arch1";
+ #clear APT::Architectures;
+ APT:: Architectures { "arch1"; "arch2"; }
+
+If a certain mirror state is needed, see if you can reproduce it with [snapshot.debian.org](http://snapshot.debian.org/).
+Your sources.list file (`dir::etc::sourcelist`) has to be correctly mention the repository,
+but if it does, you can use different downloaded archive state files via `dir::state::lists`.
+
+In case manually vs. automatically installed matters, you can ask the reporter for
+the `/var/lib/apt/extended_states` file and use it with `dir::state::extended_states`.
+
+### Dependency resolution
+
+APT works in its internal resolver in two stages: First all packages are visited
+and marked for installation, keep back or removal. Option `Debug::pkgDepCache::Marker`
+shows this. This also decides which packages are to be installed to satisfy dependencies,
+which can be seen by `Debug::pkgDepCache::AutoInstall`. After this is done, we might
+be in a situation in which two packages want to be installed, but only on of them can be.
+It is the job of the pkgProblemResolver to decide which of two packages 'wins' and can
+therefore decide what has to happen. You can see the contenders as well as their fight and
+the resulting resolution with `Debug::pkgProblemResolver`.
+
+### Downloading files
+
+Various binaries (called 'methods') are tasked with downloading files. The Acquire system
+talks to them via simple text protocol. Depending on which side you want to see, either
+`Debug::pkgAcquire::Worker` or `Debug::Acquire::http` (or similar) will show the messages.
+
+The integration tests use a simple self-built webserver which also logs. If you find that
+the http(s) methods do not behave like they should be try to implement this behavior in the
+webserver for simpler and more controlled testing.
+
+### Installation order
+
+Dependencies are solved, packages downloaded: Everything read for the installation!
+The last step in the chain is often forgotten, but still very important:
+Packages have to be installed in a particular order so that their dependencies are
+satisfied, but at the same time you don't want to install very important and optional
+packages at the same time if possible, so that a broken optional package does not
+block the correct installation of very important packages. Which option to use depends on
+if you are interested in the topology sorting (`Debug::pkgOrderList`), the dependency-aware
+cycle and unconfigured prevention (`Debug::pkgPackageManager`) or the actual calls
+to dpkg (`Debug::pkgDpkgPm`).
+
+
+Additional documentation
+------------------------
+
+Many more things could and should be said about APT and its usage but are more
+targeted at developers of related programs or only of special interest.
+
+* [Protocol specification of APTs communication with external dependency solvers (EDSP)](./doc/external-dependency-solver-protocol.md)
+* [Protocol specification of APTs communication with external installation planners (EIPP)](./doc/external-installation-planner-protocol.md)
+* [Howto use and configure APT to acquire additional files in 'update' operations](./doc/acquire-additional-files.md)
+* [Download and package installation progress reporting details](./doc/progress-reporting.md)
+* [Remarks on DNS SRV record support in APT](./doc/srv-records-support.md)
+* [Protocol specification of APT interfacing with external hooks via JSON](./doc/json-hooks-protocol.md)
diff --git a/abicheck/apt_build.xml.in b/abicheck/apt_build.xml.in
new file mode 100644
index 0000000..32886d9
--- /dev/null
+++ b/abicheck/apt_build.xml.in
@@ -0,0 +1,12 @@
+<version>
+ build-branch
+</version>
+
+ <headers>
+ @build_path@/include/apt-pkg
+ </headers>
+
+ <libs>
+ @build_path@/apt-pkg/
+ @build_path@/apt-inst/
+ </libs>
diff --git a/abicheck/apt_installed.xml.in b/abicheck/apt_installed.xml.in
new file mode 100644
index 0000000..c3ddd08
--- /dev/null
+++ b/abicheck/apt_installed.xml.in
@@ -0,0 +1,11 @@
+<version>
+ installed
+</version>
+
+<headers>
+ /usr/include/apt-pkg/
+</headers>
+
+<libs>
+ @installed_libapt@
+</libs>
diff --git a/abicheck/run_abi_test b/abicheck/run_abi_test
new file mode 100755
index 0000000..491616b
--- /dev/null
+++ b/abicheck/run_abi_test
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# ensure we are in the abibreak subdirectory
+cd "$(readlink -f $(dirname $0))"
+
+if [ ! -d ../build ]; then
+ echo "../build missing, did you run make?"
+ exit 1
+fi
+
+if ! command -v abi-compliance-checker 2>/dev/null >&2; then
+ echo "Please install the 'abi-compliance-checker' package"
+ exit 1
+fi
+
+LIBPATH=$(find /usr/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH) -type f -regex '.*/libapt-\(pkg\|inst\)\.so\..*' -printf %p\\\\n)
+sed s#@installed_libapt@#$LIBPATH# apt_installed.xml.in > apt_installed.xml
+
+BUILDPATH=$(readlink -f ../build)
+sed s#@build_path@#$BUILDPATH# apt_build.xml.in > apt_build.xml
+
+abi-compliance-checker -l apt -d1 apt_installed.xml -d2 apt_build.xml $@
diff --git a/apt-inst/CMakeLists.txt b/apt-inst/CMakeLists.txt
new file mode 100644
index 0000000..31da115
--- /dev/null
+++ b/apt-inst/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Include apt-pkg directly, as some files have #include <system.h>
+include_directories(${PROJECT_BINARY_DIR}/include/apt-pkg)
+
+# Set the version of the library
+set(MAJOR 2.0)
+set(MINOR 0)
+set(APT_INST_MAJOR ${MAJOR} PARENT_SCOPE)
+
+# Definition of the C++ files used to build the library - note that this
+# is expanded at CMake time, so you have to rerun cmake if you add or remove
+# a file (you can just run cmake . in the build directory)
+file(GLOB_RECURSE library "*.cc")
+file(GLOB_RECURSE headers "*.h")
+
+# Create a library using the C++ files
+add_library(apt-inst SHARED ${library})
+
+# Link the library and set the SONAME
+target_link_libraries(apt-inst PUBLIC apt-pkg ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(apt-inst PRIVATE ${CMAKE_THREAD_LIBS_INIT})
+set_target_properties(apt-inst PROPERTIES VERSION ${MAJOR}.${MINOR})
+set_target_properties(apt-inst PROPERTIES SOVERSION ${MAJOR})
+add_version_script(apt-inst)
+
+# Install the library and the headers
+install(TARGETS apt-inst LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(FILES ${headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/apt-pkg)
+flatify(${PROJECT_BINARY_DIR}/include/apt-pkg/ "${headers}")
diff --git a/apt-inst/contrib/arfile.cc b/apt-inst/contrib/arfile.cc
new file mode 100644
index 0000000..6d4a1f1
--- /dev/null
+++ b/apt-inst/contrib/arfile.cc
@@ -0,0 +1,179 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ AR File - Handle an 'AR' archive
+
+ AR Archives have plain text headers at the start of each file
+ section. The headers are aligned on a 2 byte boundary.
+
+ Information about the structure of AR files can be found in ar(5)
+ on a BSD system, or in the binutils source.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/arfile.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <string>
+#include <string.h>
+#include <sys/types.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+struct ARArchive::MemberHeader
+{
+ char Name[16];
+ char MTime[12];
+ char UID[6];
+ char GID[6];
+ char Mode[8];
+ char Size[10];
+ char Magic[2];
+};
+
+// ARArchive::ARArchive - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ARArchive::ARArchive(FileFd &File) : List(0), File(File)
+{
+ LoadHeaders();
+}
+ /*}}}*/
+// ARArchive::~ARArchive - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ARArchive::~ARArchive()
+{
+ while (List != 0)
+ {
+ Member *Tmp = List;
+ List = List->Next;
+ delete Tmp;
+ }
+}
+ /*}}}*/
+// ARArchive::LoadHeaders - Load the headers from each file /*{{{*/
+// ---------------------------------------------------------------------
+/* AR files are structured with a 8 byte magic string followed by a 60
+ byte plain text header then the file data, another header, data, etc */
+bool ARArchive::LoadHeaders()
+{
+ off_t Left = File.Size();
+
+ // Check the magic byte
+ char Magic[8];
+ if (File.Read(Magic,sizeof(Magic)) == false)
+ return false;
+ if (memcmp(Magic,"!<arch>\012",sizeof(Magic)) != 0)
+ return _error->Error(_("Invalid archive signature"));
+ Left -= sizeof(Magic);
+
+ // Read the member list
+ while (Left > 0)
+ {
+ MemberHeader Head;
+ if (File.Read(&Head,sizeof(Head)) == false)
+ return _error->Error(_("Error reading archive member header"));
+ Left -= sizeof(Head);
+
+ // Convert all of the integer members
+ Member *Memb = new Member();
+ if (StrToNum(Head.MTime,Memb->MTime,sizeof(Head.MTime)) == false ||
+ StrToNum(Head.UID,Memb->UID,sizeof(Head.UID)) == false ||
+ StrToNum(Head.GID,Memb->GID,sizeof(Head.GID)) == false ||
+ StrToNum(Head.Mode,Memb->Mode,sizeof(Head.Mode),8) == false ||
+ StrToNum(Head.Size,Memb->Size,sizeof(Head.Size)) == false)
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+
+ if (Left < 0 || Memb->Size > static_cast<unsigned long long>(Left))
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+ // Check for an extra long name string
+ if (memcmp(Head.Name,"#1/",3) == 0)
+ {
+ char S[300];
+ unsigned long Len;
+ if (StrToNum(Head.Name+3,Len,sizeof(Head.Size)-3) == false ||
+ Len >= sizeof(S))
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+
+ if (Len > Memb->Size)
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+
+ if (File.Read(S,Len) == false)
+ {
+ delete Memb;
+ return false;
+ }
+ S[Len] = 0;
+ Memb->Name = S;
+ Memb->Size -= Len;
+ Left -= Len;
+ }
+ else
+ {
+ unsigned int I = sizeof(Head.Name) - 1;
+ for (; Head.Name[I] == ' ' || Head.Name[I] == '/'; I--)
+ {
+ if (I == 0)
+ {
+ delete Memb;
+ return _error->Error(_("Invalid archive member header"));
+ }
+ }
+ Memb->Name = std::string(Head.Name,I+1);
+ }
+
+ // Account for the AR header alignment
+ off_t Skip = Memb->Size % 2;
+
+ // Add it to the list
+ Memb->Next = List;
+ List = Memb;
+ Memb->Start = File.Tell();
+ if (File.Skip(Memb->Size + Skip) == false)
+ return false;
+ if (Left < (off_t)(Memb->Size + Skip))
+ return _error->Error(_("Archive is too short"));
+ Left -= Memb->Size + Skip;
+ }
+ if (Left != 0)
+ return _error->Error(_("Failed to read the archive headers"));
+
+ return true;
+}
+ /*}}}*/
+// ARArchive::FindMember - Find a name in the member list /*{{{*/
+// ---------------------------------------------------------------------
+/* Find a member with the given name */
+const ARArchive::Member *ARArchive::FindMember(const char *Name) const
+{
+ const Member *Res = List;
+ while (Res != 0)
+ {
+ if (Res->Name == Name)
+ return Res;
+ Res = Res->Next;
+ }
+
+ return 0;
+}
+ /*}}}*/
diff --git a/apt-inst/contrib/arfile.h b/apt-inst/contrib/arfile.h
new file mode 100644
index 0000000..8124208
--- /dev/null
+++ b/apt-inst/contrib/arfile.h
@@ -0,0 +1,69 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ AR File - Handle an 'AR' archive
+
+ This is a reader for the usual 4.4 BSD AR format. It allows raw
+ stream access to a single member at a time. Basically all this class
+ provides is header parsing and verification. It is up to the client
+ to correctly make use of the stream start/stop points.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_ARFILE_H
+#define PKGLIB_ARFILE_H
+
+#include <apt-pkg/macros.h>
+#include <string>
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/fileutl.h>
+#endif
+
+class FileFd;
+
+class ARArchive
+{
+ struct MemberHeader;
+ public:
+ struct Member;
+
+ protected:
+
+ // Linked list of members
+ Member *List;
+
+ bool LoadHeaders();
+
+ public:
+
+ // The stream file
+ FileFd &File;
+
+ // Locate a member by name
+ const Member *FindMember(const char *Name) const;
+ inline Member *Members() { return List; }
+
+ ARArchive(FileFd &File);
+ ~ARArchive();
+};
+
+// A member of the archive
+struct ARArchive::Member
+{
+ // Fields from the header
+ std::string Name;
+ unsigned long MTime;
+ unsigned long UID;
+ unsigned long GID;
+ unsigned long Mode;
+ unsigned long long Size;
+
+ // Location of the data.
+ unsigned long long Start;
+ Member *Next;
+
+ Member() : Start(0), Next(0) {};
+};
+
+#endif
diff --git a/apt-inst/contrib/extracttar.cc b/apt-inst/contrib/extracttar.cc
new file mode 100644
index 0000000..cbee4d1
--- /dev/null
+++ b/apt-inst/contrib/extracttar.cc
@@ -0,0 +1,329 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Extract a Tar - Tar Extractor
+
+ Some performance measurements showed that zlib performed quite poorly
+ in comparison to a forked gzip process. This tar extractor makes use
+ of the fact that dup'd file descriptors have the same seek pointer
+ and that gzip will not read past the end of a compressed stream,
+ even if there is more data. We use the dup property to track extraction
+ progress and the gzip feature to just feed gzip a fd in the middle
+ of an AR file.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/extracttar.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+// The on disk header for a tar file.
+struct ExtractTar::TarHeader
+{
+ char Name[100];
+ char Mode[8];
+ char UserID[8];
+ char GroupID[8];
+ char Size[12];
+ char MTime[12];
+ char Checksum[8];
+ char LinkFlag;
+ char LinkName[100];
+ char MagicNumber[8];
+ char UserName[32];
+ char GroupName[32];
+ char Major[8];
+ char Minor[8];
+};
+
+// We need to read long names (names and link targets) into memory, so let's
+// have a limit (shamelessly stolen from libarchive) to avoid people OOMing
+// us with large streams.
+static const unsigned long long APT_LONGNAME_LIMIT = 1048576llu;
+
+// A file size limit that we allow extracting. Currently, that's 128 GB.
+// We also should leave some wiggle room for code adding files to it, and
+// possibly conversion for signed, so this should not be larger than like 2**62.
+static const unsigned long long APT_FILESIZE_LIMIT = 1llu << 37;
+
+// ExtractTar::ExtractTar - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ExtractTar::ExtractTar(FileFd &Fd,unsigned long long Max,string DecompressionProgram)
+ : File(Fd), MaxInSize(Max), DecompressProg(DecompressionProgram)
+{
+ GZPid = -1;
+ Eof = false;
+}
+ /*}}}*/
+// ExtractTar::ExtractTar - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+ExtractTar::~ExtractTar()
+{
+ // Error close
+ Done();
+}
+ /*}}}*/
+// ExtractTar::Done - Reap the gzip sub process /*{{{*/
+bool ExtractTar::Done(bool)
+{
+ return Done();
+}
+bool ExtractTar::Done()
+{
+ return InFd.Close();
+}
+ /*}}}*/
+// ExtractTar::StartGzip - Startup gzip /*{{{*/
+// ---------------------------------------------------------------------
+/* This creates a gzip sub process that has its input as the file itself.
+ If this tar file is embedded into something like an ar file then
+ gzip will efficiently ignore the extra bits. */
+bool ExtractTar::StartGzip()
+{
+ if (DecompressProg.empty())
+ {
+ InFd.OpenDescriptor(File.Fd(), FileFd::ReadOnly, FileFd::None, false);
+ return true;
+ }
+
+ std::vector<APT::Configuration::Compressor> const compressors = APT::Configuration::getCompressors();
+ std::vector<APT::Configuration::Compressor>::const_iterator compressor = compressors.begin();
+ for (; compressor != compressors.end(); ++compressor) {
+ if (compressor->Name == DecompressProg) {
+ return InFd.OpenDescriptor(File.Fd(), FileFd::ReadOnly, *compressor, false);
+ }
+ }
+
+ return _error->Error(_("Cannot find a configured compressor for '%s'"),
+ DecompressProg.c_str());
+
+}
+ /*}}}*/
+// ExtractTar::Go - Perform extraction /*{{{*/
+// ---------------------------------------------------------------------
+/* This reads each 512 byte block from the archive and extracts the header
+ information into the Item structure. Then it resolves the UID/GID and
+ invokes the correct processing function. */
+bool ExtractTar::Go(pkgDirStream &Stream)
+{
+ if (StartGzip() == false)
+ return false;
+
+ // Loop over all blocks
+ string LastLongLink, ItemLink;
+ string LastLongName, ItemName;
+ while (1)
+ {
+ bool BadRecord = false;
+ unsigned char Block[512];
+ if (InFd.Read(Block,sizeof(Block),true) == false)
+ return false;
+
+ if (InFd.Eof() == true)
+ break;
+
+ // Get the checksum
+ TarHeader *Tar = (TarHeader *)Block;
+ unsigned long CheckSum;
+ if (StrToNum(Tar->Checksum,CheckSum,sizeof(Tar->Checksum),8) == false)
+ return _error->Error(_("Corrupted archive"));
+
+ /* Compute the checksum field. The actual checksum is blanked out
+ with spaces so it is not included in the computation */
+ unsigned long NewSum = 0;
+ memset(Tar->Checksum,' ',sizeof(Tar->Checksum));
+ for (int I = 0; I != sizeof(Block); I++)
+ NewSum += Block[I];
+
+ /* Check for a block of nulls - in this case we kill gzip, GNU tar
+ does this.. */
+ if (NewSum == ' '*sizeof(Tar->Checksum))
+ return Done();
+
+ if (NewSum != CheckSum)
+ return _error->Error(_("Tar checksum failed, archive corrupted"));
+
+ // Decode all of the fields
+ pkgDirStream::Item Itm;
+ if (StrToNum(Tar->Mode,Itm.Mode,sizeof(Tar->Mode),8) == false ||
+ (Base256ToNum(Tar->UserID,Itm.UID,8) == false &&
+ StrToNum(Tar->UserID,Itm.UID,sizeof(Tar->UserID),8) == false) ||
+ (Base256ToNum(Tar->GroupID,Itm.GID,8) == false &&
+ StrToNum(Tar->GroupID,Itm.GID,sizeof(Tar->GroupID),8) == false) ||
+ (Base256ToNum(Tar->Size,Itm.Size,12) == false &&
+ StrToNum(Tar->Size,Itm.Size,sizeof(Tar->Size),8) == false) ||
+ (Base256ToNum(Tar->MTime,Itm.MTime,12) == false &&
+ StrToNum(Tar->MTime,Itm.MTime,sizeof(Tar->MTime),8) == false) ||
+ StrToNum(Tar->Major,Itm.Major,sizeof(Tar->Major),8) == false ||
+ StrToNum(Tar->Minor,Itm.Minor,sizeof(Tar->Minor),8) == false)
+ return _error->Error(_("Corrupted archive"));
+
+ // Security check. Prevents overflows below the code when rounding up in skip/copy code,
+ // and provides modest protection against decompression bombs.
+ if (Itm.Size > APT_FILESIZE_LIMIT)
+ return _error->Error("Tar member too large: %llu > %llu bytes", Itm.Size, APT_FILESIZE_LIMIT);
+
+ // Grab the filename and link target: use last long name if one was
+ // set, otherwise use the header value as-is, but remember that it may
+ // fill the entire 100-byte block and needs to be zero-terminated.
+ // See Debian Bug #689582.
+ if (LastLongName.empty() == false)
+ Itm.Name = (char *)LastLongName.c_str();
+ else
+ Itm.Name = (char *)ItemName.assign(Tar->Name, sizeof(Tar->Name)).c_str();
+ if (Itm.Name[0] == '.' && Itm.Name[1] == '/' && Itm.Name[2] != 0)
+ Itm.Name += 2;
+
+ if (LastLongLink.empty() == false)
+ Itm.LinkTarget = (char *)LastLongLink.c_str();
+ else
+ Itm.LinkTarget = (char *)ItemLink.assign(Tar->LinkName, sizeof(Tar->LinkName)).c_str();
+
+ // Convert the type over
+ switch (Tar->LinkFlag)
+ {
+ case NormalFile0:
+ case NormalFile:
+ Itm.Type = pkgDirStream::Item::File;
+ break;
+
+ case HardLink:
+ Itm.Type = pkgDirStream::Item::HardLink;
+ break;
+
+ case SymbolicLink:
+ Itm.Type = pkgDirStream::Item::SymbolicLink;
+ break;
+
+ case CharacterDevice:
+ Itm.Type = pkgDirStream::Item::CharDevice;
+ break;
+
+ case BlockDevice:
+ Itm.Type = pkgDirStream::Item::BlockDevice;
+ break;
+
+ case Directory:
+ Itm.Type = pkgDirStream::Item::Directory;
+ break;
+
+ case FIFO:
+ Itm.Type = pkgDirStream::Item::FIFO;
+ break;
+
+ case GNU_LongLink:
+ {
+ unsigned long long Length = Itm.Size;
+ unsigned char Block[512];
+ if (Length > APT_LONGNAME_LIMIT)
+ return _error->Error("Long name to large: %llu bytes > %llu bytes", Length, APT_LONGNAME_LIMIT);
+ while (Length > 0)
+ {
+ if (InFd.Read(Block,sizeof(Block),true) == false)
+ return false;
+ if (Length <= sizeof(Block))
+ {
+ LastLongLink.append(Block,Block+sizeof(Block));
+ break;
+ }
+ LastLongLink.append(Block,Block+sizeof(Block));
+ Length -= sizeof(Block);
+ }
+ continue;
+ }
+
+ case GNU_LongName:
+ {
+ unsigned long long Length = Itm.Size;
+ unsigned char Block[512];
+ if (Length > APT_LONGNAME_LIMIT)
+ return _error->Error("Long name to large: %llu bytes > %llu bytes", Length, APT_LONGNAME_LIMIT);
+ while (Length > 0)
+ {
+ if (InFd.Read(Block,sizeof(Block),true) == false)
+ return false;
+ if (Length < sizeof(Block))
+ {
+ LastLongName.append(Block,Block+sizeof(Block));
+ break;
+ }
+ LastLongName.append(Block,Block+sizeof(Block));
+ Length -= sizeof(Block);
+ }
+ continue;
+ }
+
+ default:
+ BadRecord = true;
+ _error->Warning(_("Unknown TAR header type %u"), (unsigned)Tar->LinkFlag);
+ break;
+ }
+
+ int Fd = -1;
+ if (BadRecord == false)
+ if (Stream.DoItem(Itm,Fd) == false)
+ return false;
+
+ // Copy the file over the FD
+ unsigned long long Size = Itm.Size;
+ while (Size != 0)
+ {
+ unsigned char Junk[32*1024];
+ unsigned long Read = min(Size, (unsigned long long)sizeof(Junk));
+ if (InFd.Read(Junk,((Read+511)/512)*512) == false)
+ return false;
+
+ if (BadRecord == false)
+ {
+ if (Fd > 0)
+ {
+ if (write(Fd,Junk,Read) != (signed)Read)
+ return Stream.Fail(Itm,Fd);
+ }
+ else
+ {
+ /* An Fd of -2 means to send to a special processing
+ function */
+ if (Fd == -2)
+ if (Stream.Process(Itm,Junk,Read,Itm.Size - Size) == false)
+ return Stream.Fail(Itm,Fd);
+ }
+ }
+
+ Size -= Read;
+ }
+
+ // And finish up
+ if (BadRecord == false)
+ if (Stream.FinishedFile(Itm,Fd) == false)
+ return false;
+
+ LastLongName.erase();
+ LastLongLink.erase();
+ }
+
+ return Done();
+}
+ /*}}}*/
diff --git a/apt-inst/contrib/extracttar.h b/apt-inst/contrib/extracttar.h
new file mode 100644
index 0000000..c0b340e
--- /dev/null
+++ b/apt-inst/contrib/extracttar.h
@@ -0,0 +1,61 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Extract a Tar - Tar Extractor
+
+ The tar extractor takes an ordinary gzip compressed tar stream from
+ the given file and explodes it, passing the individual items to the
+ given Directory Stream for processing.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_EXTRACTTAR_H
+#define PKGLIB_EXTRACTTAR_H
+
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/macros.h>
+
+#include <string>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/dirstream.h>
+#include <algorithm>
+using std::min;
+#endif
+
+class pkgDirStream;
+
+class ExtractTar
+{
+ protected:
+
+ struct TarHeader;
+
+ // The varios types items can be
+ enum ItemType {NormalFile0 = '\0',NormalFile = '0',HardLink = '1',
+ SymbolicLink = '2',CharacterDevice = '3',
+ BlockDevice = '4',Directory = '5',FIFO = '6',
+ GNU_LongLink = 'K',GNU_LongName = 'L'};
+
+ FileFd &File;
+ unsigned long long MaxInSize;
+ int GZPid;
+ FileFd InFd;
+ bool Eof;
+ std::string DecompressProg;
+
+ // Fork and reap gzip
+ bool StartGzip();
+ bool Done();
+ APT_DEPRECATED_MSG("Parameter Force is ignored, use Done() instead.") bool Done(bool Force);
+
+ public:
+
+ bool Go(pkgDirStream &Stream);
+
+ ExtractTar(FileFd &Fd,unsigned long long Max,std::string DecompressionProgram);
+ virtual ~ExtractTar();
+};
+
+#endif
diff --git a/apt-inst/deb/debfile.cc b/apt-inst/deb/debfile.cc
new file mode 100644
index 0000000..bef0cd0
--- /dev/null
+++ b/apt-inst/deb/debfile.cc
@@ -0,0 +1,265 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Debian Archive File (.deb)
+
+ .DEB archives are AR files containing two tars and an empty marker
+ member called 'debian-binary'. The two tars contain the meta data and
+ the actual archive contents. Thus this class is a very simple wrapper
+ around ar/tar to simply extract the right tar files.
+
+ It also uses the deb package list parser to parse the control file
+ into the cache.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/arfile.h>
+#include <apt-pkg/debfile.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/extracttar.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/tagfile.h>
+
+#include <string>
+#include <vector>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+// DebFile::debDebFile - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* Open the AR file and check for consistency */
+debDebFile::debDebFile(FileFd &File) : File(File), AR(File)
+{
+ if (_error->PendingError() == true)
+ return;
+
+ if (!CheckMember("debian-binary")) {
+ _error->Error(_("This is not a valid DEB archive, missing '%s' member"), "debian-binary");
+ return;
+ }
+
+ if (!CheckMember("control.tar") &&
+ !CheckMember("control.tar.gz") &&
+ !CheckMember("control.tar.xz") &&
+ !CheckMember("control.tar.zst"))
+ {
+ _error->Error(_("This is not a valid DEB archive, missing '%s' member"), "control.tar");
+ return;
+ }
+
+ if (!CheckMember("data.tar") &&
+ !CheckMember("data.tar.gz") &&
+ !CheckMember("data.tar.bz2") &&
+ !CheckMember("data.tar.lzma") &&
+ !CheckMember("data.tar.xz") &&
+ !CheckMember("data.tar.zst"))
+ {
+ _error->Error(_("This is not a valid DEB archive, missing '%s' member"), "data.tar");
+ return;
+ }
+}
+ /*}}}*/
+// DebFile::CheckMember - Check if a named member is in the archive /*{{{*/
+// ---------------------------------------------------------------------
+/* This is used to check for a correct deb and to give nicer error messages
+ for people playing around. */
+bool debDebFile::CheckMember(const char *Name)
+{
+ if (AR.FindMember(Name) == 0)
+ return false;
+ return true;
+}
+ /*}}}*/
+// DebFile::GotoMember - Jump to a Member /*{{{*/
+// ---------------------------------------------------------------------
+/* Jump in the file to the start of a named member and return the information
+ about that member. The caller can then read from the file up to the
+ returned size. Note, since this relies on the file position this is
+ a destructive operation, it also changes the last returned Member
+ structure - so don't nest them! */
+const ARArchive::Member *debDebFile::GotoMember(const char *Name)
+{
+ // Get the archive member and positition the file
+ const ARArchive::Member *Member = AR.FindMember(Name);
+ if (Member == 0)
+ {
+ return 0;
+ }
+ if (File.Seek(Member->Start) == false)
+ return 0;
+
+ return Member;
+}
+ /*}}}*/
+// DebFile::ExtractTarMember - Extract the contents of a tar member /*{{{*/
+// ---------------------------------------------------------------------
+/* Simple wrapper around tar.. */
+bool debDebFile::ExtractTarMember(pkgDirStream &Stream,const char *Name)
+{
+ // Get the archive member
+ const ARArchive::Member *Member = NULL;
+ std::string Compressor;
+
+ std::vector<APT::Configuration::Compressor> compressor = APT::Configuration::getCompressors();
+ for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
+ c != compressor.end(); ++c)
+ {
+ Member = AR.FindMember(std::string(Name).append(c->Extension).c_str());
+ if (Member == NULL)
+ continue;
+ Compressor = c->Name;
+ break;
+ }
+
+ if (Member == NULL)
+ Member = AR.FindMember(std::string(Name).c_str());
+
+ if (Member == NULL)
+ {
+ std::string ext = std::string(Name) + ".{";
+ for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressor.begin();
+ c != compressor.end(); ++c) {
+ if (!c->Extension.empty())
+ ext.append(c->Extension.substr(1));
+ }
+ ext.append("}");
+ return _error->Error(_("Internal error, could not locate member %s"), ext.c_str());
+ }
+
+ if (File.Seek(Member->Start) == false)
+ return false;
+
+ // Prepare Tar
+ ExtractTar Tar(File,Member->Size,Compressor);
+ if (_error->PendingError() == true)
+ return false;
+ return Tar.Go(Stream);
+}
+ /*}}}*/
+// DebFile::ExtractArchive - Extract the archive data itself /*{{{*/
+// ---------------------------------------------------------------------
+/* Simple wrapper around DebFile::ExtractTarMember. */
+bool debDebFile::ExtractArchive(pkgDirStream &Stream)
+{
+ return ExtractTarMember(Stream, "data.tar");
+}
+ /*}}}*/
+
+// DebFile::ControlExtract::DoItem - Control Tar Extraction /*{{{*/
+// ---------------------------------------------------------------------
+/* This directory stream handler for the control tar handles extracting
+ it into the temporary meta directory. It only extracts files, it does
+ not create directories, links or anything else. */
+bool debDebFile::ControlExtract::DoItem(Item &Itm,int &Fd)
+{
+ if (Itm.Type != Item::File)
+ return true;
+
+ /* Cleanse the file name, prevent people from trying to unpack into
+ absolute paths, .., etc */
+ for (char *I = Itm.Name; *I != 0; I++)
+ if (*I == '/')
+ *I = '_';
+
+ /* Force the ownership to be root and ensure correct permissions,
+ go-w, the rest are left untouched */
+ Itm.UID = 0;
+ Itm.GID = 0;
+ Itm.Mode &= ~(S_IWGRP | S_IWOTH);
+
+ return pkgDirStream::DoItem(Itm,Fd);
+}
+ /*}}}*/
+
+// MemControlExtract::DoItem - Check if it is the control file /*{{{*/
+// ---------------------------------------------------------------------
+/* This sets up to extract the control block member file into a memory
+ block of just the right size. All other files go into the bit bucket. */
+
+// Upper size limit for control files. Two reasons for having a limit here:
+//
+// 1. We read those files into memory and want to avoid being killed by OOM
+//
+// 2. We allocate (Itm.Size+2)-large arrays, so this can overflow if Itm.Size
+// becomes 2**64-2 or larger. This is obviously
+//
+// 64 MiB seems like a terribly large size that everyone should be happy with.
+static const unsigned long long DEB_CONTROL_SIZE_LIMIT = 64 * 1024 * 1024;
+bool debDebFile::MemControlExtract::DoItem(Item &Itm,int &Fd)
+{
+ // At the control file, allocate buffer memory.
+ if (Member == Itm.Name)
+ {
+ if (Itm.Size > DEB_CONTROL_SIZE_LIMIT)
+ return _error->Error("Control file too large: %llu > %llu bytes", Itm.Size, DEB_CONTROL_SIZE_LIMIT);
+ delete [] Control;
+ Control = new char[Itm.Size+2];
+ IsControl = true;
+ Fd = -2; // Signal to pass to Process
+ Length = Itm.Size;
+ }
+ else
+ IsControl = false;
+
+ return true;
+}
+ /*}}}*/
+// MemControlExtract::Process - Process extracting the control file /*{{{*/
+// ---------------------------------------------------------------------
+/* Just memcopy the block from the tar extractor and put it in the right
+ place in the pre-allocated memory block. */
+bool debDebFile::MemControlExtract::Process(Item &/*Itm*/,const unsigned char *Data,
+ unsigned long long Size,unsigned long long Pos)
+{
+ memcpy(Control + Pos, Data,Size);
+ return true;
+}
+ /*}}}*/
+// MemControlExtract::Read - Read the control information from the deb /*{{{*/
+// ---------------------------------------------------------------------
+/* This uses the internal tar extractor to fetch the control file, and then
+ it parses it into a tag section parser. */
+bool debDebFile::MemControlExtract::Read(debDebFile &Deb)
+{
+ if (Deb.ExtractTarMember(*this, "control.tar") == false)
+ return false;
+
+ if (Control == 0)
+ return true;
+
+ Control[Length] = '\n';
+ Control[Length+1] = '\n';
+ if (Section.Scan(Control,Length+2) == false)
+ return _error->Error(_("Unparsable control file"));
+ return true;
+}
+ /*}}}*/
+// MemControlExtract::TakeControl - Parse a memory block /*{{{*/
+// ---------------------------------------------------------------------
+/* The given memory block is loaded into the parser and parsed as a control
+ record. */
+bool debDebFile::MemControlExtract::TakeControl(const void *Data,unsigned long long Size)
+{
+ if (Size > DEB_CONTROL_SIZE_LIMIT)
+ return _error->Error("Control file too large: %llu > %llu bytes", Size, DEB_CONTROL_SIZE_LIMIT);
+
+ delete [] Control;
+ Control = new char[Size+2];
+ Length = Size;
+ memcpy(Control,Data,Size);
+
+ Control[Length] = '\n';
+ Control[Length+1] = '\n';
+ return Section.Scan(Control,Length+2);
+}
+ /*}}}*/
+
diff --git a/apt-inst/deb/debfile.h b/apt-inst/deb/debfile.h
new file mode 100644
index 0000000..23a76bf
--- /dev/null
+++ b/apt-inst/deb/debfile.h
@@ -0,0 +1,95 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Debian Archive File (.deb)
+
+ This Class handles all the operations performed directly on .deb
+ files. It makes use of the AR and TAR classes to give the necessary
+ external interface.
+
+ There are only two things that can be done with a raw package,
+ extract it's control information and extract the contents itself.
+
+ This should probably subclass an as-yet unwritten super class to
+ produce a generic archive mechanism.
+
+ The memory control file extractor is useful to extract a single file
+ into memory from the control.tar.gz
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_DEBFILE_H
+#define PKGLIB_DEBFILE_H
+
+#include <apt-pkg/arfile.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/macros.h>
+#include <apt-pkg/tagfile.h>
+
+#include <string>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/md5.h>
+#endif
+#ifndef APT_10_CLEANER_HEADERS
+#include <apt-pkg/pkgcache.h>
+#endif
+
+class FileFd;
+
+class debDebFile
+{
+ protected:
+
+ FileFd &File;
+ ARArchive AR;
+
+ bool CheckMember(const char *Name);
+
+ public:
+ class ControlExtract;
+ class MemControlExtract;
+
+ bool ExtractTarMember(pkgDirStream &Stream, const char *Name);
+ bool ExtractArchive(pkgDirStream &Stream);
+ const ARArchive::Member *GotoMember(const char *Name);
+ inline FileFd &GetFile() {return File;};
+
+ debDebFile(FileFd &File);
+};
+
+class debDebFile::ControlExtract : public pkgDirStream
+{
+ public:
+
+ virtual bool DoItem(Item &Itm,int &Fd) APT_OVERRIDE;
+};
+
+class debDebFile::MemControlExtract : public pkgDirStream
+{
+ bool IsControl;
+
+ public:
+
+ char *Control;
+ pkgTagSection Section;
+ unsigned long Length;
+ std::string Member;
+
+ // Members from DirStream
+ virtual bool DoItem(Item &Itm,int &Fd) APT_OVERRIDE;
+ virtual bool Process(Item &Itm,const unsigned char *Data,
+ unsigned long long Size,unsigned long long Pos) APT_OVERRIDE;
+
+ // Helpers
+ bool Read(debDebFile &Deb);
+ bool TakeControl(const void *Data,unsigned long long Size);
+
+ MemControlExtract() : IsControl(false), Control(0), Length(0), Member("control") {};
+ MemControlExtract(std::string Member) : IsControl(false), Control(0), Length(0), Member(Member) {};
+ ~MemControlExtract() {delete [] Control;};
+};
+ /*}}}*/
+
+#endif
diff --git a/apt-inst/dirstream.cc b/apt-inst/dirstream.cc
new file mode 100644
index 0000000..d6cf0ab
--- /dev/null
+++ b/apt-inst/dirstream.cc
@@ -0,0 +1,118 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Directory Stream
+
+ This class provides a simple basic extractor that can be used for
+ a number of purposes.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <apti18n.h>
+ /*}}}*/
+
+// DirStream::DoItem - Process an item /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a very simple extractor, it does not deal with things like
+ overwriting directories with files and so on. */
+bool pkgDirStream::DoItem(Item &Itm,int &Fd)
+{
+ switch (Itm.Type)
+ {
+ case Item::File:
+ {
+ /* Open the output file, NDELAY is used to prevent this from
+ blowing up on device special files.. */
+ int iFd = open(Itm.Name,O_NDELAY|O_WRONLY|O_CREAT|O_TRUNC|O_APPEND,
+ Itm.Mode);
+ if (iFd < 0)
+ return _error->Errno("open",_("Failed to write file %s"),
+ Itm.Name);
+
+ // fchmod deals with umask and fchown sets the ownership
+ if (fchmod(iFd,Itm.Mode) != 0)
+ {
+ close(iFd);
+ return _error->Errno("fchmod",_("Failed to write file %s"), Itm.Name);
+ }
+ if (fchown(iFd,Itm.UID,Itm.GID) != 0 && errno != EPERM)
+ {
+ close(iFd);
+ return _error->Errno("fchown",_("Failed to write file %s"), Itm.Name);
+ }
+ Fd = iFd;
+ return true;
+ }
+
+ case Item::HardLink:
+ case Item::SymbolicLink:
+ case Item::CharDevice:
+ case Item::BlockDevice:
+ case Item::Directory:
+ {
+ struct stat Buf;
+ // check if the dir is already there, if so return true
+ if (stat(Itm.Name,&Buf) == 0)
+ {
+ if(S_ISDIR(Buf.st_mode))
+ return true;
+ // something else is there already, return false
+ return false;
+ }
+ // nothing here, create the dir
+ if(mkdir(Itm.Name,Itm.Mode) < 0)
+ return false;
+ return true;
+ }
+ case Item::FIFO:
+ break;
+ }
+
+ return true;
+}
+ /*}}}*/
+// DirStream::FinishedFile - Finished processing a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgDirStream::FinishedFile(Item &Itm,int Fd)
+{
+ if (Fd < 0)
+ return true;
+
+ /* Set the modification times. The only way it can fail is if someone
+ has futzed with our file, which is intolerable :> */
+ struct timeval times[2];
+ times[0].tv_sec = times[1].tv_sec = Itm.MTime;
+ times[0].tv_usec = times[1].tv_usec = 0;
+ if (utimes(Itm.Name, times) != 0)
+ _error->Errno("utimes", "Failed to set modification time for %s",Itm.Name);
+
+ if (close(Fd) != 0)
+ return _error->Errno("close",_("Failed to close file %s"),Itm.Name);
+ return true;
+}
+ /*}}}*/
+// DirStream::Fail - Failed processing a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgDirStream::Fail(Item &/*Itm*/, int Fd)
+{
+ if (Fd < 0)
+ return true;
+
+ close(Fd);
+ return false;
+}
+ /*}}}*/
diff --git a/apt-inst/dirstream.h b/apt-inst/dirstream.h
new file mode 100644
index 0000000..0f0e348
--- /dev/null
+++ b/apt-inst/dirstream.h
@@ -0,0 +1,57 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Directory Stream
+
+ When unpacking the contents of the archive are passed into a directory
+ stream class for analysis and processing. The class controls all aspects
+ of actually writing the directory stream from disk. The low level
+ archive handlers are only responsible for decoding the archive format
+ and sending events (via method calls) to the specified directory
+ stream.
+
+ When unpacking a real file the archive handler is passed back a file
+ handle to write the data to, this is to support strange
+ archives+unpacking methods. If that fd is -1 then the file data is
+ simply ignored.
+
+ The provided defaults do the 'Right Thing' for a normal unpacking
+ process (ie 'tar')
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_DIRSTREAM_H
+#define PKGLIB_DIRSTREAM_H
+
+#include <apt-pkg/macros.h>
+
+class pkgDirStream
+{
+ public:
+
+ // All possible information about a component
+ struct Item
+ {
+ enum Type_t {File, HardLink, SymbolicLink, CharDevice, BlockDevice,
+ Directory, FIFO} Type;
+ char *Name;
+ char *LinkTarget;
+ unsigned long Mode;
+ unsigned long UID;
+ unsigned long GID;
+ unsigned long long Size;
+ unsigned long MTime;
+ unsigned long Major;
+ unsigned long Minor;
+ };
+
+ virtual bool DoItem(Item &Itm,int &Fd);
+ virtual bool Fail(Item &Itm,int Fd);
+ virtual bool FinishedFile(Item &Itm,int Fd);
+ virtual bool Process(Item &/*Itm*/,const unsigned char * /*Data*/,
+ unsigned long long /*Size*/,unsigned long long /*Pos*/) {return true;};
+ virtual ~pkgDirStream() {};
+};
+
+#endif
diff --git a/apt-inst/dpkg-diffs.txt b/apt-inst/dpkg-diffs.txt
new file mode 100644
index 0000000..d161055
--- /dev/null
+++ b/apt-inst/dpkg-diffs.txt
@@ -0,0 +1,5 @@
+- Replacing directories with files
+ dpkg permits this with the weak condition that the directory is owned only
+ by the package. APT requires that the directory have no files that are not
+ owned by the package. Replaces are specifically not checked to prevent
+ file list corruption.
diff --git a/apt-inst/extract.cc b/apt-inst/extract.cc
new file mode 100644
index 0000000..35fa015
--- /dev/null
+++ b/apt-inst/extract.cc
@@ -0,0 +1,514 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Archive Extraction Directory Stream
+
+ Extraction for each file is a bit of an involved process. Each object
+ undergoes an atomic backup, overwrite, erase sequence. First the
+ object is unpacked to '.dpkg.new' then the original is hardlinked to
+ '.dpkg.tmp' and finally the new object is renamed to overwrite the old
+ one. From an external perspective the file never ceased to exist.
+ After the archive has been successfully unpacked the .dpkg.tmp files
+ are erased. A failure causes all the .dpkg.tmp files to be restored.
+
+ Decisions about unpacking go like this:
+ - Store the original filename in the file listing
+ - Resolve any diversions that would effect this file, all checks
+ below apply to the diverted name, not the real one.
+ - Resolve any symlinked configuration files.
+ - If the existing file does not exist then .dpkg-tmp is checked for.
+ [Note, this is reduced to only check if a file was expected to be
+ there]
+ - If the existing link/file is not a directory then it is replaced
+ regardless
+ - If the existing link/directory is being replaced by a directory then
+ absolutely nothing happens.
+ - If the existing link/directory is being replaced by a link then
+ absolutely nothing happens.
+ - If the existing link/directory is being replaced by a non-directory
+ then this will abort if the package is not the sole owner of the
+ directory. [Note, this is changed to not happen if the directory
+ non-empty - that is, it only includes files that are part of this
+ package - prevents removing user files accidentally.]
+ - If the non-directory exists in the listing database and it
+ does not belong to the current package then an overwrite condition
+ is invoked.
+
+ As we unpack we record the file list differences in the FL cache. If
+ we need to unroll the FL cache knows which files have been unpacked
+ and can undo. When we need to erase then it knows which files have not
+ been unpacked.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/debversion.h>
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/extract.h>
+#include <apt-pkg/filelist.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/mmap.h>
+#include <apt-pkg/pkgcache.h>
+
+#include <iostream>
+#include <string>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <apti18n.h>
+ /*}}}*/
+using namespace std;
+
+static const char *TempExt = "dpkg-tmp";
+//static const char *NewExt = "dpkg-new";
+
+// Extract::pkgExtract - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+pkgExtract::pkgExtract(pkgFLCache &FLCache,pkgCache::VerIterator Ver) :
+ FLCache(FLCache), Ver(Ver)
+{
+ FLPkg = FLCache.GetPkg(Ver.ParentPkg().Name(),true);
+ if (FLPkg.end() == true)
+ return;
+ Debug = true;
+}
+ /*}}}*/
+// Extract::DoItem - Handle a single item from the stream /*{{{*/
+// ---------------------------------------------------------------------
+/* This performs the setup for the extraction.. */
+bool pkgExtract::DoItem(Item &Itm, int &/*Fd*/)
+{
+ /* Strip any leading/trailing /s from the filename, then copy it to the
+ temp buffer and re-apply the leading / We use a class variable
+ to store the new filename for use by the three extraction funcs */
+ char *End = FileName+1;
+ const char *I = Itm.Name;
+ for (; *I != 0 && *I == '/'; I++);
+ *FileName = '/';
+ for (; *I != 0 && End < FileName + sizeof(FileName); I++, End++)
+ *End = *I;
+ if (End + 20 >= FileName + sizeof(FileName))
+ return _error->Error(_("The path %s is too long"),Itm.Name);
+ for (; End > FileName && End[-1] == '/'; End--);
+ *End = 0;
+ Itm.Name = FileName;
+
+ /* Lookup the file. Nde is the file [group] we are going to write to and
+ RealNde is the actual node we are manipulating. Due to diversions
+ they may be entirely different. */
+ pkgFLCache::NodeIterator Nde = FLCache.GetNode(Itm.Name,End,0,false,false);
+ pkgFLCache::NodeIterator RealNde = Nde;
+
+ // See if the file is already in the file listing
+ unsigned long FileGroup = RealNde->File;
+ for (; RealNde.end() == false && FileGroup == RealNde->File; RealNde++)
+ if (RealNde.RealPackage() == FLPkg)
+ break;
+
+ // Nope, create an entry
+ if (RealNde.end() == true)
+ {
+ RealNde = FLCache.GetNode(Itm.Name,End,FLPkg.Offset(),true,false);
+ if (RealNde.end() == true)
+ return false;
+ RealNde->Flags |= pkgFLCache::Node::NewFile;
+ }
+
+ /* Check if this entry already was unpacked. The only time this should
+ ever happen is if someone has hacked tar to support capabilities, in
+ which case this needs to be modified anyhow.. */
+ if ((RealNde->Flags & pkgFLCache::Node::Unpacked) ==
+ pkgFLCache::Node::Unpacked)
+ return _error->Error(_("Unpacking %s more than once"),Itm.Name);
+
+ if (Nde.end() == true)
+ Nde = RealNde;
+
+ /* Consider a diverted file - We are not permitted to divert directories,
+ but everything else is fair game (including conf files!) */
+ if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
+ {
+ if (Itm.Type == Item::Directory)
+ return _error->Error(_("The directory %s is diverted"),Itm.Name);
+
+ /* A package overwriting a diversion target is just the same as
+ overwriting a normally owned file and is checked for below in
+ the overwrites mechanism */
+
+ /* If this package is trying to overwrite the target of a diversion,
+ that is never, ever permitted */
+ pkgFLCache::DiverIterator Div = Nde.Diversion();
+ if (Div.DivertTo() == Nde)
+ return _error->Error(_("The package is trying to write to the "
+ "diversion target %s/%s"),Nde.DirN(),Nde.File());
+
+ // See if it is us and we are following it in the right direction
+ if (Div->OwnerPkg != FLPkg.Offset() && Div.DivertFrom() == Nde)
+ {
+ Nde = Div.DivertTo();
+ End = FileName + snprintf(FileName,sizeof(FileName)-20,"%s/%s",
+ Nde.DirN(),Nde.File());
+ if (End <= FileName)
+ return _error->Error(_("The diversion path is too long"));
+ }
+ }
+
+ // Deal with symlinks and conf files
+ if ((RealNde->Flags & pkgFLCache::Node::NewConfFile) ==
+ pkgFLCache::Node::NewConfFile)
+ {
+ string Res = flNoLink(Itm.Name);
+ if (Res.length() > sizeof(FileName))
+ return _error->Error(_("The path %s is too long"),Res.c_str());
+ if (Debug == true)
+ clog << "Followed conf file from " << FileName << " to " << Res << endl;
+ Itm.Name = strcpy(FileName,Res.c_str());
+ }
+
+ /* Get information about the existing file, and attempt to restore
+ a backup if it does not exist */
+ struct stat LExisting;
+ bool EValid = false;
+ if (lstat(Itm.Name,&LExisting) != 0)
+ {
+ // This is bad news.
+ if (errno != ENOENT)
+ return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
+
+ // See if we can recover the backup file
+ if (Nde.end() == false)
+ {
+ char Temp[sizeof(FileName)];
+ snprintf(Temp,sizeof(Temp),"%s.%s",Itm.Name,TempExt);
+ if (rename(Temp,Itm.Name) != 0 && errno != ENOENT)
+ return _error->Errno("rename",_("Failed to rename %s to %s"),
+ Temp,Itm.Name);
+ if (stat(Itm.Name,&LExisting) != 0)
+ {
+ if (errno != ENOENT)
+ return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
+ }
+ else
+ EValid = true;
+ }
+ }
+ else
+ EValid = true;
+
+ /* If the file is a link we need to stat its destination, get the
+ existing file modes */
+ struct stat Existing = LExisting;
+ if (EValid == true && S_ISLNK(Existing.st_mode))
+ {
+ if (stat(Itm.Name,&Existing) != 0)
+ {
+ if (errno != ENOENT)
+ return _error->Errno("stat",_("Failed to stat %s"),Itm.Name);
+ Existing = LExisting;
+ }
+ }
+
+ // We pretend a non-existing file looks like it is a normal file
+ if (EValid == false)
+ Existing.st_mode = S_IFREG;
+
+ /* Okay, at this point 'Existing' is the stat information for the
+ real non-link file */
+
+ /* The only way this can be a no-op is if a directory is being
+ replaced by a directory or by a link */
+ if (S_ISDIR(Existing.st_mode) != 0 &&
+ (Itm.Type == Item::Directory || Itm.Type == Item::SymbolicLink))
+ return true;
+
+ /* Non-Directory being replaced by non-directory. We check for over
+ writes here. */
+ if (Nde.end() == false)
+ {
+ if (HandleOverwrites(Nde) == false)
+ return false;
+ }
+
+ /* Directory being replaced by a non-directory - this needs to see if
+ the package is the owner and then see if the directory would be
+ empty after the package is removed [ie no user files will be
+ erased] */
+ if (S_ISDIR(Existing.st_mode) != 0)
+ {
+ if (CheckDirReplace(Itm.Name) == false)
+ return _error->Error(_("The directory %s is being replaced by a non-directory"),Itm.Name);
+ }
+
+ if (Debug == true)
+ clog << "Extract " << string(Itm.Name,End) << endl;
+/* if (Count != 0)
+ return _error->Error(_("Done"));*/
+
+ return true;
+}
+ /*}}}*/
+// Extract::Finished - Sequence finished, erase the temp files /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+APT_PURE bool pkgExtract::Finished()
+{
+ return true;
+}
+ /*}}}*/
+// Extract::Aborted - Sequence aborted, undo all our unpacking /*{{{*/
+// ---------------------------------------------------------------------
+/* This undoes everything that was done by all calls to the DoItem method
+ and restores the File Listing cache to its original form. It bases its
+ actions on the flags value for each node in the cache. */
+bool pkgExtract::Aborted()
+{
+ if (Debug == true)
+ clog << "Aborted, backing out" << endl;
+
+ pkgFLCache::NodeIterator Files = FLPkg.Files();
+ map_ptrloc *Last = &FLPkg->Files;
+
+ /* Loop over all files, restore those that have been unpacked from their
+ dpkg-tmp entries */
+ while (Files.end() == false)
+ {
+ // Locate the hash bucket for the node and locate its group head
+ pkgFLCache::NodeIterator Nde(FLCache,FLCache.HashNode(Files));
+ for (; Nde.end() == false && Files->File != Nde->File; Nde++);
+ if (Nde.end() == true)
+ return _error->Error(_("Failed to locate node in its hash bucket"));
+
+ if (snprintf(FileName,sizeof(FileName)-20,"%s/%s",
+ Nde.DirN(),Nde.File()) <= 0)
+ return _error->Error(_("The path is too long"));
+
+ // Deal with diversions
+ if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
+ {
+ pkgFLCache::DiverIterator Div = Nde.Diversion();
+
+ // See if it is us and we are following it in the right direction
+ if (Div->OwnerPkg != FLPkg.Offset() && Div.DivertFrom() == Nde)
+ {
+ Nde = Div.DivertTo();
+ if (snprintf(FileName,sizeof(FileName)-20,"%s/%s",
+ Nde.DirN(),Nde.File()) <= 0)
+ return _error->Error(_("The diversion path is too long"));
+ }
+ }
+
+ // Deal with overwrites+replaces
+ for (; Nde.end() == false && Files->File == Nde->File; Nde++)
+ {
+ if ((Nde->Flags & pkgFLCache::Node::Replaced) ==
+ pkgFLCache::Node::Replaced)
+ {
+ if (Debug == true)
+ clog << "De-replaced " << FileName << " from " << Nde.RealPackage()->Name << endl;
+ Nde->Flags &= ~pkgFLCache::Node::Replaced;
+ }
+ }
+
+ // Undo the change in the filesystem
+ if (Debug == true)
+ clog << "Backing out " << FileName;
+
+ // Remove a new node
+ if ((Files->Flags & pkgFLCache::Node::NewFile) ==
+ pkgFLCache::Node::NewFile)
+ {
+ if (Debug == true)
+ clog << " [new node]" << endl;
+ pkgFLCache::Node *Tmp = Files;
+ Files++;
+ *Last = Tmp->NextPkg;
+ Tmp->NextPkg = 0;
+
+ FLCache.DropNode(Tmp - FLCache.NodeP);
+ }
+ else
+ {
+ if (Debug == true)
+ clog << endl;
+
+ Last = &Files->NextPkg;
+ Files++;
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+// Extract::Fail - Extraction of a file Failed /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgExtract::Fail(Item &Itm,int Fd)
+{
+ return pkgDirStream::Fail(Itm,Fd);
+}
+ /*}}}*/
+// Extract::FinishedFile - Finished a file /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgExtract::FinishedFile(Item &Itm,int Fd)
+{
+ return pkgDirStream::FinishedFile(Itm,Fd);
+}
+ /*}}}*/
+// Extract::HandleOverwrites - See if a replaces covers this overwrite /*{{{*/
+// ---------------------------------------------------------------------
+/* Check if the file is in a package that is being replaced by this
+ package or if the file is being overwritten. Note that if the file
+ is really a directory but it has been erased from the filesystem
+ this will fail with an overwrite message. This is a limitation of the
+ dpkg file information format.
+
+ XX If a new package installs and another package replaces files in this
+ package what should we do? */
+bool pkgExtract::HandleOverwrites(pkgFLCache::NodeIterator Nde,
+ bool DiverCheck)
+{
+ pkgFLCache::NodeIterator TmpNde = Nde;
+ unsigned long DiverOwner = 0;
+ unsigned long FileGroup = Nde->File;
+ for (; Nde.end() == false && FileGroup == Nde->File; Nde++)
+ {
+ if ((Nde->Flags & pkgFLCache::Node::Diversion) != 0)
+ {
+ /* Store the diversion owner if this is the forward direction
+ of the diversion */
+ if (DiverCheck == true)
+ DiverOwner = Nde.Diversion()->OwnerPkg;
+ continue;
+ }
+
+ pkgFLCache::PkgIterator FPkg(FLCache,Nde.RealPackage());
+ if (FPkg.end() == true || FPkg == FLPkg)
+ continue;
+
+ /* This tests trips when we are checking a diversion to see
+ if something has already been diverted by this diversion */
+ if (FPkg.Offset() == DiverOwner)
+ continue;
+
+ // Now see if this package matches one in a replace depends
+ pkgCache::DepIterator Dep = Ver.DependsList();
+ bool Ok = false;
+ for (; Dep.end() == false; ++Dep)
+ {
+ if (Dep->Type != pkgCache::Dep::Replaces)
+ continue;
+
+ // Does the replaces apply to this package?
+ if (strcmp(Dep.TargetPkg().Name(),FPkg.Name()) != 0)
+ continue;
+
+ /* Check the version for match. I do not think CurrentVer can be
+ 0 if we are here.. */
+ pkgCache::PkgIterator Pkg = Dep.TargetPkg();
+ if (Pkg->CurrentVer == 0)
+ {
+ _error->Warning(_("Overwrite package match with no version for %s"),Pkg.Name());
+ continue;
+ }
+
+ // Replaces is met
+ if (debVS.CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp,Dep.TargetVer()) == true)
+ {
+ if (Debug == true)
+ clog << "Replaced file " << Nde.DirN() << '/' << Nde.File() << " from " << Pkg.Name() << endl;
+ Nde->Flags |= pkgFLCache::Node::Replaced;
+ Ok = true;
+ break;
+ }
+ }
+
+ // Negative Hit
+ if (Ok == false)
+ return _error->Error(_("File %s/%s overwrites the one in the package %s"),
+ Nde.DirN(),Nde.File(),FPkg.Name());
+ }
+
+ /* If this is a diversion we might have to recurse to process
+ the other side of it */
+ if ((TmpNde->Flags & pkgFLCache::Node::Diversion) != 0)
+ {
+ pkgFLCache::DiverIterator Div = TmpNde.Diversion();
+ if (Div.DivertTo() == TmpNde)
+ return HandleOverwrites(Div.DivertFrom(),true);
+ }
+
+ return true;
+}
+ /*}}}*/
+// Extract::CheckDirReplace - See if this directory can be erased /*{{{*/
+// ---------------------------------------------------------------------
+/* If this directory is owned by a single package and that package is
+ replacing it with something non-directoryish then dpkg allows this.
+ We increase the requirement to be that the directory is non-empty after
+ the package is removed */
+bool pkgExtract::CheckDirReplace(string Dir,unsigned int Depth)
+{
+ // Looping?
+ if (Depth > 40)
+ return false;
+
+ if (Dir[Dir.size() - 1] != '/')
+ Dir += '/';
+
+ DIR *D = opendir(Dir.c_str());
+ if (D == 0)
+ return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
+
+ string File;
+ for (struct dirent *Dent = readdir(D); Dent != 0; Dent = readdir(D))
+ {
+ // Skip some files
+ if (strcmp(Dent->d_name,".") == 0 ||
+ strcmp(Dent->d_name,"..") == 0)
+ continue;
+
+ // Look up the node
+ File = Dir + Dent->d_name;
+ pkgFLCache::NodeIterator Nde = FLCache.GetNode(File.c_str(),
+ File.c_str() + File.length(),0,false,false);
+
+ // The file is not owned by this package
+ if (Nde.end() != false || Nde.RealPackage() != FLPkg)
+ {
+ closedir(D);
+ return false;
+ }
+
+ // See if it is a directory
+ struct stat St;
+ if (lstat(File.c_str(),&St) != 0)
+ {
+ closedir(D);
+ return _error->Errno("lstat",_("Unable to stat %s"),File.c_str());
+ }
+
+ // Recurse down directories
+ if (S_ISDIR(St.st_mode) != 0)
+ {
+ if (CheckDirReplace(File,Depth + 1) == false)
+ {
+ closedir(D);
+ return false;
+ }
+ }
+ }
+
+ // No conflicts
+ closedir(D);
+ return true;
+}
+ /*}}}*/
diff --git a/apt-inst/extract.h b/apt-inst/extract.h
new file mode 100644
index 0000000..4b4c8d7
--- /dev/null
+++ b/apt-inst/extract.h
@@ -0,0 +1,49 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Archive Extraction Directory Stream
+
+ This Directory Stream implements extraction of an archive into the
+ filesystem. It makes the choices on what files should be unpacked and
+ replaces as well as guiding the actual unpacking.
+
+ When the unpacking sequence is completed one of the two functions,
+ Finished or Aborted must be called.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_EXTRACT_H
+#define PKGLIB_EXTRACT_H
+
+#include <apt-pkg/dirstream.h>
+#include <apt-pkg/filelist.h>
+#include <apt-pkg/pkgcache.h>
+
+#include <string>
+
+class pkgExtract : public pkgDirStream
+{
+ pkgFLCache &FLCache;
+ pkgCache::VerIterator Ver;
+ pkgFLCache::PkgIterator FLPkg;
+ char FileName[1024];
+ bool Debug;
+
+ bool HandleOverwrites(pkgFLCache::NodeIterator Nde,
+ bool DiverCheck = false);
+ bool CheckDirReplace(std::string Dir,unsigned int Depth = 0);
+
+ public:
+
+ virtual bool DoItem(Item &Itm,int &Fd) APT_OVERRIDE;
+ virtual bool Fail(Item &Itm,int Fd) APT_OVERRIDE;
+ virtual bool FinishedFile(Item &Itm,int Fd) APT_OVERRIDE;
+
+ bool Finished();
+ bool Aborted();
+
+ pkgExtract(pkgFLCache &FLCache,pkgCache::VerIterator Ver);
+};
+
+#endif
diff --git a/apt-inst/filelist.cc b/apt-inst/filelist.cc
new file mode 100644
index 0000000..44b97d0
--- /dev/null
+++ b/apt-inst/filelist.cc
@@ -0,0 +1,586 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ File Listing - Manages a Cache of File -> Package names.
+
+ Diversions add some significant complexity to the system. To keep
+ storage space down in the very special case of a diverted file no
+ extra bytes are allocated in the Node structure. Instead a diversion
+ is inserted directly into the hash table and its flag bit set. Every
+ lookup for that filename will always return the diversion.
+
+ The hash buckets are stored in sorted form, with diversions having
+ the highest sort order. Identical files are assigned the same file
+ pointer, thus after a search all of the nodes owning that file can be
+ found by iterating down the bucket.
+
+ Re-updates of diversions (another extremely special case) are done by
+ marking all diversions as untouched, then loading the entire diversion
+ list again, touching each diversion and then finally going back and
+ releasing all untouched diversions. It is assumed that the diversion
+ table will always be quite small and be a very irregular case.
+
+ Diversions that are user-installed are represented by a package with
+ an empty name string.
+
+ Conf files are handled like diversions by changing the meaning of the
+ Pointer field to point to a conf file entry - again to reduce over
+ head for a special case.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/error.h>
+#include <apt-pkg/filelist.h>
+#include <apt-pkg/mmap.h>
+#include <apt-pkg/strutl.h>
+
+#include <iostream>
+#include <string.h>
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+// FlCache::Header::Header - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* Initialize the header variables. These are the defaults used when
+ creating new caches */
+pkgFLCache::Header::Header()
+{
+ Signature = 0xEA3F1295;
+
+ /* Whenever the structures change the major version should be bumped,
+ whenever the generator changes the minor version should be bumped. */
+ MajorVersion = 1;
+ MinorVersion = 0;
+ Dirty = true;
+
+ HeaderSz = sizeof(pkgFLCache::Header);
+ NodeSz = sizeof(pkgFLCache::Node);
+ DirSz = sizeof(pkgFLCache::Directory);
+ PackageSz = sizeof(pkgFLCache::Package);
+ DiversionSz = sizeof(pkgFLCache::Diversion);
+ ConfFileSz = sizeof(pkgFLCache::ConfFile);
+
+ NodeCount = 0;
+ DirCount = 0;
+ PackageCount = 0;
+ DiversionCount = 0;
+ ConfFileCount = 0;
+ HashSize = 1 << 14;
+
+ FileHash = 0;
+ DirTree = 0;
+ Packages = 0;
+ Diversions = 0;
+ UniqNodes = 0;
+ memset(Pools,0,sizeof(Pools));
+}
+ /*}}}*/
+// FLCache::Header::CheckSizes - Check if the two headers have same *sz /*{{{*/
+// ---------------------------------------------------------------------
+/* Compare to make sure we are matching versions */
+APT_PURE bool pkgFLCache::Header::CheckSizes(Header &Against) const
+{
+ if (HeaderSz == Against.HeaderSz &&
+ NodeSz == Against.NodeSz &&
+ DirSz == Against.DirSz &&
+ DiversionSz == Against.DiversionSz &&
+ PackageSz == Against.PackageSz &&
+ ConfFileSz == Against.ConfFileSz)
+ return true;
+ return false;
+}
+ /*}}}*/
+
+// FLCache::pkgFLCache - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* If this is a new cache then a new header and hash table are instantaited
+ otherwise the existing ones are mearly attached */
+pkgFLCache::pkgFLCache(DynamicMMap &Map) : Map(Map)
+{
+ if (_error->PendingError() == true)
+ return;
+
+ LastTreeLookup = 0;
+ LastLookupSize = 0;
+
+ // Apply the typecasts
+ HeaderP = (Header *)Map.Data();
+ NodeP = (Node *)Map.Data();
+ DirP = (Directory *)Map.Data();
+ DiverP = (Diversion *)Map.Data();
+ PkgP = (Package *)Map.Data();
+ ConfP = (ConfFile *)Map.Data();
+ StrP = (char *)Map.Data();
+ AnyP = (unsigned char *)Map.Data();
+
+ // New mapping, create the basic cache structures
+ if (Map.Size() == 0)
+ {
+ Map.RawAllocate(sizeof(pkgFLCache::Header));
+ *HeaderP = pkgFLCache::Header();
+ HeaderP->FileHash = Map.RawAllocate(sizeof(pkgFLCache::Node)*HeaderP->HashSize,
+ sizeof(pkgFLCache::Node))/sizeof(pkgFLCache::Node);
+ }
+
+ FileHash = NodeP + HeaderP->FileHash;
+
+ // Setup the dynamic map manager
+ HeaderP->Dirty = true;
+ Map.Sync(0,sizeof(pkgFLCache::Header));
+ Map.UsePools(*HeaderP->Pools,sizeof(HeaderP->Pools)/sizeof(HeaderP->Pools[0]));
+}
+ /*}}}*/
+// FLCache::TreeLookup - Perform a lookup in a generic tree /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a simple generic tree lookup. The first three entries of
+ the Directory structure are used as a template, but any other similar
+ structure could be used in it's place. */
+map_ptrloc pkgFLCache::TreeLookup(map_ptrloc *Base,const char *Text,
+ const char *TextEnd,unsigned long Size,
+ unsigned int *Count,bool Insert)
+{
+ pkgFLCache::Directory *Dir;
+
+ // Check our last entry cache
+ if (LastTreeLookup != 0 && LastLookupSize == Size)
+ {
+ Dir = (pkgFLCache::Directory *)(AnyP + LastTreeLookup*Size);
+ if (stringcmp(Text,TextEnd,StrP + Dir->Name) == 0)
+ return LastTreeLookup;
+ }
+
+ while (1)
+ {
+ // Allocate a new one
+ if (*Base == 0)
+ {
+ if (Insert == false)
+ return 0;
+
+ *Base = Map.Allocate(Size);
+ if (*Base == 0)
+ return 0;
+
+ (*Count)++;
+ Dir = (pkgFLCache::Directory *)(AnyP + *Base*Size);
+ Dir->Name = Map.WriteString(Text,TextEnd - Text);
+ LastTreeLookup = *Base;
+ LastLookupSize = Size;
+ return *Base;
+ }
+
+ // Compare this node
+ Dir = (pkgFLCache::Directory *)(AnyP + *Base*Size);
+ int Res = stringcmp(Text,TextEnd,StrP + Dir->Name);
+ if (Res == 0)
+ {
+ LastTreeLookup = *Base;
+ LastLookupSize = Size;
+ return *Base;
+ }
+
+ if (Res > 0)
+ Base = &Dir->Left;
+ if (Res < 0)
+ Base = &Dir->Right;
+ }
+}
+ /*}}}*/
+// FLCache::PrintTree - Print out a tree /*{{{*/
+// ---------------------------------------------------------------------
+/* This is a simple generic tree dumper, meant for debugging. */
+void pkgFLCache::PrintTree(map_ptrloc Base,unsigned long Size)
+{
+ if (Base == 0)
+ return;
+
+ pkgFLCache::Directory *Dir = (pkgFLCache::Directory *)(AnyP + Base*Size);
+ PrintTree(Dir->Left,Size);
+ cout << (StrP + Dir->Name) << endl;
+ PrintTree(Dir->Right,Size);
+}
+ /*}}}*/
+// FLCache::GetPkg - Get a package pointer /*{{{*/
+// ---------------------------------------------------------------------
+/* Locate a package by name in it's tree, this is just a wrapper for
+ TreeLookup */
+pkgFLCache::PkgIterator pkgFLCache::GetPkg(const char *Name,const char *NameEnd,
+ bool Insert)
+{
+ if (NameEnd == 0)
+ NameEnd = Name + strlen(Name);
+
+ map_ptrloc Pos = TreeLookup(&HeaderP->Packages,Name,NameEnd,
+ sizeof(pkgFLCache::Package),
+ &HeaderP->PackageCount,Insert);
+ if (Pos == 0)
+ return pkgFLCache::PkgIterator();
+ return pkgFLCache::PkgIterator(*this,PkgP + Pos);
+}
+ /*}}}*/
+// FLCache::GetNode - Get the node associated with the filename /*{{{*/
+// ---------------------------------------------------------------------
+/* Lookup a node in the hash table. If Insert is true then a new node is
+ always inserted. The hash table can have multiple instances of a
+ single name available. A search returns the first. It is important
+ that additions for the same name insert after the first entry of
+ the name group. */
+pkgFLCache::NodeIterator pkgFLCache::GetNode(const char *Name,
+ const char *NameEnd,
+ map_ptrloc Loc,
+ bool Insert,bool Divert)
+{
+ // Split the name into file and directory, hashing as it is copied
+ const char *File = Name;
+ unsigned long HashPos = 0;
+ for (const char *I = Name; I < NameEnd; I++)
+ {
+ HashPos = 1637*HashPos + *I;
+ if (*I == '/')
+ File = I;
+ }
+
+ // Search for it
+ Node *Hash = NodeP + HeaderP->FileHash + (HashPos % HeaderP->HashSize);
+ int Res = 0;
+ map_ptrloc FilePtr = 0;
+ while (Hash->Pointer != 0)
+ {
+ // Compare
+ Res = stringcmp(File+1,NameEnd,StrP + Hash->File);
+ if (Res == 0)
+ Res = stringcmp(Name,File,StrP + DirP[Hash->Dir].Name);
+
+ // Diversion?
+ if (Res == 0 && Insert == true)
+ {
+ /* Dir and File match exactly, we need to reuse the file name
+ when we link it in */
+ FilePtr = Hash->File;
+ Res = Divert - ((Hash->Flags & Node::Diversion) == Node::Diversion);
+ }
+
+ // Is a match
+ if (Res == 0)
+ {
+ if (Insert == false)
+ return NodeIterator(*this,Hash);
+
+ // Only one diversion per name!
+ if (Divert == true)
+ return NodeIterator(*this,Hash);
+ break;
+ }
+
+ // Out of sort order
+ if (Res > 0)
+ break;
+
+ if (Hash->Next != 0)
+ Hash = NodeP + Hash->Next;
+ else
+ break;
+ }
+
+ // Fail, not found
+ if (Insert == false)
+ return NodeIterator(*this);
+
+ // Find a directory node
+ map_ptrloc Dir = TreeLookup(&HeaderP->DirTree,Name,File,
+ sizeof(pkgFLCache::Directory),
+ &HeaderP->DirCount,true);
+ if (Dir == 0)
+ return NodeIterator(*this);
+
+ // Allocate a new node
+ if (Hash->Pointer != 0)
+ {
+ // Overwrite or append
+ if (Res > 0)
+ {
+ Node *Next = NodeP + Map.Allocate(sizeof(*Hash));
+ if (Next == NodeP)
+ return NodeIterator(*this);
+ *Next = *Hash;
+ Hash->Next = Next - NodeP;
+ }
+ else
+ {
+ unsigned long NewNext = Map.Allocate(sizeof(*Hash));
+ if (NewNext == 0)
+ return NodeIterator(*this);
+ NodeP[NewNext].Next = Hash->Next;
+ Hash->Next = NewNext;
+ Hash = NodeP + Hash->Next;
+ }
+ }
+
+ // Insert into the new item
+ Hash->Dir = Dir;
+ Hash->Pointer = Loc;
+ Hash->Flags = 0;
+ if (Divert == true)
+ Hash->Flags |= Node::Diversion;
+
+ if (FilePtr != 0)
+ Hash->File = FilePtr;
+ else
+ {
+ HeaderP->UniqNodes++;
+ Hash->File = Map.WriteString(File+1,NameEnd - File-1);
+ }
+
+ // Link the node to the package list
+ if (Divert == false && Loc == 0)
+ {
+ Hash->Next = PkgP[Loc].Files;
+ PkgP[Loc].Files = Hash - NodeP;
+ }
+
+ HeaderP->NodeCount++;
+ return NodeIterator(*this,Hash);
+}
+ /*}}}*/
+// FLCache::HashNode - Return the hash bucket for the node /*{{{*/
+// ---------------------------------------------------------------------
+/* This is one of two hashing functions. The other is inlined into the
+ GetNode routine. */
+APT_PURE pkgFLCache::Node *pkgFLCache::HashNode(NodeIterator const &Nde)
+{
+ // Hash the node
+ unsigned long HashPos = 0;
+ for (const char *I = Nde.DirN(); *I != 0; I++)
+ HashPos = 1637*HashPos + *I;
+ HashPos = 1637*HashPos + '/';
+ for (const char *I = Nde.File(); *I != 0; I++)
+ HashPos = 1637*HashPos + *I;
+ return NodeP + HeaderP->FileHash + (HashPos % HeaderP->HashSize);
+}
+ /*}}}*/
+// FLCache::DropNode - Drop a node from the hash table /*{{{*/
+// ---------------------------------------------------------------------
+/* This erases a node from the hash table. Note that this does not unlink
+ the node from the package linked list. */
+void pkgFLCache::DropNode(map_ptrloc N)
+{
+ if (N == 0)
+ return;
+
+ NodeIterator Nde(*this,NodeP + N);
+
+ if (Nde->NextPkg != 0)
+ _error->Warning(_("DropNode called on still linked node"));
+
+ // Locate it in the hash table
+ Node *Last = 0;
+ Node *Hash = HashNode(Nde);
+ while (Hash->Pointer != 0)
+ {
+ // Got it
+ if (Hash == Nde)
+ {
+ // Top of the bucket..
+ if (Last == 0)
+ {
+ Hash->Pointer = 0;
+ if (Hash->Next == 0)
+ return;
+ *Hash = NodeP[Hash->Next];
+ // Release Hash->Next
+ return;
+ }
+ Last->Next = Hash->Next;
+ // Release Hash
+ return;
+ }
+
+ Last = Hash;
+ if (Hash->Next != 0)
+ Hash = NodeP + Hash->Next;
+ else
+ break;
+ }
+
+ _error->Error(_("Failed to locate the hash element!"));
+}
+ /*}}}*/
+// FLCache::BeginDiverLoad - Start reading new diversions /*{{{*/
+// ---------------------------------------------------------------------
+/* Tag all the diversions as untouched */
+void pkgFLCache::BeginDiverLoad()
+{
+ for (DiverIterator I = DiverBegin(); I.end() == false; I++)
+ I->Flags = 0;
+}
+ /*}}}*/
+// FLCache::FinishDiverLoad - Finish up a new diversion load /*{{{*/
+// ---------------------------------------------------------------------
+/* This drops any untouched diversions. In effect removing any diversions
+ that where not loaded (ie missing from the diversion file) */
+void pkgFLCache::FinishDiverLoad()
+{
+ map_ptrloc *Cur = &HeaderP->Diversions;
+ while (*Cur != 0)
+ {
+ Diversion *Div = DiverP + *Cur;
+ if ((Div->Flags & Diversion::Touched) == Diversion::Touched)
+ {
+ Cur = &Div->Next;
+ continue;
+ }
+
+ // Purge!
+ DropNode(Div->DivertTo);
+ DropNode(Div->DivertFrom);
+ *Cur = Div->Next;
+ }
+}
+ /*}}}*/
+// FLCache::AddDiversion - Add a new diversion /*{{{*/
+// ---------------------------------------------------------------------
+/* Add a new diversion to the diverion tables and make sure that it is
+ unique and non-chaining. */
+bool pkgFLCache::AddDiversion(PkgIterator const &Owner,
+ const char *From,const char *To)
+{
+ /* Locate the two hash nodes we are going to manipulate. If there
+ are pre-existing diversions then they will be returned */
+ NodeIterator FromN = GetNode(From,From+strlen(From),0,true,true);
+ NodeIterator ToN = GetNode(To,To+strlen(To),0,true,true);
+ if (FromN.end() == true || ToN.end() == true)
+ return _error->Error(_("Failed to allocate diversion"));
+
+ // Should never happen
+ if ((FromN->Flags & Node::Diversion) != Node::Diversion ||
+ (ToN->Flags & Node::Diversion) != Node::Diversion)
+ return _error->Error(_("Internal error in AddDiversion"));
+
+ // Now, try to reclaim an existing diversion..
+ map_ptrloc Diver = 0;
+ if (FromN->Pointer != 0)
+ Diver = FromN->Pointer;
+
+ /* Make sure from and to point to the same diversion, if they don't
+ then we are trying to intermix diversions - very bad */
+ if (ToN->Pointer != 0 && ToN->Pointer != Diver)
+ {
+ // It could be that the other diversion is no longer in use
+ if ((DiverP[ToN->Pointer].Flags & Diversion::Touched) == Diversion::Touched)
+ return _error->Error(_("Trying to overwrite a diversion, %s -> %s and %s/%s"),
+ From,To,ToN.File(),ToN.Dir().Name());
+
+ // We can erase it.
+ Diversion *Div = DiverP + ToN->Pointer;
+ ToN->Pointer = 0;
+
+ if (Div->DivertTo == ToN.Offset())
+ Div->DivertTo = 0;
+ if (Div->DivertFrom == ToN.Offset())
+ Div->DivertFrom = 0;
+
+ // This diversion will be cleaned up by FinishDiverLoad
+ }
+
+ // Allocate a new diversion
+ if (Diver == 0)
+ {
+ Diver = Map.Allocate(sizeof(Diversion));
+ if (Diver == 0)
+ return false;
+ DiverP[Diver].Next = HeaderP->Diversions;
+ HeaderP->Diversions = Diver;
+ HeaderP->DiversionCount++;
+ }
+
+ // Can only have one diversion of the same files
+ Diversion *Div = DiverP + Diver;
+ if ((Div->Flags & Diversion::Touched) == Diversion::Touched)
+ return _error->Error(_("Double add of diversion %s -> %s"),From,To);
+
+ // Setup the From/To links
+ if (Div->DivertFrom != FromN.Offset() && Div->DivertFrom != ToN.Offset())
+ DropNode(Div->DivertFrom);
+ Div->DivertFrom = FromN.Offset();
+ if (Div->DivertTo != FromN.Offset() && Div->DivertTo != ToN.Offset())
+ DropNode(Div->DivertTo);
+ Div->DivertTo = ToN.Offset();
+
+ // Link it to the two nodes
+ FromN->Pointer = Diver;
+ ToN->Pointer = Diver;
+
+ // And the package
+ Div->OwnerPkg = Owner.Offset();
+ Div->Flags |= Diversion::Touched;
+
+ return true;
+}
+ /*}}}*/
+// FLCache::AddConfFile - Add a new configuration file /*{{{*/
+// ---------------------------------------------------------------------
+/* This simply adds a new conf file node to the hash table. This is only
+ used by the status file reader. It associates a hash with each conf
+ file entry that exists in the status file and the list file for
+ the proper package. Duplicate conf files (across packages) are left
+ up to other routines to deal with. */
+bool pkgFLCache::AddConfFile(const char *Name,const char *NameEnd,
+ PkgIterator const &Owner,
+ const unsigned char *Sum)
+{
+ NodeIterator Nde = GetNode(Name,NameEnd,0,false,false);
+ if (Nde.end() == true)
+ return true;
+
+ unsigned long File = Nde->File;
+ for (; Nde->File == File && Nde.end() == false; Nde++)
+ {
+ if (Nde.RealPackage() != Owner)
+ continue;
+
+ if ((Nde->Flags & Node::ConfFile) == Node::ConfFile)
+ return _error->Error(_("Duplicate conf file %s/%s"),Nde.DirN(),Nde.File());
+
+ // Allocate a new conf file structure
+ map_ptrloc Conf = Map.Allocate(sizeof(ConfFile));
+ if (Conf == 0)
+ return false;
+ ConfP[Conf].OwnerPkg = Owner.Offset();
+ memcpy(ConfP[Conf].MD5,Sum,sizeof(ConfP[Conf].MD5));
+
+ Nde->Pointer = Conf;
+ Nde->Flags |= Node::ConfFile;
+ return true;
+ }
+
+ /* This means the conf file has been replaced, but the entry in the
+ status file was not updated */
+ return true;
+}
+ /*}}}*/
+
+// NodeIterator::RealPackage - Return the package for this node /*{{{*/
+// ---------------------------------------------------------------------
+/* Since the package pointer is indirected in all sorts of interesting ways
+ this is used to get a pointer to the owning package */
+APT_PURE pkgFLCache::Package *pkgFLCache::NodeIterator::RealPackage() const
+{
+ if (Nde->Pointer == 0)
+ return 0;
+
+ if ((Nde->Flags & Node::ConfFile) == Node::ConfFile)
+ return Owner->PkgP + Owner->ConfP[Nde->Pointer].OwnerPkg;
+
+ // Diversions are ignored
+ if ((Nde->Flags & Node::Diversion) == Node::Diversion)
+ return 0;
+
+ return Owner->PkgP + Nde->Pointer;
+}
+ /*}}}*/
diff --git a/apt-inst/filelist.h b/apt-inst/filelist.h
new file mode 100644
index 0000000..7fe43de
--- /dev/null
+++ b/apt-inst/filelist.h
@@ -0,0 +1,312 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ File Listing - Manages a Cache of File -> Package names.
+
+ This is identical to the Package cache, except that the generator
+ (which is much simpler) is integrated directly into the main class,
+ and it has been designed to handle live updates.
+
+ The storage content of the class is maintained in a memory map and is
+ written directly to the file system. Performance is traded against
+ space to give something that performs well and remains small.
+ The average per file usage is 32 bytes which yields about a meg every
+ 36k files. Directory paths are collected into a binary tree and stored
+ only once, this offsets the cost of the hash nodes enough to keep
+ memory usage slightly less than the sum of the filenames.
+
+ The file names are stored into a fixed size chained hash table that is
+ linked to the package name and to the directory component.
+
+ Each file node has a set of associated flags that indicate the current
+ state of the file.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_FILELIST_H
+#define PKGLIB_FILELIST_H
+
+#include <apt-pkg/mmap.h>
+
+#include <cstring>
+#include <string>
+
+class pkgFLCache
+{
+ public:
+ struct Header;
+ struct Node;
+ struct Directory;
+ struct Package;
+ struct Diversion;
+ struct ConfFile;
+
+ class NodeIterator;
+ class DirIterator;
+ class PkgIterator;
+ class DiverIterator;
+
+ protected:
+ std::string CacheFile;
+ DynamicMMap &Map;
+ map_ptrloc LastTreeLookup;
+ unsigned long LastLookupSize;
+
+ // Helpers for the addition algorithms
+ map_ptrloc TreeLookup(map_ptrloc *Base,const char *Text,const char *TextEnd,
+ unsigned long Size,unsigned int *Count = 0,
+ bool Insert = false);
+
+ public:
+
+ // Pointers to the arrays of items
+ Header *HeaderP;
+ Node *NodeP;
+ Directory *DirP;
+ Package *PkgP;
+ Diversion *DiverP;
+ ConfFile *ConfP;
+ char *StrP;
+ unsigned char *AnyP;
+
+ // Quick accessors
+ Node *FileHash;
+
+ // Accessors
+ Header &Head() {return *HeaderP;};
+ void PrintTree(map_ptrloc Base,unsigned long Size);
+
+ // Add/Find things
+ PkgIterator GetPkg(const char *Name,const char *End,bool Insert);
+ inline PkgIterator GetPkg(const char *Name,bool Insert);
+ NodeIterator GetNode(const char *Name,
+ const char *NameEnd,
+ map_ptrloc Loc,
+ bool Insert,bool Divert);
+ Node *HashNode(NodeIterator const &N);
+ void DropNode(map_ptrloc Node);
+
+ inline DiverIterator DiverBegin();
+
+ // Diversion control
+ void BeginDiverLoad();
+ void FinishDiverLoad();
+ bool AddDiversion(PkgIterator const &Owner,const char *From,
+ const char *To);
+ bool AddConfFile(const char *Name,const char *NameEnd,
+ PkgIterator const &Owner,const unsigned char *Sum);
+
+ pkgFLCache(DynamicMMap &Map);
+// ~pkgFLCache();
+};
+
+struct pkgFLCache::Header
+{
+ // Signature information
+ unsigned long Signature;
+ short MajorVersion;
+ short MinorVersion;
+ bool Dirty;
+
+ // Size of structure values
+ unsigned HeaderSz;
+ unsigned NodeSz;
+ unsigned DirSz;
+ unsigned PackageSz;
+ unsigned DiversionSz;
+ unsigned ConfFileSz;
+
+ // Structure Counts;
+ unsigned int NodeCount;
+ unsigned int DirCount;
+ unsigned int PackageCount;
+ unsigned int DiversionCount;
+ unsigned int ConfFileCount;
+ unsigned int HashSize;
+ unsigned long UniqNodes;
+
+ // Offsets
+ map_ptrloc FileHash;
+ map_ptrloc DirTree;
+ map_ptrloc Packages;
+ map_ptrloc Diversions;
+
+ /* Allocation pools, there should be one of these for each structure
+ excluding the header */
+ DynamicMMap::Pool Pools[5];
+
+ bool CheckSizes(Header &Against) const;
+ Header();
+};
+
+/* The bit field is used to advoid incurring an extra 4 bytes x 40000,
+ Pointer is the most infrequently used member of the structure */
+struct pkgFLCache::Node
+{
+ map_ptrloc Dir; // Dir
+ map_ptrloc File; // String
+ unsigned Pointer:24; // Package/Diversion/ConfFile
+ unsigned Flags:8; // Package
+ map_ptrloc Next; // Node
+ map_ptrloc NextPkg; // Node
+
+ enum Flags {Diversion = (1<<0),ConfFile = (1<<1),
+ NewConfFile = (1<<2),NewFile = (1<<3),
+ Unpacked = (1<<4),Replaced = (1<<5)};
+};
+
+struct pkgFLCache::Directory
+{
+ map_ptrloc Left; // Directory
+ map_ptrloc Right; // Directory
+ map_ptrloc Name; // String
+};
+
+struct pkgFLCache::Package
+{
+ map_ptrloc Left; // Package
+ map_ptrloc Right; // Package
+ map_ptrloc Name; // String
+ map_ptrloc Files; // Node
+};
+
+struct pkgFLCache::Diversion
+{
+ map_ptrloc OwnerPkg; // Package
+ map_ptrloc DivertFrom; // Node
+ map_ptrloc DivertTo; // String
+
+ map_ptrloc Next; // Diversion
+ unsigned long Flags;
+
+ enum Flags {Touched = (1<<0)};
+};
+
+struct pkgFLCache::ConfFile
+{
+ map_ptrloc OwnerPkg; // Package
+ unsigned char MD5[16];
+};
+
+class pkgFLCache::PkgIterator
+{
+ Package *Pkg;
+ pkgFLCache *Owner;
+
+ public:
+
+ inline bool end() const {return Owner == 0 || Pkg == Owner->PkgP?true:false;}
+
+ // Accessors
+ inline Package *operator ->() {return Pkg;}
+ inline Package const *operator ->() const {return Pkg;}
+ inline Package const &operator *() const {return *Pkg;}
+ inline operator Package *() {return Pkg == Owner->PkgP?0:Pkg;}
+ inline operator Package const *() const {return Pkg == Owner->PkgP?0:Pkg;}
+
+ inline unsigned long Offset() const {return Pkg - Owner->PkgP;}
+ inline const char *Name() const {return Pkg->Name == 0?0:Owner->StrP + Pkg->Name;}
+ inline pkgFLCache::NodeIterator Files() const;
+
+ PkgIterator() : Pkg(0), Owner(0) {}
+ PkgIterator(pkgFLCache &Owner,Package *Trg) : Pkg(Trg), Owner(&Owner) {}
+};
+
+class pkgFLCache::DirIterator
+{
+ Directory *Dir;
+ pkgFLCache *Owner;
+
+ public:
+
+ // Accessors
+ inline Directory *operator ->() {return Dir;}
+ inline Directory const *operator ->() const {return Dir;}
+ inline Directory const &operator *() const {return *Dir;}
+ inline operator Directory *() {return Dir == Owner->DirP?0:Dir;}
+ inline operator Directory const *() const {return Dir == Owner->DirP?0:Dir;}
+
+ inline const char *Name() const {return Dir->Name == 0?0:Owner->StrP + Dir->Name;}
+
+ DirIterator() : Dir(0), Owner(0) {}
+ DirIterator(pkgFLCache &Owner,Directory *Trg) : Dir(Trg), Owner(&Owner) {}
+};
+
+class pkgFLCache::DiverIterator
+{
+ Diversion *Diver;
+ pkgFLCache *Owner;
+
+ public:
+
+ // Iteration
+ void operator ++(int) {if (Diver != Owner->DiverP) Diver = Owner->DiverP + Diver->Next;}
+ inline void operator ++() {operator ++(0);}
+ inline bool end() const {return Owner == 0 || Diver == Owner->DiverP;}
+
+ // Accessors
+ inline Diversion *operator ->() {return Diver;}
+ inline Diversion const *operator ->() const {return Diver;}
+ inline Diversion const &operator *() const {return *Diver;}
+ inline operator Diversion *() {return Diver == Owner->DiverP?0:Diver;}
+ inline operator Diversion const *() const {return Diver == Owner->DiverP?0:Diver;}
+
+ inline PkgIterator OwnerPkg() const {return PkgIterator(*Owner,Owner->PkgP + Diver->OwnerPkg);}
+ inline NodeIterator DivertFrom() const;
+ inline NodeIterator DivertTo() const;
+
+ DiverIterator() : Diver(0), Owner(0) {};
+ DiverIterator(pkgFLCache &Owner,Diversion *Trg) : Diver(Trg), Owner(&Owner) {}
+};
+
+class pkgFLCache::NodeIterator
+{
+ Node *Nde;
+ enum {NdePkg, NdeHash} Type;
+ pkgFLCache *Owner;
+
+ public:
+
+ // Iteration
+ void operator ++(int) {if (Nde != Owner->NodeP) Nde = Owner->NodeP +
+ (Type == NdePkg?Nde->NextPkg:Nde->Next);}
+ inline void operator ++() {operator ++(0);}
+ inline bool end() const {return Owner == 0 || Nde == Owner->NodeP;}
+
+ // Accessors
+ inline Node *operator ->() {return Nde;}
+ inline Node const *operator ->() const {return Nde;}
+ inline Node const &operator *() const {return *Nde;}
+ inline operator Node *() {return Nde == Owner->NodeP?0:Nde;}
+ inline operator Node const *() const {return Nde == Owner->NodeP?0:Nde;}
+ inline unsigned long Offset() const {return Nde - Owner->NodeP;}
+ inline DirIterator Dir() const {return DirIterator(*Owner,Owner->DirP + Nde->Dir);}
+ inline DiverIterator Diversion() const {return DiverIterator(*Owner,Owner->DiverP + Nde->Pointer);}
+ inline const char *File() const {return Nde->File == 0?0:Owner->StrP + Nde->File;}
+ inline const char *DirN() const {return Owner->StrP + Owner->DirP[Nde->Dir].Name;}
+ Package *RealPackage() const;
+
+ NodeIterator() : Nde(0), Type(NdeHash), Owner(0) {};
+ NodeIterator(pkgFLCache &Owner) : Nde(Owner.NodeP), Type(NdeHash), Owner(&Owner) {}
+ NodeIterator(pkgFLCache &Owner,Node *Trg) : Nde(Trg), Type(NdeHash), Owner(&Owner) {}
+ NodeIterator(pkgFLCache &Owner,Node *Trg,Package *) : Nde(Trg), Type(NdePkg), Owner(&Owner) {}
+};
+
+/* Inlines with forward references that cannot be included directly in their
+ respsective classes */
+inline pkgFLCache::NodeIterator pkgFLCache::DiverIterator::DivertFrom() const
+ {return NodeIterator(*Owner,Owner->NodeP + Diver->DivertFrom);}
+inline pkgFLCache::NodeIterator pkgFLCache::DiverIterator::DivertTo() const
+ {return NodeIterator(*Owner,Owner->NodeP + Diver->DivertTo);}
+
+inline pkgFLCache::NodeIterator pkgFLCache::PkgIterator::Files() const
+ {return NodeIterator(*Owner,Owner->NodeP + Pkg->Files,Pkg);}
+
+inline pkgFLCache::DiverIterator pkgFLCache::DiverBegin()
+ {return DiverIterator(*this,DiverP + HeaderP->Diversions);}
+
+inline pkgFLCache::PkgIterator pkgFLCache::GetPkg(const char *Name,bool Insert)
+ {return GetPkg(Name,Name+strlen(Name),Insert);}
+
+#endif
diff --git a/apt-pkg/CMakeLists.txt b/apt-pkg/CMakeLists.txt
new file mode 100644
index 0000000..ce73c6a
--- /dev/null
+++ b/apt-pkg/CMakeLists.txt
@@ -0,0 +1,76 @@
+# Include apt-pkg directly, as some files have #include <system.h>
+include_directories(${PROJECT_BINARY_DIR}/include/apt-pkg)
+
+add_definitions("-DAPT_PKG_EXPOSE_STRING_VIEW")
+
+file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/apt-pkg/)
+execute_process(COMMAND ${PERL_EXECUTABLE} ${PROJECT_SOURCE_DIR}/triehash/triehash.pl
+ --ignore-case
+ --header ${PROJECT_BINARY_DIR}/include/apt-pkg/tagfile-keys.h
+ --code ${CMAKE_CURRENT_BINARY_DIR}/tagfile-keys.cc
+ --enum-class
+ --enum-name pkgTagSection::Key
+ --function-name pkgTagHash
+ --include "<apt-pkg/tagfile.h>"
+ ${CMAKE_CURRENT_SOURCE_DIR}/tagfile-keys.list)
+set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "tagfile-keys.list")
+
+
+# Set the version of the library
+execute_process(COMMAND awk -v ORS=. "/^\#define APT_PKG_M/ {print \$3}"
+ COMMAND sed "s/\\.\$//"
+ INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/contrib/macros.h
+ OUTPUT_VARIABLE MAJOR OUTPUT_STRIP_TRAILING_WHITESPACE)
+execute_process(COMMAND grep "^#define APT_PKG_RELEASE"
+ COMMAND cut -d " " -f 3
+ INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/contrib/macros.h
+ OUTPUT_VARIABLE MINOR OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+message(STATUS "Building libapt-pkg ${MAJOR} (release ${MINOR})")
+set(APT_PKG_MAJOR ${MAJOR} PARENT_SCOPE) # exporting for methods/CMakeLists.txt
+
+# Definition of the C++ files used to build the library - note that this
+# is expanded at CMake time, so you have to rerun cmake if you add or remove
+# a file (you can just run cmake . in the build directory)
+file(GLOB_RECURSE library "*.cc" "${CMAKE_CURRENT_BINARY_DIR}/tagfile-keys.cc")
+file(GLOB_RECURSE headers "*.h")
+
+# Create a library using the C++ files
+add_library(apt-pkg SHARED ${library})
+add_dependencies(apt-pkg apt-pkg-versionscript)
+# Link the library and set the SONAME
+target_include_directories(apt-pkg
+ PRIVATE ${ZLIB_INCLUDE_DIRS}
+ ${BZIP2_INCLUDE_DIR}
+ ${LZMA_INCLUDE_DIRS}
+ ${LZ4_INCLUDE_DIRS}
+ $<$<BOOL:${ZSTD_FOUND}>:${ZSTD_INCLUDE_DIRS}>
+ $<$<BOOL:${UDEV_FOUND}>:${UDEV_INCLUDE_DIRS}>
+ $<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_INCLUDE_DIRS}>
+ ${ICONV_INCLUDE_DIRS}
+)
+
+target_link_libraries(apt-pkg
+ PRIVATE -lutil ${CMAKE_DL_LIBS} ${RESOLV_LIBRARIES}
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${ZLIB_LIBRARIES}
+ ${BZIP2_LIBRARIES}
+ ${LZMA_LIBRARIES}
+ ${LZ4_LIBRARIES}
+ $<$<BOOL:${ZSTD_FOUND}>:${ZSTD_LIBRARIES}>
+ $<$<BOOL:${UDEV_FOUND}>:${UDEV_LIBRARIES}>
+ $<$<BOOL:${SYSTEMD_FOUND}>:${SYSTEMD_LIBRARIES}>
+ ${ICONV_LIBRARIES}
+)
+set_target_properties(apt-pkg PROPERTIES VERSION ${MAJOR}.${MINOR})
+set_target_properties(apt-pkg PROPERTIES SOVERSION ${MAJOR})
+add_version_script(apt-pkg)
+
+# Install the library and the header files
+install(TARGETS apt-pkg LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(FILES ${headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/apt-pkg)
+flatify(${PROJECT_BINARY_DIR}/include/apt-pkg/ "${headers}")
+
+if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
+ target_link_libraries(apt-pkg PUBLIC noprofile)
+endif()
diff --git a/apt-pkg/acquire-item.cc b/apt-pkg/acquire-item.cc
new file mode 100644
index 0000000..606c949
--- /dev/null
+++ b/apt-pkg/acquire-item.cc
@@ -0,0 +1,4078 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Item - Item to acquire
+
+ Each item can download to exactly one file at a time. This means you
+ cannot create an item that fetches two uri's to two files at the same
+ time. The pkgAcqIndex class creates a second class upon instantiation
+ to fetch the other index files because of this.
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire-worker.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/gpgv.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/indexfile.h>
+#include <apt-pkg/metaindex.h>
+#include <apt-pkg/netrc.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/sourcelist.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/tagfile.h>
+
+#include <algorithm>
+#include <ctime>
+#include <chrono>
+#include <iostream>
+#include <memory>
+#include <numeric>
+#include <random>
+#include <sstream>
+#include <string>
+#include <unordered_set>
+#include <vector>
+#include <errno.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+static std::string GetPartialFileName(std::string const &file) /*{{{*/
+{
+ std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
+ DestFile += file;
+ return DestFile;
+}
+ /*}}}*/
+static std::string GetPartialFileNameFromURI(std::string const &uri) /*{{{*/
+{
+ return GetPartialFileName(URItoFileName(uri));
+}
+ /*}}}*/
+static std::string GetFinalFileNameFromURI(std::string const &uri) /*{{{*/
+{
+ return _config->FindDir("Dir::State::lists") + URItoFileName(uri);
+}
+ /*}}}*/
+static std::string GetKeepCompressedFileName(std::string file, IndexTarget const &Target)/*{{{*/
+{
+ if (Target.KeepCompressed == false)
+ return file;
+
+ std::string const KeepCompressedAs = Target.Option(IndexTarget::KEEPCOMPRESSEDAS);
+ if (KeepCompressedAs.empty() == false)
+ {
+ std::string const ext = KeepCompressedAs.substr(0, KeepCompressedAs.find(' '));
+ if (ext != "uncompressed")
+ file.append(".").append(ext);
+ }
+ return file;
+}
+ /*}}}*/
+static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/
+{
+ // rred expects the patch as $FinalFile.ed.$patchname.gz
+ return Final + ".ed." + Patch + ".gz";
+}
+ /*}}}*/
+static std::string GetDiffsPatchFileName(std::string const &Final) /*{{{*/
+{
+ // rred expects the patch as $FinalFile.ed
+ return Final + ".ed";
+}
+ /*}}}*/
+static std::string GetExistingFilename(std::string const &File) /*{{{*/
+{
+ if (RealFileExists(File))
+ return File;
+ for (auto const &type : APT::Configuration::getCompressorExtensions())
+ {
+ std::string const Final = File + type;
+ if (RealFileExists(Final))
+ return Final;
+ }
+ return "";
+}
+ /*}}}*/
+static std::string GetDiffIndexFileName(std::string const &Name) /*{{{*/
+{
+ return Name + ".diff/Index";
+}
+ /*}}}*/
+static std::string GetDiffIndexURI(IndexTarget const &Target) /*{{{*/
+{
+ return Target.URI + ".diff/Index";
+}
+ /*}}}*/
+
+static void ReportMirrorFailureToCentral(pkgAcquire::Item const &I, std::string const &FailCode, std::string const &Details)/*{{{*/
+{
+ // we only act if a mirror was used at all
+ if(I.UsedMirror.empty())
+ return;
+#if 0
+ std::cerr << "\nReportMirrorFailure: "
+ << UsedMirror
+ << " Uri: " << DescURI()
+ << " FailCode: "
+ << FailCode << std::endl;
+#endif
+ string const report = _config->Find("Methods::Mirror::ProblemReporting",
+ LIBEXEC_DIR "/apt-report-mirror-failure");
+ if(!FileExists(report))
+ return;
+
+ std::vector<char const*> const Args = {
+ report.c_str(),
+ I.UsedMirror.c_str(),
+ I.DescURI().c_str(),
+ FailCode.c_str(),
+ Details.c_str(),
+ NULL
+ };
+
+ pid_t pid = ExecFork();
+ if(pid < 0)
+ {
+ _error->Error("ReportMirrorFailure Fork failed");
+ return;
+ }
+ else if(pid == 0)
+ {
+ execvp(Args[0], (char**)Args.data());
+ std::cerr << "Could not exec " << Args[0] << std::endl;
+ _exit(100);
+ }
+ if(!ExecWait(pid, "report-mirror-failure"))
+ _error->Warning("Couldn't report problem to '%s'", report.c_str());
+}
+ /*}}}*/
+
+static APT_NONNULL(2) bool MessageInsecureRepository(bool const isError, char const * const msg, std::string const &repo)/*{{{*/
+{
+ std::string m;
+ strprintf(m, msg, repo.c_str());
+ if (isError)
+ {
+ _error->Error("%s", m.c_str());
+ _error->Notice("%s", _("Updating from such a repository can't be done securely, and is therefore disabled by default."));
+ }
+ else
+ {
+ _error->Warning("%s", m.c_str());
+ _error->Notice("%s", _("Data from such a repository can't be authenticated and is therefore potentially dangerous to use."));
+ }
+ _error->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
+ return false;
+}
+ /*}}}*/
+// AllowInsecureRepositories /*{{{*/
+enum class InsecureType { UNSIGNED, WEAK, NORELEASE };
+static bool TargetIsAllowedToBe(IndexTarget const &Target, InsecureType const type)
+{
+ if (_config->FindB("Acquire::AllowInsecureRepositories"))
+ return true;
+
+ if (Target.OptionBool(IndexTarget::ALLOW_INSECURE))
+ return true;
+
+ switch (type)
+ {
+ case InsecureType::UNSIGNED: break;
+ case InsecureType::NORELEASE: break;
+ case InsecureType::WEAK:
+ if (_config->FindB("Acquire::AllowWeakRepositories"))
+ return true;
+ if (Target.OptionBool(IndexTarget::ALLOW_WEAK))
+ return true;
+ break;
+ }
+ return false;
+}
+static bool APT_NONNULL(3, 4, 5) AllowInsecureRepositories(InsecureType const msg, std::string const &repo,
+ metaIndex const * const MetaIndexParser, pkgAcqMetaClearSig * const TransactionManager, pkgAcquire::Item * const I)
+{
+ // we skip weak downgrades as its unlikely that a repository gets really weaker –
+ // its more realistic that apt got pickier in a newer version
+ if (msg != InsecureType::WEAK)
+ {
+ std::string const FinalInRelease = TransactionManager->GetFinalFilename();
+ std::string const FinalReleasegpg = FinalInRelease.substr(0, FinalInRelease.length() - strlen("InRelease")) + "Release.gpg";
+ if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
+ {
+ char const * msgstr = nullptr;
+ switch (msg)
+ {
+ case InsecureType::UNSIGNED: msgstr = _("The repository '%s' is no longer signed."); break;
+ case InsecureType::NORELEASE: msgstr = _("The repository '%s' no longer has a Release file."); break;
+ case InsecureType::WEAK: /* unreachable */ break;
+ }
+ if (_config->FindB("Acquire::AllowDowngradeToInsecureRepositories") ||
+ TransactionManager->Target.OptionBool(IndexTarget::ALLOW_DOWNGRADE_TO_INSECURE))
+ {
+ // meh, the users wants to take risks (we still mark the packages
+ // from this repository as unauthenticated)
+ _error->Warning(msgstr, repo.c_str());
+ _error->Warning(_("This is normally not allowed, but the option "
+ "Acquire::AllowDowngradeToInsecureRepositories was "
+ "given to override it."));
+ } else {
+ MessageInsecureRepository(true, msgstr, repo);
+ TransactionManager->AbortTransaction();
+ I->Status = pkgAcquire::Item::StatError;
+ return false;
+ }
+ }
+ }
+
+ if(MetaIndexParser->GetTrusted() == metaIndex::TRI_YES)
+ return true;
+
+ char const * msgstr = nullptr;
+ switch (msg)
+ {
+ case InsecureType::UNSIGNED: msgstr = _("The repository '%s' is not signed."); break;
+ case InsecureType::NORELEASE: msgstr = _("The repository '%s' does not have a Release file."); break;
+ case InsecureType::WEAK: msgstr = _("The repository '%s' provides only weak security information."); break;
+ }
+
+ if (TargetIsAllowedToBe(TransactionManager->Target, msg) == true)
+ {
+ MessageInsecureRepository(false, msgstr, repo);
+ return true;
+ }
+
+ MessageInsecureRepository(true, msgstr, repo);
+ TransactionManager->AbortTransaction();
+ I->Status = pkgAcquire::Item::StatError;
+ return false;
+}
+ /*}}}*/
+static HashStringList GetExpectedHashesFromFor(metaIndex * const Parser, std::string const &MetaKey)/*{{{*/
+{
+ if (Parser == NULL)
+ return HashStringList();
+ metaIndex::checkSum * const R = Parser->Lookup(MetaKey);
+ if (R == NULL)
+ return HashStringList();
+ return R->Hashes;
+}
+ /*}}}*/
+
+class pkgAcquire::Item::Private /*{{{*/
+{
+public:
+ struct AlternateURI
+ {
+ std::string URI;
+ std::unordered_map<std::string, std::string> changefields;
+ AlternateURI(std::string &&u, decltype(changefields) &&cf) : URI(u), changefields(cf) {}
+ };
+ std::list<AlternateURI> AlternativeURIs;
+ std::vector<std::string> BadAlternativeSites;
+ std::vector<std::string> PastRedirections;
+ std::unordered_map<std::string, std::string> CustomFields;
+ unsigned int Retries;
+
+ Private() : Retries(_config->FindI("Acquire::Retries", 0))
+ {
+ }
+};
+ /*}}}*/
+
+// all ::HashesRequired and ::GetExpectedHashes implementations /*{{{*/
+/* ::GetExpectedHashes is abstract and has to be implemented by all subclasses.
+ It is best to implement it as broadly as possible, while ::HashesRequired defaults
+ to true and should be as restrictive as possible for false cases. Note that if
+ a hash is returned by ::GetExpectedHashes it must match. Only if it doesn't
+ ::HashesRequired is called to evaluate if its okay to have no hashes. */
+APT_PURE bool pkgAcqTransactionItem::HashesRequired() const
+{
+ /* signed repositories obviously have a parser and good hashes.
+ unsigned repositories, too, as even if we can't trust them for security,
+ we can at least trust them for integrity of the download itself.
+ Only repositories without a Release file can (obviously) not have
+ hashes – and they are very uncommon and strongly discouraged */
+ if (TransactionManager->MetaIndexParser->GetLoadedSuccessfully() != metaIndex::TRI_YES)
+ return false;
+ if (TargetIsAllowedToBe(Target, InsecureType::WEAK))
+ {
+ /* If we allow weak hashes, we check that we have some (weak) and then
+ declare hashes not needed. That will tip us in the right direction
+ as if hashes exist, they will be used, even if not required */
+ auto const hsl = GetExpectedHashes();
+ if (hsl.usable())
+ return true;
+ if (hsl.empty() == false)
+ return false;
+ }
+ return true;
+}
+HashStringList pkgAcqTransactionItem::GetExpectedHashes() const
+{
+ return GetExpectedHashesFor(GetMetaKey());
+}
+
+APT_PURE bool pkgAcqMetaBase::HashesRequired() const
+{
+ // Release and co have no hashes 'by design'.
+ return false;
+}
+HashStringList pkgAcqMetaBase::GetExpectedHashes() const
+{
+ return HashStringList();
+}
+
+APT_PURE bool pkgAcqIndexDiffs::HashesRequired() const
+{
+ /* We can't check hashes of rred result as we don't know what the
+ hash of the file will be. We just know the hash of the patch(es),
+ the hash of the file they will apply on and the hash of the resulting
+ file. */
+ if (State == StateFetchDiff)
+ return true;
+ return false;
+}
+HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const
+{
+ if (State == StateFetchDiff)
+ return available_patches[0].download_hashes;
+ return HashStringList();
+}
+
+APT_PURE bool pkgAcqIndexMergeDiffs::HashesRequired() const
+{
+ /* @see #pkgAcqIndexDiffs::HashesRequired, with the difference that
+ we can check the rred result after all patches are applied as
+ we know the expected result rather than potentially apply more patches */
+ if (State == StateFetchDiff)
+ return true;
+ return State == StateApplyDiff;
+}
+HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const
+{
+ if (State == StateFetchDiff)
+ return patch.download_hashes;
+ else if (State == StateApplyDiff)
+ return GetExpectedHashesFor(Target.MetaKey);
+ return HashStringList();
+}
+
+APT_PURE bool pkgAcqArchive::HashesRequired() const
+{
+ return LocalSource == false;
+}
+HashStringList pkgAcqArchive::GetExpectedHashes() const
+{
+ // figured out while parsing the records
+ return ExpectedHashes;
+}
+
+APT_PURE bool pkgAcqFile::HashesRequired() const
+{
+ // supplied as parameter at creation time, so the caller decides
+ return ExpectedHashes.usable();
+}
+HashStringList pkgAcqFile::GetExpectedHashes() const
+{
+ return ExpectedHashes;
+}
+ /*}}}*/
+// Acquire::Item::QueueURI and specialisations from child classes /*{{{*/
+bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ Owner->Enqueue(Item);
+ return true;
+}
+/* The idea here is that an item isn't queued if it exists on disk and the
+ transition manager was a hit as this means that the files it contains
+ the checksums for can't be updated either (or they are and we are asking
+ for a hashsum mismatch to happen which helps nobody) */
+bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ if (TransactionManager->State != TransactionStarted)
+ {
+ if (_config->FindB("Debug::Acquire::Transaction", false))
+ std::clog << "Skip " << Target.URI << " as transaction was already dealt with!" << std::endl;
+ return false;
+ }
+ std::string const FinalFile = GetFinalFilename();
+ if (TransactionManager->IMSHit == true && FileExists(FinalFile) == true)
+ {
+ PartialFile = DestFile = FinalFile;
+ Status = StatDone;
+ return false;
+ }
+ // this ensures we rewrite only once and only the first step
+ auto const OldBaseURI = Target.Option(IndexTarget::BASE_URI);
+ if (OldBaseURI.empty() || APT::String::Startswith(Item.URI, OldBaseURI) == false)
+ return pkgAcquire::Item::QueueURI(Item);
+ // the given URI is our last resort
+ PushAlternativeURI(std::string(Item.URI), {}, false);
+ // If we got the InRelease file via a mirror, pick all indexes directly from this mirror, too
+ std::string SameMirrorURI;
+ if (TransactionManager->BaseURI.empty() == false && TransactionManager->UsedMirror.empty() == false &&
+ URI::SiteOnly(Item.URI) != URI::SiteOnly(TransactionManager->BaseURI))
+ {
+ auto ExtraPath = Item.URI.substr(OldBaseURI.length());
+ auto newURI = flCombine(TransactionManager->BaseURI, std::move(ExtraPath));
+ if (IsGoodAlternativeURI(newURI))
+ {
+ SameMirrorURI = std::move(newURI);
+ PushAlternativeURI(std::string(SameMirrorURI), {}, false);
+ }
+ }
+ // add URI and by-hash based on it
+ if (AcquireByHash())
+ {
+ // if we use the mirror transport, ask it for by-hash uris
+ // we need to stick to the same mirror only for non-unique filenames
+ auto const sameMirrorException = [&]() {
+ if (Item.URI.find("mirror") == std::string::npos)
+ return false;
+ ::URI uri(Item.URI);
+ return uri.Access == "mirror" || APT::String::Startswith(uri.Access, "mirror+") ||
+ APT::String::Endswith(uri.Access, "+mirror") || uri.Access.find("+mirror+") != std::string::npos;
+ }();
+ if (sameMirrorException)
+ SameMirrorURI.clear();
+ // now add the actual by-hash uris
+ auto const Expected = GetExpectedHashes();
+ auto const TargetHash = Expected.find(nullptr);
+ auto const PushByHashURI = [&](std::string U) {
+ if (unlikely(TargetHash == nullptr))
+ return false;
+ auto const trailing_slash = U.find_last_of("/");
+ if (unlikely(trailing_slash == std::string::npos))
+ return false;
+ auto byhashSuffix = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
+ U.replace(trailing_slash, U.length() - trailing_slash, std::move(byhashSuffix));
+ PushAlternativeURI(std::move(U), {}, false);
+ return true;
+ };
+ PushByHashURI(Item.URI);
+ if (SameMirrorURI.empty() == false && PushByHashURI(SameMirrorURI) == false)
+ SameMirrorURI.clear();
+ }
+ // the last URI added is the first one tried
+ if (unlikely(PopAlternativeURI(Item.URI) == false))
+ return false;
+ if (SameMirrorURI.empty() == false)
+ {
+ UsedMirror = TransactionManager->UsedMirror;
+ if (Item.Description.find(" ") != string::npos)
+ Item.Description.replace(0, Item.Description.find(" "), UsedMirror);
+ }
+ return pkgAcquire::Item::QueueURI(Item);
+}
+/* The transition manager InRelease itself (or its older sisters-in-law
+ Release & Release.gpg) is always queued as this allows us to rerun gpgv
+ on it to verify that we aren't stalled with old files */
+bool pkgAcqMetaBase::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ return pkgAcquire::Item::QueueURI(Item);
+}
+/* the Diff/Index needs to queue also the up-to-date complete index file
+ to ensure that the list cleaner isn't eating it */
+bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc &Item)
+{
+ if (pkgAcqTransactionItem::QueueURI(Item) == true)
+ return true;
+ QueueOnIMSHit();
+ return false;
+}
+ /*}}}*/
+// Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
+std::string pkgAcquire::Item::GetFinalFilename() const
+{
+ // Beware: Desc.URI is modified by redirections
+ return GetFinalFileNameFromURI(Desc.URI);
+}
+std::string pkgAcqDiffIndex::GetFinalFilename() const
+{
+ std::string const FinalFile = GetFinalFileNameFromURI(GetDiffIndexURI(Target));
+ // we don't want recompress, so lets keep whatever we got
+ if (CurrentCompressionExtension == "uncompressed")
+ return FinalFile;
+ return FinalFile + "." + CurrentCompressionExtension;
+}
+std::string pkgAcqIndex::GetFinalFilename() const
+{
+ std::string const FinalFile = GetFinalFileNameFromURI(Target.URI);
+ return GetKeepCompressedFileName(FinalFile, Target);
+}
+std::string pkgAcqMetaSig::GetFinalFilename() const
+{
+ return GetFinalFileNameFromURI(Target.URI);
+}
+std::string pkgAcqBaseIndex::GetFinalFilename() const
+{
+ return GetFinalFileNameFromURI(Target.URI);
+}
+std::string pkgAcqMetaBase::GetFinalFilename() const
+{
+ return GetFinalFileNameFromURI(Target.URI);
+}
+std::string pkgAcqArchive::GetFinalFilename() const
+{
+ return _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
+}
+ /*}}}*/
+// pkgAcqTransactionItem::GetMetaKey and specialisations for child classes /*{{{*/
+std::string pkgAcqTransactionItem::GetMetaKey() const
+{
+ return Target.MetaKey;
+}
+std::string pkgAcqIndex::GetMetaKey() const
+{
+ if (Stage == STAGE_DECOMPRESS_AND_VERIFY || CurrentCompressionExtension == "uncompressed")
+ return Target.MetaKey;
+ return Target.MetaKey + "." + CurrentCompressionExtension;
+}
+std::string pkgAcqDiffIndex::GetMetaKey() const
+{
+ auto const metakey = GetDiffIndexFileName(Target.MetaKey);
+ if (CurrentCompressionExtension == "uncompressed")
+ return metakey;
+ return metakey + "." + CurrentCompressionExtension;
+}
+ /*}}}*/
+//pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
+bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
+{
+ bool const Debug = _config->FindB("Debug::Acquire::Transaction", false);
+ switch(state)
+ {
+ case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
+ case TransactionAbort:
+ if(Debug == true)
+ std::clog << " Cancel: " << DestFile << std::endl;
+ if (Status == pkgAcquire::Item::StatIdle)
+ {
+ Status = pkgAcquire::Item::StatDone;
+ Dequeue();
+ }
+ break;
+ case TransactionCommit:
+ if(PartialFile.empty() == false)
+ {
+ bool sameFile = (PartialFile == DestFile);
+ // we use symlinks on IMS-Hit to avoid copies
+ if (RealFileExists(DestFile))
+ {
+ struct stat Buf;
+ if (lstat(PartialFile.c_str(), &Buf) != -1)
+ {
+ if (S_ISLNK(Buf.st_mode) && Buf.st_size > 0)
+ {
+ char partial[Buf.st_size + 1];
+ ssize_t const sp = readlink(PartialFile.c_str(), partial, Buf.st_size);
+ if (sp == -1)
+ _error->Errno("pkgAcqTransactionItem::TransactionState-sp", _("Failed to readlink %s"), PartialFile.c_str());
+ else
+ {
+ partial[sp] = '\0';
+ sameFile = (DestFile == partial);
+ }
+ }
+ }
+ else
+ _error->Errno("pkgAcqTransactionItem::TransactionState-stat", _("Failed to stat %s"), PartialFile.c_str());
+ }
+ if (sameFile == false)
+ {
+ // ensure that even without lists-cleanup all compressions are nuked
+ std::string FinalFile = GetFinalFileNameFromURI(Target.URI);
+ if (FileExists(FinalFile))
+ {
+ if(Debug == true)
+ std::clog << "rm " << FinalFile << " # " << DescURI() << std::endl;
+ if (RemoveFile("TransactionStates-Cleanup", FinalFile) == false)
+ return false;
+ }
+ for (auto const &ext: APT::Configuration::getCompressorExtensions())
+ {
+ auto const Final = FinalFile + ext;
+ if (FileExists(Final))
+ {
+ if(Debug == true)
+ std::clog << "rm " << Final << " # " << DescURI() << std::endl;
+ if (RemoveFile("TransactionStates-Cleanup", Final) == false)
+ return false;
+ }
+ }
+ if(Debug == true)
+ std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
+ if (Rename(PartialFile, DestFile) == false)
+ return false;
+ }
+ else if(Debug == true)
+ std::clog << "keep " << PartialFile << " # " << DescURI() << std::endl;
+
+ } else {
+ if(Debug == true)
+ std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
+ if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+bool pkgAcqMetaBase::TransactionState(TransactionStates const state)
+{
+ // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
+ if (TransactionManager->IMSHit == false)
+ return pkgAcqTransactionItem::TransactionState(state);
+ return true;
+}
+bool pkgAcqIndex::TransactionState(TransactionStates const state)
+{
+ if (pkgAcqTransactionItem::TransactionState(state) == false)
+ return false;
+
+ switch (state)
+ {
+ case TransactionStarted: _error->Fatal("AcqIndex %s changed to invalid transaction start state!", Target.URI.c_str()); break;
+ case TransactionAbort:
+ if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
+ {
+ // keep the compressed file, but drop the decompressed
+ EraseFileName.clear();
+ if (PartialFile.empty() == false && flExtension(PartialFile) != CurrentCompressionExtension)
+ RemoveFile("TransactionAbort", PartialFile);
+ }
+ break;
+ case TransactionCommit:
+ if (EraseFileName.empty() == false)
+ RemoveFile("AcqIndex::TransactionCommit", EraseFileName);
+ break;
+ }
+ return true;
+}
+bool pkgAcqDiffIndex::TransactionState(TransactionStates const state)
+{
+ if (pkgAcqTransactionItem::TransactionState(state) == false)
+ return false;
+
+ switch (state)
+ {
+ case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
+ case TransactionCommit:
+ break;
+ case TransactionAbort:
+ std::string const Partial = GetPartialFileNameFromURI(Target.URI);
+ RemoveFile("TransactionAbort", Partial);
+ break;
+ }
+
+ return true;
+}
+ /*}}}*/
+// pkgAcqTransactionItem::AcquireByHash and specialisations for child classes /*{{{*/
+bool pkgAcqTransactionItem::AcquireByHash() const
+{
+ if (TransactionManager->MetaIndexParser == nullptr)
+ return false;
+ auto const useByHashConf = Target.Option(IndexTarget::BY_HASH);
+ if (useByHashConf == "force")
+ return true;
+ return StringToBool(useByHashConf) == true && TransactionManager->MetaIndexParser->GetSupportsAcquireByHash();
+}
+// pdiff patches have a unique name already, no need for by-hash
+bool pkgAcqIndexMergeDiffs::AcquireByHash() const
+{
+ return false;
+}
+bool pkgAcqIndexDiffs::AcquireByHash() const
+{
+ return false;
+}
+ /*}}}*/
+
+class APT_HIDDEN NoActionItem : public pkgAcquire::Item /*{{{*/
+/* The sole purpose of this class is having an item which does nothing to
+ reach its done state to prevent cleanup deleting the mentioned file.
+ Handy in cases in which we know we have the file already, like IMS-Hits. */
+{
+ IndexTarget const Target;
+ public:
+ virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
+
+ NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target) :
+ pkgAcquire::Item(Owner), Target(Target)
+ {
+ Status = StatDone;
+ DestFile = GetFinalFileNameFromURI(Target.URI);
+ }
+ NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target, std::string const &FinalFile) :
+ pkgAcquire::Item(Owner), Target(Target)
+ {
+ Status = StatDone;
+ DestFile = FinalFile;
+ }
+};
+ /*}}}*/
+class APT_HIDDEN CleanupItem : public pkgAcqTransactionItem /*{{{*/
+/* This class ensures that a file which was configured but isn't downloaded
+ for various reasons isn't kept in an old version in the lists directory.
+ In a way its the reverse of NoActionItem as it helps with removing files
+ even if the lists-cleanup is deactivated. */
+{
+ public:
+ virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
+
+ CleanupItem(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager, IndexTarget const &Target) :
+ pkgAcqTransactionItem(Owner, TransactionManager, Target)
+ {
+ Status = StatDone;
+ DestFile = GetFinalFileNameFromURI(Target.URI);
+ }
+ bool TransactionState(TransactionStates const state) APT_OVERRIDE
+ {
+ switch (state)
+ {
+ case TransactionStarted:
+ break;
+ case TransactionAbort:
+ break;
+ case TransactionCommit:
+ if (_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
+ if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
+ return false;
+ break;
+ }
+ return true;
+ }
+};
+ /*}}}*/
+
+// Acquire::Item::Item - Constructor /*{{{*/
+APT_IGNORE_DEPRECATED_PUSH
+pkgAcquire::Item::Item(pkgAcquire * const owner) :
+ FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false), Local(false),
+ QueueCounter(0), ExpectedAdditionalItems(0), Owner(owner), d(new Private())
+{
+ Owner->Add(this);
+ Status = StatIdle;
+}
+APT_IGNORE_DEPRECATED_POP
+ /*}}}*/
+// Acquire::Item::~Item - Destructor /*{{{*/
+pkgAcquire::Item::~Item()
+{
+ Owner->Remove(this);
+ delete d;
+}
+ /*}}}*/
+std::string pkgAcquire::Item::Custom600Headers() const /*{{{*/
+{
+ std::ostringstream header;
+ for (auto const &f : d->CustomFields)
+ if (f.second.empty() == false)
+ header << '\n'
+ << f.first << ": " << f.second;
+ return header.str();
+}
+ /*}}}*/
+std::unordered_map<std::string, std::string> &pkgAcquire::Item::ModifyCustomFields() /*{{{*/
+{
+ return d->CustomFields;
+}
+ /*}}}*/
+bool pkgAcquire::Item::PopAlternativeURI(std::string &NewURI) /*{{{*/
+{
+ if (d->AlternativeURIs.empty())
+ return false;
+ auto const AltUri = d->AlternativeURIs.front();
+ d->AlternativeURIs.pop_front();
+ NewURI = AltUri.URI;
+ auto &CustomFields = ModifyCustomFields();
+ for (auto const &f : AltUri.changefields)
+ {
+ if (f.second.empty())
+ CustomFields.erase(f.first);
+ else
+ CustomFields[f.first] = f.second;
+ }
+ return true;
+}
+ /*}}}*/
+bool pkgAcquire::Item::IsGoodAlternativeURI(std::string const &AltUri) const/*{{{*/
+{
+ return std::find(d->PastRedirections.cbegin(), d->PastRedirections.cend(), AltUri) == d->PastRedirections.cend() &&
+ std::find(d->BadAlternativeSites.cbegin(), d->BadAlternativeSites.cend(), URI::SiteOnly(AltUri)) == d->BadAlternativeSites.cend();
+}
+ /*}}}*/
+void pkgAcquire::Item::PushAlternativeURI(std::string &&NewURI, std::unordered_map<std::string, std::string> &&fields, bool const at_the_back) /*{{{*/
+{
+ if (IsGoodAlternativeURI(NewURI) == false)
+ return;
+ if (at_the_back)
+ d->AlternativeURIs.emplace_back(std::move(NewURI), std::move(fields));
+ else
+ d->AlternativeURIs.emplace_front(std::move(NewURI), std::move(fields));
+}
+ /*}}}*/
+void pkgAcquire::Item::RemoveAlternativeSite(std::string &&OldSite) /*{{{*/
+{
+ d->AlternativeURIs.erase(std::remove_if(d->AlternativeURIs.begin(), d->AlternativeURIs.end(),
+ [&](decltype(*d->AlternativeURIs.cbegin()) AltUri) {
+ return URI::SiteOnly(AltUri.URI) == OldSite;
+ }),
+ d->AlternativeURIs.end());
+ d->BadAlternativeSites.push_back(std::move(OldSite));
+}
+ /*}}}*/
+unsigned int &pkgAcquire::Item::ModifyRetries() /*{{{*/
+{
+ return d->Retries;
+}
+ /*}}}*/
+std::string pkgAcquire::Item::ShortDesc() const /*{{{*/
+{
+ return DescURI();
+}
+ /*}}}*/
+void pkgAcquire::Item::Finished() /*{{{*/
+{
+}
+ /*}}}*/
+APT_PURE pkgAcquire * pkgAcquire::Item::GetOwner() const /*{{{*/
+{
+ return Owner;
+}
+ /*}}}*/
+APT_PURE pkgAcquire::ItemDesc &pkgAcquire::Item::GetItemDesc() /*{{{*/
+{
+ return Desc;
+}
+ /*}}}*/
+APT_PURE bool pkgAcquire::Item::IsTrusted() const /*{{{*/
+{
+ return false;
+}
+ /*}}}*/
+// Acquire::Item::Failed - Item failed to download /*{{{*/
+// ---------------------------------------------------------------------
+/* We return to an idle state if there are still other queues that could
+ fetch this object */
+static void formatHashsum(std::ostream &out, HashString const &hs)
+{
+ auto const type = hs.HashType();
+ if (type == "Checksum-FileSize")
+ out << " - Filesize";
+ else
+ out << " - " << type;
+ out << ':' << hs.HashValue();
+ if (hs.usable() == false)
+ out << " [weak]";
+ out << std::endl;
+}
+void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
+{
+ if (QueueCounter <= 1)
+ {
+ /* This indicates that the file is not available right now but might
+ be sometime later. If we do a retry cycle then this should be
+ retried [CDROMs] */
+ if (Cnf != NULL && Cnf->LocalOnly == true &&
+ StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
+ {
+ Status = StatIdle;
+ Dequeue();
+ return;
+ }
+
+ switch (Status)
+ {
+ case StatIdle:
+ case StatFetching:
+ case StatDone:
+ Status = StatError;
+ break;
+ case StatAuthError:
+ case StatError:
+ case StatTransientNetworkError:
+ break;
+ }
+ Complete = false;
+ Dequeue();
+ }
+
+ FailMessage(Message);
+
+ if (QueueCounter > 1)
+ Status = StatIdle;
+}
+void pkgAcquire::Item::FailMessage(string const &Message)
+{
+ string const FailReason = LookupTag(Message, "FailReason");
+ enum { MAXIMUM_SIZE_EXCEEDED, HASHSUM_MISMATCH, WEAK_HASHSUMS, REDIRECTION_LOOP, OTHER } failreason = OTHER;
+ if ( FailReason == "MaximumSizeExceeded")
+ failreason = MAXIMUM_SIZE_EXCEEDED;
+ else if ( FailReason == "WeakHashSums")
+ failreason = WEAK_HASHSUMS;
+ else if (FailReason == "RedirectionLoop")
+ failreason = REDIRECTION_LOOP;
+ else if (Status == StatAuthError)
+ failreason = HASHSUM_MISMATCH;
+
+ if(ErrorText.empty())
+ {
+ std::ostringstream out;
+ switch (failreason)
+ {
+ case HASHSUM_MISMATCH:
+ out << _("Hash Sum mismatch") << std::endl;
+ break;
+ case WEAK_HASHSUMS:
+ out << _("Insufficient information available to perform this download securely") << std::endl;
+ break;
+ case REDIRECTION_LOOP:
+ out << "Redirection loop encountered" << std::endl;
+ break;
+ case MAXIMUM_SIZE_EXCEEDED:
+ out << LookupTag(Message, "Message") << std::endl;
+ break;
+ case OTHER:
+ out << LookupTag(Message, "Message");
+ break;
+ }
+
+ if (Status == StatAuthError)
+ {
+ auto const ExpectedHashes = GetExpectedHashes();
+ if (ExpectedHashes.empty() == false)
+ {
+ out << "Hashes of expected file:" << std::endl;
+ for (auto const &hs: ExpectedHashes)
+ formatHashsum(out, hs);
+ }
+ if (failreason == HASHSUM_MISMATCH)
+ {
+ out << "Hashes of received file:" << std::endl;
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+ {
+ std::string const tagname = std::string(*type) + "-Hash";
+ std::string const hashsum = LookupTag(Message, tagname.c_str());
+ if (hashsum.empty() == false)
+ formatHashsum(out, HashString(*type, hashsum));
+ }
+ }
+ auto const lastmod = LookupTag(Message, "Last-Modified", "");
+ if (lastmod.empty() == false)
+ out << "Last modification reported: " << lastmod << std::endl;
+ }
+ ErrorText = out.str();
+ }
+
+ switch (failreason)
+ {
+ case MAXIMUM_SIZE_EXCEEDED: RenameOnError(MaximumSizeExceeded); break;
+ case HASHSUM_MISMATCH: RenameOnError(HashSumMismatch); break;
+ case WEAK_HASHSUMS: break;
+ case REDIRECTION_LOOP: break;
+ case OTHER: break;
+ }
+
+ if (FailReason.empty() == false)
+ ReportMirrorFailureToCentral(*this, FailReason, ErrorText);
+ else
+ ReportMirrorFailureToCentral(*this, ErrorText, ErrorText);
+}
+ /*}}}*/
+// Acquire::Item::Start - Item has begun to download /*{{{*/
+// ---------------------------------------------------------------------
+/* Stash status and the file size. Note that setting Complete means
+ sub-phases of the acquire process such as decompresion are operating */
+void pkgAcquire::Item::Start(string const &/*Message*/, unsigned long long const Size)
+{
+ Status = StatFetching;
+ ErrorText.clear();
+ if (FileSize == 0 && Complete == false)
+ FileSize = Size;
+}
+ /*}}}*/
+// Acquire::Item::VerifyDone - check if Item was downloaded OK /*{{{*/
+/* Note that hash-verification is 'hardcoded' in acquire-worker and has
+ * already passed if this method is called. */
+bool pkgAcquire::Item::VerifyDone(std::string const &Message,
+ pkgAcquire::MethodConfig const * const /*Cnf*/)
+{
+ std::string const FileName = LookupTag(Message,"Filename");
+ if (FileName.empty() == true)
+ {
+ Status = StatError;
+ ErrorText = "Method gave a blank filename";
+ return false;
+ }
+
+ return true;
+}
+ /*}}}*/
+// Acquire::Item::Done - Item downloaded OK /*{{{*/
+void pkgAcquire::Item::Done(string const &/*Message*/, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const /*Cnf*/)
+{
+ // We just downloaded something..
+ if (FileSize == 0)
+ {
+ unsigned long long const downloadedSize = Hashes.FileSize();
+ if (downloadedSize != 0)
+ {
+ FileSize = downloadedSize;
+ }
+ }
+ Status = StatDone;
+ ErrorText.clear();
+ Dequeue();
+}
+ /*}}}*/
+// Acquire::Item::Rename - Rename a file /*{{{*/
+// ---------------------------------------------------------------------
+/* This helper function is used by a lot of item methods as their final
+ step */
+bool pkgAcquire::Item::Rename(string const &From,string const &To)
+{
+ if (From == To || rename(From.c_str(),To.c_str()) == 0)
+ return true;
+
+ std::string S;
+ strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
+ From.c_str(),To.c_str());
+ Status = StatError;
+ if (ErrorText.empty())
+ ErrorText = S;
+ else
+ ErrorText = ErrorText + ": " + S;
+ return false;
+}
+ /*}}}*/
+void pkgAcquire::Item::Dequeue() /*{{{*/
+{
+ d->AlternativeURIs.clear();
+ Owner->Dequeue(this);
+}
+ /*}}}*/
+bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
+{
+ if (RealFileExists(DestFile))
+ Rename(DestFile, DestFile + ".FAILED");
+
+ std::string errtext;
+ switch (error)
+ {
+ case HashSumMismatch:
+ errtext = _("Hash Sum mismatch");
+ break;
+ case SizeMismatch:
+ errtext = _("Size mismatch");
+ Status = StatAuthError;
+ break;
+ case InvalidFormat:
+ errtext = _("Invalid file format");
+ Status = StatError;
+ // do not report as usually its not the mirrors fault, but Portal/Proxy
+ break;
+ case SignatureError:
+ errtext = _("Signature error");
+ Status = StatError;
+ break;
+ case NotClearsigned:
+ strprintf(errtext, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NOSPLIT");
+ Status = StatAuthError;
+ break;
+ case MaximumSizeExceeded:
+ // the method is expected to report a good error for this
+ break;
+ case PDiffError:
+ // no handling here, done by callers
+ break;
+ }
+ if (ErrorText.empty())
+ ErrorText = errtext;
+ return false;
+}
+ /*}}}*/
+void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
+{
+ ActiveSubprocess = subprocess;
+ APT_IGNORE_DEPRECATED_PUSH
+ Mode = ActiveSubprocess.c_str();
+ APT_IGNORE_DEPRECATED_POP
+}
+ /*}}}*/
+// Acquire::Item::ReportMirrorFailure /*{{{*/
+void pkgAcquire::Item::ReportMirrorFailure(std::string const &FailCode)
+{
+ ReportMirrorFailureToCentral(*this, FailCode, FailCode);
+}
+ /*}}}*/
+std::string pkgAcquire::Item::HashSum() const /*{{{*/
+{
+ HashStringList const hashes = GetExpectedHashes();
+ HashString const * const hs = hashes.find(NULL);
+ return hs != NULL ? hs->toStr() : "";
+}
+ /*}}}*/
+bool pkgAcquire::Item::IsRedirectionLoop(std::string const &NewURI) /*{{{*/
+{
+ // store can fail due to permission errors and the item will "loop" then
+ if (APT::String::Startswith(NewURI, "store:"))
+ return false;
+ if (d->PastRedirections.empty())
+ {
+ d->PastRedirections.push_back(NewURI);
+ return false;
+ }
+ auto const LastURI = std::prev(d->PastRedirections.end());
+ // redirections to the same file are a way of restarting/resheduling,
+ // individual methods will have to make sure that they aren't looping this way
+ if (*LastURI == NewURI)
+ return false;
+ if (std::find(d->PastRedirections.begin(), LastURI, NewURI) != LastURI)
+ return true;
+ d->PastRedirections.push_back(NewURI);
+ return false;
+}
+ /*}}}*/
+int pkgAcquire::Item::Priority() /*{{{*/
+{
+ // Stage 0: Files requested by methods
+ // - they will usually not end up here, but if they do we make sure
+ // to get them as soon as possible as they are probably blocking
+ // the processing of files by the requesting method
+ if (dynamic_cast<pkgAcqAuxFile *>(this) != nullptr)
+ return 5000;
+ // Stage 1: Meta indices and diff indices
+ // - those need to be fetched first to have progress reporting working
+ // for the rest
+ if (dynamic_cast<pkgAcqMetaSig*>(this) != nullptr
+ || dynamic_cast<pkgAcqMetaBase*>(this) != nullptr
+ || dynamic_cast<pkgAcqDiffIndex*>(this) != nullptr)
+ return 1000;
+ // Stage 2: Diff files
+ // - fetch before complete indexes so we can apply the diffs while fetching
+ // larger files.
+ if (dynamic_cast<pkgAcqIndexDiffs*>(this) != nullptr ||
+ dynamic_cast<pkgAcqIndexMergeDiffs*>(this) != nullptr)
+ return 800;
+
+ // Stage 3: The rest - complete index files and other stuff
+ return 500;
+}
+ /*}}}*/
+
+pkgAcqTransactionItem::pkgAcqTransactionItem(pkgAcquire * const Owner, /*{{{*/
+ pkgAcqMetaClearSig * const transactionManager, IndexTarget const &target) :
+ pkgAcquire::Item(Owner), d(NULL), Target(target), TransactionManager(transactionManager)
+{
+ if (TransactionManager != this)
+ TransactionManager->Add(this);
+ ModifyCustomFields() = {
+ {"Target-Site", Target.Option(IndexTarget::SITE)},
+ {"Target-Repo-URI", Target.Option(IndexTarget::REPO_URI)},
+ {"Target-Base-URI", Target.Option(IndexTarget::BASE_URI)},
+ {"Target-Component", Target.Option(IndexTarget::COMPONENT)},
+ {"Target-Release", Target.Option(IndexTarget::RELEASE)},
+ {"Target-Architecture", Target.Option(IndexTarget::ARCHITECTURE)},
+ {"Target-Language", Target.Option(IndexTarget::LANGUAGE)},
+ {"Target-Type", "index"},
+ };
+}
+ /*}}}*/
+pkgAcqTransactionItem::~pkgAcqTransactionItem() /*{{{*/
+{
+}
+ /*}}}*/
+HashStringList pkgAcqTransactionItem::GetExpectedHashesFor(std::string const &MetaKey) const /*{{{*/
+{
+ return GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, MetaKey);
+}
+ /*}}}*/
+
+static void LoadLastMetaIndexParser(pkgAcqMetaClearSig * const TransactionManager, std::string const &FinalRelease, std::string const &FinalInRelease)/*{{{*/
+{
+ if (TransactionManager->IMSHit == true)
+ return;
+ if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
+ {
+ TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
+ if (TransactionManager->LastMetaIndexParser != NULL)
+ {
+ _error->PushToStack();
+ if (RealFileExists(FinalInRelease))
+ TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
+ else
+ TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
+ // its unlikely to happen, but if what we have is bad ignore it
+ if (_error->PendingError())
+ {
+ delete TransactionManager->LastMetaIndexParser;
+ TransactionManager->LastMetaIndexParser = NULL;
+ }
+ _error->RevertToStack();
+ }
+ }
+}
+ /*}}}*/
+
+// AcqMetaBase - Constructor /*{{{*/
+pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &DataTarget)
+: pkgAcqTransactionItem(Owner, TransactionManager, DataTarget), d(NULL),
+ AuthPass(false), IMSHit(false), State(TransactionStarted)
+{
+}
+ /*}}}*/
+// AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
+void pkgAcqMetaBase::Add(pkgAcqTransactionItem * const I)
+{
+ Transaction.push_back(I);
+}
+ /*}}}*/
+// AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
+void pkgAcqMetaBase::AbortTransaction()
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "AbortTransaction: " << TransactionManager << std::endl;
+
+ switch (TransactionManager->State)
+ {
+ case TransactionStarted: break;
+ case TransactionAbort: _error->Fatal("Transaction %s was already aborted and is aborted again", TransactionManager->Target.URI.c_str()); return;
+ case TransactionCommit: _error->Fatal("Transaction %s was already aborted and is now committed", TransactionManager->Target.URI.c_str()); return;
+ }
+ TransactionManager->State = TransactionAbort;
+ TransactionManager->ExpectedAdditionalItems = 0;
+
+ // ensure the toplevel is in error state too
+ for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
+ I != Transaction.end(); ++I)
+ {
+ (*I)->ExpectedAdditionalItems = 0;
+ if ((*I)->Status != pkgAcquire::Item::StatFetching)
+ (*I)->Dequeue();
+ (*I)->TransactionState(TransactionAbort);
+ }
+ Transaction.clear();
+}
+ /*}}}*/
+// AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
+APT_PURE bool pkgAcqMetaBase::TransactionHasError() const
+{
+ for (std::vector<pkgAcqTransactionItem*>::const_iterator I = Transaction.begin();
+ I != Transaction.end(); ++I)
+ {
+ switch((*I)->Status) {
+ case StatDone: break;
+ case StatIdle: break;
+ case StatAuthError: return true;
+ case StatError: return true;
+ case StatTransientNetworkError: return true;
+ case StatFetching: break;
+ }
+ }
+ return false;
+}
+ /*}}}*/
+// AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
+void pkgAcqMetaBase::CommitTransaction()
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "CommitTransaction: " << this << std::endl;
+
+ switch (TransactionManager->State)
+ {
+ case TransactionStarted: break;
+ case TransactionAbort: _error->Fatal("Transaction %s was already committed and is now aborted", TransactionManager->Target.URI.c_str()); return;
+ case TransactionCommit: _error->Fatal("Transaction %s was already committed and is again committed", TransactionManager->Target.URI.c_str()); return;
+ }
+ TransactionManager->State = TransactionCommit;
+
+ // move new files into place *and* remove files that are not
+ // part of the transaction but are still on disk
+ for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
+ I != Transaction.end(); ++I)
+ {
+ (*I)->TransactionState(TransactionCommit);
+ }
+ Transaction.clear();
+}
+ /*}}}*/
+// AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
+void pkgAcqMetaBase::TransactionStageCopy(pkgAcqTransactionItem * const I,
+ const std::string &From,
+ const std::string &To)
+{
+ I->PartialFile = From;
+ I->DestFile = To;
+}
+ /*}}}*/
+// AcqMetaBase::TransactionStageRemoval - Stage a file for removal /*{{{*/
+void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I,
+ const std::string &FinalFile)
+{
+ I->PartialFile = "";
+ I->DestFile = FinalFile;
+}
+ /*}}}*/
+// AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
+/* This method is called from ::Failed handlers. If it returns true,
+ no fallback to other files or modi is performed */
+bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message)
+{
+ string const Final = I->GetFinalFilename();
+ std::string const GPGError = LookupTag(Message, "Message");
+ if (FileExists(Final))
+ {
+ I->Status = StatTransientNetworkError;
+ _error->Warning(_("An error occurred during the signature verification. "
+ "The repository is not updated and the previous index files will be used. "
+ "GPG error: %s: %s"),
+ Desc.Description.c_str(),
+ GPGError.c_str());
+ RunScripts("APT::Update::Auth-Failure");
+ return true;
+ } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
+ /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
+ _error->Error(_("GPG error: %s: %s"),
+ Desc.Description.c_str(),
+ GPGError.c_str());
+ I->Status = StatAuthError;
+ return true;
+ } else {
+ _error->Warning(_("GPG error: %s: %s"),
+ Desc.Description.c_str(),
+ GPGError.c_str());
+ }
+ // gpgv method failed
+ ReportMirrorFailureToCentral(*this, "GPGFailure", GPGError);
+ return false;
+}
+ /*}}}*/
+// AcqMetaBase::Custom600Headers - Get header for AcqMetaBase /*{{{*/
+// ---------------------------------------------------------------------
+string pkgAcqMetaBase::Custom600Headers() const
+{
+ std::string Header = pkgAcqTransactionItem::Custom600Headers();
+ Header.append("\nIndex-File: true");
+ std::string MaximumSize;
+ strprintf(MaximumSize, "\nMaximum-Size: %i",
+ _config->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
+ Header += MaximumSize;
+
+ string const FinalFile = GetFinalFilename();
+ struct stat Buf;
+ if (stat(FinalFile.c_str(),&Buf) == 0)
+ Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
+
+ return Header;
+}
+ /*}}}*/
+// AcqMetaBase::QueueForSignatureVerify /*{{{*/
+void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature)
+{
+ AuthPass = true;
+ I->Desc.URI = "gpgv:" + Signature;
+ I->DestFile = File;
+ QueueURI(I->Desc);
+ I->SetActiveSubprocess("gpgv");
+}
+ /*}}}*/
+// AcqMetaBase::CheckDownloadDone /*{{{*/
+bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const
+{
+ // We have just finished downloading a Release file (it is not
+ // verified yet)
+
+ // Save the final base URI we got this Release file from
+ if (I->UsedMirror.empty() == false && _config->FindB("Acquire::SameMirrorForAllIndexes", true))
+ {
+ auto InReleasePath = Target.Option(IndexTarget::INRELEASE_PATH);
+ if (InReleasePath.empty())
+ InReleasePath = "InRelease";
+
+ if (APT::String::Endswith(I->Desc.URI, InReleasePath))
+ {
+ TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - InReleasePath.length());
+ TransactionManager->UsedMirror = I->UsedMirror;
+ }
+ else if (APT::String::Endswith(I->Desc.URI, "Release"))
+ {
+ TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("Release"));
+ TransactionManager->UsedMirror = I->UsedMirror;
+ }
+ }
+
+ std::string const FileName = LookupTag(Message,"Filename");
+ if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
+ {
+ I->Local = true;
+ I->Desc.URI = "copy:" + FileName;
+ I->QueueURI(I->Desc);
+ return false;
+ }
+
+ // make sure to verify against the right file on I-M-S hit
+ bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
+ if (IMSHit == false && Hashes.usable())
+ {
+ // detect IMS-Hits servers haven't detected by Hash comparison
+ std::string const FinalFile = I->GetFinalFilename();
+ if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
+ {
+ IMSHit = true;
+ RemoveFile("CheckDownloadDone", I->DestFile);
+ }
+ }
+
+ if(IMSHit == true)
+ {
+ // for simplicity, the transaction manager is always InRelease
+ // even if it doesn't exist.
+ TransactionManager->IMSHit = true;
+ I->PartialFile = I->DestFile = I->GetFinalFilename();
+ }
+
+ // set Item to complete as the remaining work is all local (verify etc)
+ I->Complete = true;
+
+ return true;
+}
+ /*}}}*/
+bool pkgAcqMetaBase::CheckAuthDone(string const &Message, pkgAcquire::MethodConfig const *const Cnf) /*{{{*/
+{
+ /* If we work with a recent version of our gpgv method, we expect that it tells us
+ which key(s) have signed the file so stuff like CVE-2018-0501 is harder in the future */
+ if (Cnf->Version != "1.0" && LookupTag(Message, "Signed-By").empty())
+ {
+ std::string errmsg;
+ strprintf(errmsg, "Internal Error: Signature on %s seems good, but expected details are missing! (%s)", Target.URI.c_str(), "Signed-By");
+ if (ErrorText.empty())
+ ErrorText = errmsg;
+ Status = StatAuthError;
+ return _error->Error("%s", errmsg.c_str());
+ }
+
+ // At this point, the gpgv method has succeeded, so there is a
+ // valid signature from a key in the trusted keyring. We
+ // perform additional verification of its contents, and use them
+ // to verify the indexes we are about to download
+ if (_config->FindB("Debug::pkgAcquire::Auth", false))
+ std::cerr << "Signature verification succeeded: " << DestFile << std::endl;
+
+ if (TransactionManager->IMSHit == false)
+ {
+ // open the last (In)Release if we have it
+ std::string const FinalFile = GetFinalFilename();
+ std::string FinalRelease;
+ std::string FinalInRelease;
+ if (APT::String::Endswith(FinalFile, "InRelease"))
+ {
+ FinalInRelease = FinalFile;
+ FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
+ }
+ else
+ {
+ FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
+ FinalRelease = FinalFile;
+ }
+ LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
+ }
+
+ bool const GoodAuth = TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText);
+ if (GoodAuth == false && AllowInsecureRepositories(InsecureType::WEAK, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == false)
+ {
+ Status = StatAuthError;
+ return false;
+ }
+
+ if (!VerifyVendor(Message))
+ {
+ Status = StatAuthError;
+ return false;
+ }
+
+ // Download further indexes with verification
+ TransactionManager->QueueIndexes(GoodAuth);
+
+ return GoodAuth;
+}
+ /*}}}*/
+void pkgAcqMetaClearSig::QueueIndexes(bool const verify) /*{{{*/
+{
+ // at this point the real Items are loaded in the fetcher
+ ExpectedAdditionalItems = 0;
+
+ std::unordered_set<std::string> targetsSeen, componentsSeen;
+ bool const hasReleaseFile = TransactionManager->MetaIndexParser != NULL;
+ bool hasHashes = true;
+ auto IndexTargets = TransactionManager->MetaIndexParser->GetIndexTargets();
+ if (hasReleaseFile && verify == false)
+ hasHashes = std::any_of(IndexTargets.begin(), IndexTargets.end(),
+ [&](IndexTarget const &Target) { return TransactionManager->MetaIndexParser->Exists(Target.MetaKey); });
+ if (_config->FindB("Acquire::IndexTargets::Randomized", true) && likely(IndexTargets.empty() == false))
+ {
+ /* For fallback handling and to have some reasonable progress information
+ we can't randomize everything, but at least the order in the same type
+ can be as we shouldn't be telling the mirrors (and everyone else watching)
+ which is native/foreign arch, specific order of preference of translations, … */
+ auto range_start = IndexTargets.begin();
+ auto seed = (std::chrono::high_resolution_clock::now().time_since_epoch() / std::chrono::nanoseconds(1)) ^ getpid();
+ std::default_random_engine g(seed);
+ do {
+ auto const type = range_start->Option(IndexTarget::CREATED_BY);
+ auto const range_end = std::find_if_not(range_start, IndexTargets.end(),
+ [&type](IndexTarget const &T) { return type == T.Option(IndexTarget::CREATED_BY); });
+ std::shuffle(range_start, range_end, g);
+ range_start = range_end;
+ } while (range_start != IndexTargets.end());
+ }
+ /* Collect all components for which files exist to prevent apt from warning users
+ about "hidden" components for which not all files exist like main/debian-installer
+ and Translation files */
+ if (hasReleaseFile == true)
+ for (auto const &Target : IndexTargets)
+ if (TransactionManager->MetaIndexParser->Exists(Target.MetaKey))
+ {
+ auto component = Target.Option(IndexTarget::COMPONENT);
+ if (component.empty() == false)
+ componentsSeen.emplace(std::move(component));
+ }
+
+ for (auto&& Target: IndexTargets)
+ {
+ // if we have seen a target which is created-by a target this one here is declared a
+ // fallback to, we skip acquiring the fallback (but we make sure we clean up)
+ if (targetsSeen.find(Target.Option(IndexTarget::FALLBACK_OF)) != targetsSeen.end())
+ {
+ targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
+ new CleanupItem(Owner, TransactionManager, Target);
+ continue;
+ }
+ // all is an implementation detail. Users shouldn't use this as arch
+ // We need this support trickery here as e.g. Debian has binary-all files already,
+ // but arch:all packages are still in the arch:any files, so we would waste precious
+ // download time, bandwidth and diskspace for nothing, BUT Debian doesn't feature all
+ // in the set of supported architectures, so we can filter based on this property rather
+ // than invent an entirely new flag we would need to carry for all of eternity.
+ if (hasReleaseFile && Target.Option(IndexTarget::ARCHITECTURE) == "all")
+ {
+ if (TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(Target) == false)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ continue;
+ }
+ }
+
+ bool trypdiff = Target.OptionBool(IndexTarget::PDIFFS);
+ if (hasReleaseFile == true)
+ {
+ if (TransactionManager->MetaIndexParser->Exists(Target.MetaKey) == false)
+ {
+ auto const component = Target.Option(IndexTarget::COMPONENT);
+ if (component.empty() == false &&
+ componentsSeen.find(component) == std::end(componentsSeen) &&
+ TransactionManager->MetaIndexParser->HasSupportForComponent(component) == false)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' doesn't have the component '%s' (component misspelt in sources.list?)"),
+ Target.MetaKey.c_str(), TransactionManager->Target.Description.c_str(), component.c_str());
+ continue;
+
+ }
+
+ // optional targets that we do not have in the Release file are skipped
+ if (hasHashes == true && Target.IsOptional)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ continue;
+ }
+
+ std::string const &arch = Target.Option(IndexTarget::ARCHITECTURE);
+ if (arch.empty() == false)
+ {
+ if (TransactionManager->MetaIndexParser->IsArchitectureSupported(arch) == false)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ _error->Notice(_("Skipping acquire of configured file '%s' as repository '%s' doesn't support architecture '%s'"),
+ Target.MetaKey.c_str(), TransactionManager->Target.Description.c_str(), arch.c_str());
+ continue;
+ }
+ // if the architecture is officially supported but currently no packages for it available,
+ // ignore silently as this is pretty much the same as just shipping an empty file.
+ // if we don't know which architectures are supported, we do NOT ignore it to notify user about this
+ if (hasHashes == true && TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ continue;
+ }
+ }
+
+ if (hasHashes == true)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' does not seem to provide it (sources.list entry misspelt?)"),
+ Target.MetaKey.c_str(), TransactionManager->Target.Description.c_str());
+ continue;
+ }
+ else
+ {
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+ continue;
+ }
+ }
+ else if (verify)
+ {
+ auto const hashes = GetExpectedHashesFor(Target.MetaKey);
+ if (hashes.empty() == false)
+ {
+ if (hashes.usable() == false && TargetIsAllowedToBe(TransactionManager->Target, InsecureType::WEAK) == false)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
+ Target.MetaKey.c_str(), TransactionManager->Target.Description.c_str());
+ continue;
+ }
+ // empty files are skipped as acquiring the very small compressed files is a waste of time
+ else if (hashes.FileSize() == 0)
+ {
+ new CleanupItem(Owner, TransactionManager, Target);
+ targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
+ continue;
+ }
+ }
+ }
+
+ // autoselect the compression method
+ std::vector<std::string> types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
+ types.erase(std::remove_if(types.begin(), types.end(), [&](std::string const &t) {
+ if (t == "uncompressed")
+ return TransactionManager->MetaIndexParser->Exists(Target.MetaKey) == false;
+ std::string const MetaKey = Target.MetaKey + "." + t;
+ return TransactionManager->MetaIndexParser->Exists(MetaKey) == false;
+ }), types.end());
+ if (types.empty() == false)
+ {
+ std::ostringstream os;
+ std::copy(types.begin(), types.end()-1, std::ostream_iterator<std::string>(os, " "));
+ os << *types.rbegin();
+ Target.Options["COMPRESSIONTYPES"] = os.str();
+ }
+ else
+ Target.Options["COMPRESSIONTYPES"].clear();
+
+ std::string filename = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
+ if (filename.empty() == false)
+ {
+ // if the Release file is a hit and we have an index it must be the current one
+ if (TransactionManager->IMSHit == true)
+ ;
+ else if (TransactionManager->LastMetaIndexParser != NULL)
+ {
+ // see if the file changed since the last Release file
+ // we use the uncompressed files as we might compress differently compared to the server,
+ // so the hashes might not match, even if they contain the same data.
+ HashStringList const newFile = GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, Target.MetaKey);
+ HashStringList const oldFile = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
+ if (newFile != oldFile)
+ filename.clear();
+ }
+ else
+ filename.clear();
+ }
+ else
+ trypdiff = false; // no file to patch
+
+ if (filename.empty() == false)
+ {
+ new NoActionItem(Owner, Target, filename);
+ std::string const idxfilename = GetFinalFileNameFromURI(GetDiffIndexURI(Target));
+ if (FileExists(idxfilename))
+ new NoActionItem(Owner, Target, idxfilename);
+ targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
+ continue;
+ }
+
+ // check if we have patches available
+ trypdiff &= TransactionManager->MetaIndexParser->Exists(GetDiffIndexFileName(Target.MetaKey));
+ }
+ else
+ {
+ // if we have no file to patch, no point in trying
+ trypdiff &= (GetExistingFilename(GetFinalFileNameFromURI(Target.URI)).empty() == false);
+ }
+
+ // no point in patching from local sources
+ if (trypdiff)
+ {
+ std::string const proto = Target.URI.substr(0, strlen("file:/"));
+ if (proto == "file:/" || proto == "copy:/" || proto == "cdrom:")
+ trypdiff = false;
+ }
+
+ // Queue the Index file (Packages, Sources, Translation-$foo, …)
+ targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
+ if (trypdiff)
+ new pkgAcqDiffIndex(Owner, TransactionManager, Target);
+ else
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+ }
+}
+ /*}}}*/
+bool pkgAcqMetaBase::VerifyVendor(string const &) /*{{{*/
+{
+ if (TransactionManager->MetaIndexParser->GetValidUntil() > 0)
+ {
+ time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
+ if (invalid_since > 0)
+ {
+ std::string errmsg;
+ strprintf(errmsg,
+ // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
+ // the time since then the file is invalid - formatted in the same way as in
+ // the download progress display (e.g. 7d 3h 42min 1s)
+ _("Release file for %s is expired (invalid since %s). "
+ "Updates for this repository will not be applied."),
+ Target.URI.c_str(), TimeToStr(invalid_since).c_str());
+ if (ErrorText.empty())
+ ErrorText = errmsg;
+ return _error->Error("%s", errmsg.c_str());
+ }
+ }
+
+ if (TransactionManager->MetaIndexParser->GetNotBefore() > 0)
+ {
+ time_t const invalid_for = TransactionManager->MetaIndexParser->GetNotBefore() - time(nullptr);
+ if (invalid_for > 0)
+ {
+ std::string errmsg;
+ strprintf(errmsg,
+ // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
+ // the time until the file will be valid - formatted in the same way as in
+ // the download progress display (e.g. 7d 3h 42min 1s)
+ _("Release file for %s is not valid yet (invalid for another %s). "
+ "Updates for this repository will not be applied."),
+ Target.URI.c_str(), TimeToStr(invalid_for).c_str());
+ if (ErrorText.empty())
+ ErrorText = errmsg;
+ return _error->Error("%s", errmsg.c_str());
+ }
+ }
+
+ /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
+ as a prevention of downgrading us to older (still valid) files */
+ if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
+ TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
+ {
+ TransactionManager->IMSHit = true;
+ RemoveFile("VerifyVendor", DestFile);
+ PartialFile = DestFile = GetFinalFilename();
+ // load the 'old' file in the 'new' one instead of flipping pointers as
+ // the new one isn't owned by us, while the old one is so cleanup would be confused.
+ TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
+ delete TransactionManager->LastMetaIndexParser;
+ TransactionManager->LastMetaIndexParser = NULL;
+ }
+
+ if (_config->FindB("Debug::pkgAcquire::Auth", false))
+ {
+ std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
+ std::cerr << "Got Suite: " << TransactionManager->MetaIndexParser->GetSuite() << std::endl;
+ std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
+ }
+
+ // One day that might become fatal…
+ auto const ExpectedDist = TransactionManager->MetaIndexParser->GetExpectedDist();
+ auto const NowCodename = TransactionManager->MetaIndexParser->GetCodename();
+ if (TransactionManager->MetaIndexParser->CheckDist(ExpectedDist) == false)
+ _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
+ Desc.Description.c_str(), ExpectedDist.c_str(), NowCodename.c_str());
+
+ // changed info potentially breaks user config like pinning
+ if (TransactionManager->LastMetaIndexParser != nullptr)
+ {
+ std::vector<pkgAcquireStatus::ReleaseInfoChange> Changes;
+ auto const AllowInfoChange = _config->FindB("Acquire::AllowReleaseInfoChange", false);
+ auto const quietInfoChange = _config->FindB("quiet::ReleaseInfoChange", false);
+ struct {
+ char const * const Type;
+ bool const Allowed;
+ decltype(&metaIndex::GetOrigin) const Getter;
+ } checkers[] = {
+ { "Origin", AllowInfoChange, &metaIndex::GetOrigin },
+ { "Label", AllowInfoChange, &metaIndex::GetLabel },
+ { "Version", true, &metaIndex::GetVersion }, // numbers change all the time, that is okay
+ { "Suite", true, &metaIndex::GetSuite },
+ { "Codename", AllowInfoChange, &metaIndex::GetCodename },
+ { nullptr, false, nullptr }
+ };
+ auto const CheckReleaseInfo = [&](char const * const Type, bool const AllowChange, decltype(checkers[0].Getter) const Getter) {
+ std::string const Last = (TransactionManager->LastMetaIndexParser->*Getter)();
+ std::string const Now = (TransactionManager->MetaIndexParser->*Getter)();
+ if (Last == Now)
+ return;
+ auto const Allow = _config->FindB(std::string("Acquire::AllowReleaseInfoChange::").append(Type), AllowChange);
+ if (Allow == true && _config->FindB(std::string("quiet::ReleaseInfoChange::").append(Type), quietInfoChange) == true)
+ return;
+ std::string msg;
+ strprintf(msg, _("Repository '%s' changed its '%s' value from '%s' to '%s'"),
+ Desc.Description.c_str(), Type, Last.c_str(), Now.c_str());
+ Changes.push_back({Type, std::move(Last), std::move(Now), std::move(msg), Allow});
+ };
+ for (short i = 0; checkers[i].Type != nullptr; ++i)
+ CheckReleaseInfo(checkers[i].Type, checkers[i].Allowed, checkers[i].Getter);
+
+ {
+ auto const Last = TransactionManager->LastMetaIndexParser->GetDefaultPin();
+ auto const Now = TransactionManager->MetaIndexParser->GetDefaultPin();
+ if (Last != Now)
+ {
+ auto const Allow = _config->FindB("Acquire::AllowReleaseInfoChange::DefaultPin", AllowInfoChange);
+ if (Allow == false || _config->FindB("quiet::ReleaseInfoChange::DefaultPin", quietInfoChange) == false)
+ {
+ std::string msg;
+ strprintf(msg, _("Repository '%s' changed its default priority for %s from %hi to %hi."),
+ Desc.Description.c_str(), "apt_preferences(5)", Last, Now);
+ Changes.push_back({"DefaultPin", std::to_string(Last), std::to_string(Now), std::move(msg), Allow});
+ }
+ }
+ }
+ if (Changes.empty() == false)
+ {
+ auto const notes = TransactionManager->MetaIndexParser->GetReleaseNotes();
+ if (notes.empty() == false)
+ {
+ std::string msg;
+ // TRANSLATOR: the "this" refers to changes in the repository like a new release or owner change
+ strprintf(msg, _("More information about this can be found online in the Release notes at: %s"), notes.c_str());
+ Changes.push_back({"Release-Notes", "", std::move(notes), std::move(msg), true});
+ }
+ if (std::any_of(Changes.begin(),Changes.end(),[](pkgAcquireStatus::ReleaseInfoChange const &c) { return c.DefaultAction == false; }))
+ {
+ std::string msg;
+ // TRANSLATOR: %s is the name of the manpage in question, e.g. apt-secure(8)
+ strprintf(msg, _("This must be accepted explicitly before updates for "
+ "this repository can be applied. See %s manpage for details."), "apt-secure(8)");
+ Changes.push_back({"Confirmation", "", "", std::move(msg), true});
+ }
+
+ }
+ if (Owner->Log == nullptr)
+ return pkgAcquireStatus::ReleaseInfoChangesAsGlobalErrors(std::move(Changes));
+ return Owner->Log->ReleaseInfoChanges(TransactionManager->LastMetaIndexParser, TransactionManager->MetaIndexParser, std::move(Changes));
+ }
+ return true;
+}
+ /*}}}*/
+pkgAcqMetaBase::~pkgAcqMetaBase()
+{
+}
+
+pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
+ IndexTarget const &ClearsignedTarget,
+ IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
+ metaIndex * const MetaIndexParser) :
+ pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget),
+ d(NULL), DetachedDataTarget(DetachedDataTarget),
+ MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
+{
+ // index targets + (worst case:) Release/Release.gpg
+ ExpectedAdditionalItems = std::numeric_limits<decltype(ExpectedAdditionalItems)>::max();
+ TransactionManager->Add(this);
+}
+ /*}}}*/
+pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
+{
+ if (LastMetaIndexParser != NULL)
+ delete LastMetaIndexParser;
+}
+ /*}}}*/
+// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
+string pkgAcqMetaClearSig::Custom600Headers() const
+{
+ string Header = pkgAcqMetaBase::Custom600Headers();
+ Header += "\nFail-Ignore: true";
+ std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
+ if (key.empty() == false)
+ Header += "\nSigned-By: " + key;
+
+ return Header;
+}
+ /*}}}*/
+void pkgAcqMetaClearSig::Finished() /*{{{*/
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "Finished: " << DestFile <<std::endl;
+ if(TransactionManager->State == TransactionStarted &&
+ TransactionManager->TransactionHasError() == false)
+ TransactionManager->CommitTransaction();
+}
+ /*}}}*/
+bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ if (Item::VerifyDone(Message, Cnf) == false)
+ return false;
+
+ if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
+ return RenameOnError(NotClearsigned);
+
+ return true;
+}
+ /*}}}*/
+// pkgAcqMetaClearSig::Done - We got a file /*{{{*/
+void pkgAcqMetaClearSig::Done(std::string const &Message,
+ HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Done(Message, Hashes, Cnf);
+
+ if(AuthPass == false)
+ {
+ if(CheckDownloadDone(this, Message, Hashes) == true)
+ QueueForSignatureVerify(this, DestFile, DestFile);
+ return;
+ }
+ else if (CheckAuthDone(Message, Cnf) == true)
+ {
+ if (TransactionManager->IMSHit == false)
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+ else if (RealFileExists(GetFinalFilename()) == false)
+ {
+ // We got an InRelease file IMSHit, but we haven't one, which means
+ // we had a valid Release/Release.gpg combo stepping in, which we have
+ // to 'acquire' now to ensure list cleanup isn't removing them
+ new NoActionItem(Owner, DetachedDataTarget);
+ new NoActionItem(Owner, DetachedSigTarget);
+ }
+ }
+ else if (Status != StatAuthError)
+ {
+ string const FinalFile = GetFinalFileNameFromURI(DetachedDataTarget.URI);
+ string const OldFile = GetFinalFilename();
+ if (TransactionManager->IMSHit == false)
+ TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
+ else if (RealFileExists(OldFile) == false)
+ new NoActionItem(Owner, DetachedDataTarget);
+ else
+ TransactionManager->TransactionStageCopy(this, OldFile, FinalFile);
+ }
+}
+ /*}}}*/
+void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
+{
+ Item::Failed(Message, Cnf);
+
+ if (AuthPass == false)
+ {
+ if (Status == StatTransientNetworkError)
+ {
+ TransactionManager->AbortTransaction();
+ return;
+ }
+ auto const failreason = LookupTag(Message, "FailReason");
+ auto const httperror = "HttpError";
+ if (Status == StatAuthError ||
+ Target.Option(IndexTarget::INRELEASE_PATH).empty() == false || /* do not fallback if InRelease was requested */
+ (strncmp(failreason.c_str(), httperror, strlen(httperror)) == 0 &&
+ failreason != "HttpError404"))
+ {
+ // if we expected a ClearTextSignature (InRelease) but got a network
+ // error or got a file, but it wasn't valid, we end up here (see VerifyDone).
+ // As these is usually called by web-portals we do not try Release/Release.gpg
+ // as this is going to fail anyway and instead abort our try (LP#346386)
+ _error->PushToStack();
+ _error->Error(_("Failed to fetch %s %s"), Target.URI.c_str(), ErrorText.c_str());
+ if (Target.Option(IndexTarget::INRELEASE_PATH).empty() == true && AllowInsecureRepositories(InsecureType::UNSIGNED, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+ _error->RevertToStack();
+ else
+ return;
+ }
+
+ // Queue the 'old' InRelease file for removal if we try Release.gpg
+ // as otherwise the file will stay around and gives a false-auth
+ // impression (CVE-2012-0214)
+ TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
+ Status = StatDone;
+
+ new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget);
+ }
+ else
+ {
+ if(CheckStopAuthentication(this, Message))
+ return;
+
+ if(AllowInsecureRepositories(InsecureType::UNSIGNED, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+ {
+ Status = StatDone;
+
+ /* InRelease files become Release files, otherwise
+ * they would be considered as trusted later on */
+ string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
+ string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
+ string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
+ string const FinalInRelease = GetFinalFilename();
+ Rename(DestFile, PartialRelease);
+ TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
+ LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
+
+ // we parse the indexes here because at this point the user wanted
+ // a repository that may potentially harm him
+ if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
+ /* expired Release files are still a problem you need extra force for */;
+ else
+ TransactionManager->QueueIndexes(true);
+ }
+ }
+}
+ /*}}}*/
+
+pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &DataTarget,
+ IndexTarget const &DetachedSigTarget) :
+ pkgAcqMetaBase(Owner, TransactionManager, DataTarget), d(NULL),
+ DetachedSigTarget(DetachedSigTarget)
+{
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "New pkgAcqMetaIndex with TransactionManager "
+ << this->TransactionManager << std::endl;
+
+ DestFile = GetPartialFileNameFromURI(DataTarget.URI);
+
+ // Create the item
+ Desc.Description = DataTarget.Description;
+ Desc.Owner = this;
+ Desc.ShortDesc = DataTarget.ShortDesc;
+
+ // Rewrite the description URI if INRELEASE_PATH was specified so
+ // we download the specified file instead.
+ auto InReleasePath = DataTarget.Option(IndexTarget::INRELEASE_PATH);
+ if (InReleasePath.empty() == false && APT::String::Endswith(DataTarget.URI, "/InRelease"))
+ {
+ Desc.URI = DataTarget.URI.substr(0, DataTarget.URI.size() - strlen("InRelease")) + InReleasePath;
+ }
+ else
+ {
+ Desc.URI = DataTarget.URI;
+ }
+
+ QueueURI(Desc);
+}
+ /*}}}*/
+void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
+ HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
+{
+ Item::Done(Message,Hashes,Cfg);
+
+ if(CheckDownloadDone(this, Message, Hashes))
+ {
+ // we have a Release file, now download the Signature, all further
+ // verify/queue for additional downloads will be done in the
+ // pkgAcqMetaSig::Done() code
+ new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
+ }
+}
+ /*}}}*/
+// pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
+void pkgAcqMetaIndex::Failed(string const &Message,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ pkgAcquire::Item::Failed(Message, Cnf);
+ Status = StatDone;
+
+ // No Release file was present so fall
+ // back to queueing Packages files without verification
+ // only allow going further if the user explicitly wants it
+ if(AllowInsecureRepositories(InsecureType::NORELEASE, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+ {
+ // ensure old Release files are removed
+ TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
+
+ // queue without any kind of hashsum support
+ TransactionManager->QueueIndexes(false);
+ }
+}
+ /*}}}*/
+std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
+{
+ return Target.URI;
+}
+ /*}}}*/
+pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
+
+// AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
+pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target,
+ pkgAcqMetaIndex * const MetaIndex) :
+ pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
+{
+ DestFile = GetPartialFileNameFromURI(Target.URI);
+
+ // remove any partial downloaded sig-file in partial/.
+ // it may confuse proxies and is too small to warrant a
+ // partial download anyway
+ RemoveFile("pkgAcqMetaSig", DestFile);
+
+ // set the TransactionManager
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "New pkgAcqMetaSig with TransactionManager "
+ << TransactionManager << std::endl;
+
+ // Create the item
+ Desc.Description = Target.Description;
+ Desc.Owner = this;
+ Desc.ShortDesc = Target.ShortDesc;
+ Desc.URI = Target.URI;
+
+ // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
+ // so we skip the download step and go instantly to verification
+ if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
+ {
+ Complete = true;
+ Status = StatDone;
+ PartialFile = DestFile = GetFinalFilename();
+ MetaIndexFileSignature = DestFile;
+ MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
+ }
+ else
+ QueueURI(Desc);
+}
+ /*}}}*/
+pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
+{
+}
+ /*}}}*/
+// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
+std::string pkgAcqMetaSig::Custom600Headers() const
+{
+ std::string Header = pkgAcqTransactionItem::Custom600Headers();
+ std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
+ if (key.empty() == false)
+ Header += "\nSigned-By: " + key;
+ return Header;
+}
+ /*}}}*/
+// AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
+void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
+{
+ if (MetaIndexFileSignature.empty() == false)
+ {
+ DestFile = MetaIndexFileSignature;
+ MetaIndexFileSignature.clear();
+ }
+ Item::Done(Message, Hashes, Cfg);
+
+ if(MetaIndex->AuthPass == false)
+ {
+ if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
+ {
+ // destfile will be modified to point to MetaIndexFile for the
+ // gpgv method, so we need to save it here
+ MetaIndexFileSignature = DestFile;
+ MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
+ }
+ return;
+ }
+ else if (MetaIndex->CheckAuthDone(Message, Cfg) == true)
+ {
+ auto const Releasegpg = GetFinalFilename();
+ auto const Release = MetaIndex->GetFinalFilename();
+ // if this is an IMS-Hit on Release ensure we also have the Release.gpg file stored
+ // (previously an unknown pubkey) – but only if the Release file exists locally (unlikely
+ // event of InRelease removed from the mirror causing fallback but still an IMS-Hit)
+ if (TransactionManager->IMSHit == false ||
+ (FileExists(Releasegpg) == false && FileExists(Release) == true))
+ {
+ TransactionManager->TransactionStageCopy(this, DestFile, Releasegpg);
+ TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, Release);
+ }
+ }
+ else if (MetaIndex->Status != StatAuthError)
+ {
+ std::string const FinalFile = MetaIndex->GetFinalFilename();
+ if (TransactionManager->IMSHit == false)
+ TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, FinalFile);
+ else
+ TransactionManager->TransactionStageCopy(MetaIndex, FinalFile, FinalFile);
+ }
+}
+ /*}}}*/
+void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+ Item::Failed(Message,Cnf);
+
+ // check if we need to fail at this point
+ if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
+ return;
+
+ // ensures that a Release.gpg file in the lists/ is removed by the transaction
+ TransactionManager->TransactionStageRemoval(this, DestFile);
+
+ // only allow going further if the user explicitly wants it
+ if (AllowInsecureRepositories(InsecureType::UNSIGNED, MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
+ {
+ string const FinalRelease = MetaIndex->GetFinalFilename();
+ string const FinalInRelease = TransactionManager->GetFinalFilename();
+ LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
+
+ // we parse the indexes here because at this point the user wanted
+ // a repository that may potentially harm him
+ bool const GoodLoad = TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText);
+ if (MetaIndex->VerifyVendor(Message) == false)
+ /* expired Release files are still a problem you need extra force for */;
+ else
+ TransactionManager->QueueIndexes(GoodLoad);
+
+ TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, FinalRelease);
+ }
+ else if (TransactionManager->IMSHit == false)
+ Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
+
+ // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
+ if (Cnf->LocalOnly == true ||
+ StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
+ {
+ // Ignore this
+ Status = StatDone;
+ }
+}
+ /*}}}*/
+
+
+// AcqBaseIndex - Constructor /*{{{*/
+pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target)
+: pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
+{
+}
+ /*}}}*/
+void pkgAcqBaseIndex::Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+ pkgAcquire::Item::Failed(Message, Cnf);
+ if (Status != StatAuthError)
+ return;
+
+ ErrorText.append("Release file created at: ");
+ auto const timespec = TransactionManager->MetaIndexParser->GetDate();
+ if (timespec == 0)
+ ErrorText.append("<unknown>");
+ else
+ ErrorText.append(TimeRFC1123(timespec, true));
+ ErrorText.append("\n");
+}
+ /*}}}*/
+pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
+
+// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* Get the DiffIndex file first and see if there are patches available
+ * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
+ * patches. If anything goes wrong in that process, it will fall back to
+ * the original packages file
+ */
+pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target)
+ : pkgAcqIndex(Owner, TransactionManager, Target, true), d(NULL), diffs(NULL)
+{
+ // FIXME: Magic number as an upper bound on pdiffs we will reasonably acquire
+ ExpectedAdditionalItems = 40;
+ Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+
+ CompressionExtensions.clear();
+ {
+ std::vector<std::string> types = APT::Configuration::getCompressionTypes();
+ if (types.empty() == false)
+ {
+ std::ostringstream os;
+ std::copy_if(types.begin(), types.end()-1, std::ostream_iterator<std::string>(os, " "), [&](std::string const type) {
+ if (type == "uncompressed")
+ return true;
+ return TransactionManager->MetaIndexParser->Exists(GetDiffIndexFileName(Target.MetaKey) + '.' + type);
+ });
+ os << *types.rbegin();
+ CompressionExtensions = os.str();
+ }
+ }
+ Init(GetDiffIndexURI(Target), GetDiffIndexFileName(Target.Description), Target.ShortDesc);
+
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
+}
+ /*}}}*/
+void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
+{
+ // list cleanup needs to know that this file as well as the already
+ // present index is ours, so we create an empty diff to save it for us
+ new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
+}
+ /*}}}*/
+static bool RemoveFileForBootstrapLinking(std::string &ErrorText, std::string const &For, std::string const &Boot)/*{{{*/
+{
+ if (FileExists(Boot) && RemoveFile("Bootstrap-linking", Boot) == false)
+ {
+ strprintf(ErrorText, "Bootstrap for patching %s by removing stale %s failed!", For.c_str(), Boot.c_str());
+ return false;
+ }
+ return true;
+}
+ /*}}}*/
+bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
+{
+ available_patches.clear();
+ ExpectedAdditionalItems = 0;
+ // failing here is fine: our caller will take care of trying to
+ // get the complete file if patching fails
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
+ << std::endl;
+
+ FileFd Fd(IndexDiffFile, FileFd::ReadOnly, FileFd::Extension);
+ pkgTagFile TF(&Fd);
+ if (Fd.IsOpen() == false || Fd.Failed())
+ return false;
+
+ pkgTagSection Tags;
+ if(unlikely(TF.Step(Tags) == false))
+ return false;
+
+ HashStringList ServerHashes;
+ unsigned long long ServerSize = 0;
+
+ auto const &posix = std::locale::classic();
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+ {
+ std::string tagname = *type;
+ tagname.append("-Current");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
+
+ string hash;
+ unsigned long long size;
+ std::stringstream ss(tmp);
+ ss.imbue(posix);
+ ss >> hash >> size;
+ if (unlikely(hash.empty() == true))
+ continue;
+ if (unlikely(ServerSize != 0 && ServerSize != size))
+ continue;
+ ServerHashes.push_back(HashString(*type, hash));
+ ServerSize = size;
+ }
+
+ if (ServerHashes.usable() == false)
+ {
+ ErrorText = "Did not find a good hashsum in the index";
+ return false;
+ }
+
+ std::string const CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
+ HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
+ if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
+ {
+ ErrorText = "Index has different hashes than parser (probably older)";
+ return false;
+ }
+
+ HashStringList LocalHashes;
+ // try avoiding calculating the hash here as this is costly
+ if (TransactionManager->LastMetaIndexParser != NULL)
+ LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
+ if (LocalHashes.usable() == false)
+ {
+ FileFd fd(CurrentPackagesFile, FileFd::ReadOnly, FileFd::Auto);
+ Hashes LocalHashesCalc(ServerHashes);
+ LocalHashesCalc.AddFD(fd);
+ LocalHashes = LocalHashesCalc.GetHashStringList();
+ }
+
+ if (ServerHashes == LocalHashes)
+ {
+ available_patches.clear();
+ return true;
+ }
+
+ if(Debug)
+ std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
+ << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
+
+ // historically, older hashes have more info than newer ones, so start
+ // collecting with older ones first to avoid implementing complicated
+ // information merging techniques… a failure is after all always
+ // recoverable with a complete file and hashes aren't changed that often.
+ std::vector<char const *> types;
+ for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
+ types.push_back(*type);
+
+ // parse all of (provided) history
+ bool firstAcceptedHashes = true;
+ for (auto type = types.crbegin(); type != types.crend(); ++type)
+ {
+ if (LocalHashes.find(*type) == NULL)
+ continue;
+
+ std::string tagname = *type;
+ tagname.append("-History");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
+
+ string hash, filename;
+ unsigned long long size;
+ std::stringstream ss(tmp);
+ ss.imbue(posix);
+
+ while (ss >> hash >> size >> filename)
+ {
+ if (unlikely(hash.empty() == true || filename.empty() == true))
+ continue;
+
+ // see if we have a record for this file already
+ std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ for (; cur != available_patches.end(); ++cur)
+ {
+ if (cur->file != filename)
+ continue;
+ cur->result_hashes.push_back(HashString(*type, hash));
+ break;
+ }
+ if (cur != available_patches.end())
+ continue;
+ if (firstAcceptedHashes == true)
+ {
+ DiffInfo next;
+ next.file = filename;
+ next.result_hashes.push_back(HashString(*type, hash));
+ next.result_hashes.FileSize(size);
+ available_patches.push_back(next);
+ }
+ else
+ {
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
+ << " wasn't in the list for the first parsed hash! (history)" << std::endl;
+ break;
+ }
+ }
+ firstAcceptedHashes = false;
+ }
+
+ if (unlikely(available_patches.empty() == true))
+ {
+ ErrorText = "Couldn't find any patches for the patch series";
+ return false;
+ }
+
+ for (auto type = types.crbegin(); type != types.crend(); ++type)
+ {
+ if (LocalHashes.find(*type) == NULL)
+ continue;
+
+ std::string tagname = *type;
+ tagname.append("-Patches");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
+
+ string hash, filename;
+ unsigned long long size;
+ std::stringstream ss(tmp);
+ ss.imbue(posix);
+
+ while (ss >> hash >> size >> filename)
+ {
+ if (unlikely(hash.empty() == true || filename.empty() == true))
+ continue;
+
+ // see if we have a record for this file already
+ std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ for (; cur != available_patches.end(); ++cur)
+ {
+ if (cur->file != filename)
+ continue;
+ if (cur->patch_hashes.empty())
+ cur->patch_hashes.FileSize(size);
+ cur->patch_hashes.push_back(HashString(*type, hash));
+ break;
+ }
+ if (cur != available_patches.end())
+ continue;
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
+ << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
+ break;
+ }
+ }
+
+ for (auto type = types.crbegin(); type != types.crend(); ++type)
+ {
+ std::string tagname = *type;
+ tagname.append("-Download");
+ std::string const tmp = Tags.FindS(tagname.c_str());
+ if (tmp.empty() == true)
+ continue;
+
+ string hash, filename;
+ unsigned long long size;
+ std::stringstream ss(tmp);
+ ss.imbue(posix);
+
+ // FIXME: all of pdiff supports only .gz compressed patches
+ while (ss >> hash >> size >> filename)
+ {
+ if (unlikely(hash.empty() == true || filename.empty() == true))
+ continue;
+ if (unlikely(APT::String::Endswith(filename, ".gz") == false))
+ continue;
+ filename.erase(filename.length() - 3);
+
+ // see if we have a record for this file already
+ std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ for (; cur != available_patches.end(); ++cur)
+ {
+ if (cur->file != filename)
+ continue;
+ if (cur->download_hashes.empty())
+ cur->download_hashes.FileSize(size);
+ cur->download_hashes.push_back(HashString(*type, hash));
+ break;
+ }
+ if (cur != available_patches.end())
+ continue;
+ if (Debug == true)
+ std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
+ << " wasn't in the list for the first parsed hash! (download)" << std::endl;
+ break;
+ }
+ }
+
+
+ bool foundStart = false;
+ for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
+ cur != available_patches.end(); ++cur)
+ {
+ if (LocalHashes != cur->result_hashes)
+ continue;
+
+ available_patches.erase(available_patches.begin(), cur);
+ foundStart = true;
+ break;
+ }
+
+ if (foundStart == false || unlikely(available_patches.empty() == true))
+ {
+ ErrorText = "Couldn't find the start of the patch series";
+ return false;
+ }
+
+ for (auto const &patch: available_patches)
+ if (patch.result_hashes.usable() == false ||
+ patch.patch_hashes.usable() == false ||
+ patch.download_hashes.usable() == false)
+ {
+ strprintf(ErrorText, "Provides no usable hashes for %s", patch.file.c_str());
+ return false;
+ }
+
+ // patching with too many files is rather slow compared to a fast download
+ unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
+ if (fileLimit != 0 && fileLimit < available_patches.size())
+ {
+ strprintf(ErrorText, "Need %lu diffs, but limit is %lu", available_patches.size(), fileLimit);
+ return false;
+ }
+
+ // calculate the size of all patches we have to get
+ unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100);
+ if (sizeLimitPercent > 0)
+ {
+ unsigned long long downloadSize = std::accumulate(available_patches.begin(),
+ available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) {
+ return T + I.download_hashes.FileSize();
+ });
+ if (downloadSize != 0)
+ {
+ unsigned long long downloadSizeIdx = 0;
+ auto const types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
+ for (auto const &t : types)
+ {
+ std::string MetaKey = Target.MetaKey;
+ if (t != "uncompressed")
+ MetaKey += '.' + t;
+ HashStringList const hsl = GetExpectedHashesFor(MetaKey);
+ if (unlikely(hsl.usable() == false))
+ continue;
+ downloadSizeIdx = hsl.FileSize();
+ break;
+ }
+ unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent;
+ if ((sizeLimit/100) < downloadSize)
+ {
+ strprintf(ErrorText, "Need %llu compressed bytes, but limit is %llu and original is %llu", downloadSize, (sizeLimit/100), downloadSizeIdx);
+ return false;
+ }
+ }
+ }
+
+ /* decide if we should download patches one by one or in one go:
+ The first is good if the server merges patches, but many don't so client
+ based merging can be attempt in which case the second is better.
+ "bad things" will happen if patches are merged on the server,
+ but client side merging is attempt as well */
+ pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
+ if (pdiff_merge == true)
+ {
+ // reprepro adds this flag if it has merged patches on the server
+ std::string const precedence = Tags.FindS("X-Patch-Precedence");
+ pdiff_merge = (precedence != "merged");
+ }
+
+ // clean the plate
+ {
+ std::string const Final = GetExistingFilename(CurrentPackagesFile);
+ if (unlikely(Final.empty())) // because we wouldn't be called in such a case
+ return false;
+ std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
+ std::string const PatchedFile = GetKeepCompressedFileName(PartialFile + "-patched", Target);
+ if (RemoveFileForBootstrapLinking(ErrorText, CurrentPackagesFile, PartialFile) == false ||
+ RemoveFileForBootstrapLinking(ErrorText, CurrentPackagesFile, PatchedFile) == false)
+ return false;
+ for (auto const &ext : APT::Configuration::getCompressorExtensions())
+ {
+ if (RemoveFileForBootstrapLinking(ErrorText, CurrentPackagesFile, PartialFile + ext) == false ||
+ RemoveFileForBootstrapLinking(ErrorText, CurrentPackagesFile, PatchedFile + ext) == false)
+ return false;
+ }
+ std::string const Ext = Final.substr(CurrentPackagesFile.length());
+ std::string const Partial = PartialFile + Ext;
+ if (symlink(Final.c_str(), Partial.c_str()) != 0)
+ {
+ strprintf(ErrorText, "Bootstrap for patching by linking %s to %s failed!", Final.c_str(), Partial.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+ if (CommonFailed(GetDiffIndexURI(Target), Message, Cnf))
+ return;
+
+ RenameOnError(PDiffError);
+ Status = StatDone;
+ ExpectedAdditionalItems = 0;
+
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
+ << "Falling back to normal index file acquire" << std::endl;
+
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+}
+ /*}}}*/
+bool pkgAcqDiffIndex::VerifyDone(std::string const &Message, pkgAcquire::MethodConfig const * const)/*{{{*/
+{
+ string const FinalFile = GetFinalFilename();
+ if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
+ DestFile = FinalFile;
+
+ if (ParseDiffIndex(DestFile))
+ return true;
+
+ Status = StatError;
+ if (ErrorText.empty())
+ ErrorText = "Couldn't parse pdiff index";
+ return false;
+}
+ /*}}}*/
+void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
+
+ Item::Done(Message, Hashes, Cnf);
+
+ if (available_patches.empty())
+ {
+ // we have the same sha1 as the server so we are done here
+ if(Debug)
+ std::clog << "pkgAcqDiffIndex: Package file is up-to-date" << std::endl;
+ QueueOnIMSHit();
+ }
+ else
+ {
+ if (pdiff_merge == false)
+ new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
+ else
+ {
+ diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
+ for(size_t i = 0; i < available_patches.size(); ++i)
+ (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
+ Target,
+ available_patches[i],
+ diffs);
+ }
+ }
+
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+
+ Complete = true;
+ Status = StatDone;
+ Dequeue();
+
+ return;
+}
+ /*}}}*/
+pkgAcqDiffIndex::~pkgAcqDiffIndex()
+{
+ if (diffs != NULL)
+ delete diffs;
+}
+
+// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* The package diff is added to the queue. one object is constructed
+ * for each diff and the index
+ */
+pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *const Owner,
+ pkgAcqMetaClearSig *const TransactionManager,
+ IndexTarget const &Target,
+ vector<DiffInfo> const &diffs)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target),
+ available_patches(diffs)
+{
+ DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
+
+ Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+
+ Desc.Owner = this;
+ Desc.ShortDesc = Target.ShortDesc;
+
+ if(available_patches.empty() == true)
+ {
+ // we are done (yeah!), check hashes against the final file
+ DestFile = GetKeepCompressedFileName(GetFinalFileNameFromURI(Target.URI), Target);
+ Finish(true);
+ }
+ else
+ {
+ State = StateFetchDiff;
+ QueueNextDiff();
+ }
+}
+ /*}}}*/
+void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+ pkgAcqBaseIndex::Failed(Message,Cnf);
+ Status = StatDone;
+
+ DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
+ << "Falling back to normal index file acquire " << std::endl;
+ RenameOnError(PDiffError);
+ std::string const patchname = GetDiffsPatchFileName(DestFile);
+ if (RealFileExists(patchname))
+ Rename(patchname, patchname + ".FAILED");
+ std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
+ if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
+ Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+ Finish();
+}
+ /*}}}*/
+// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
+void pkgAcqIndexDiffs::Finish(bool allDone)
+{
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs::Finish(): "
+ << allDone << " "
+ << Desc.URI << std::endl;
+
+ // we restore the original name, this is required, otherwise
+ // the file will be cleaned
+ if(allDone)
+ {
+ std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
+ TransactionManager->TransactionStageCopy(this, DestFile, Final);
+
+ // this is for the "real" finish
+ Complete = true;
+ Status = StatDone;
+ Dequeue();
+ if(Debug)
+ std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
+ return;
+ }
+ else
+ DestFile.clear();
+
+ if(Debug)
+ std::clog << "Finishing: " << Desc.URI << std::endl;
+ Complete = false;
+ Status = StatDone;
+ Dequeue();
+ return;
+}
+ /*}}}*/
+bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
+{
+ // calc sha1 of the just patched file
+ std::string const PartialFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
+ if(unlikely(PartialFile.empty()))
+ {
+ Failed("Message: The file " + GetPartialFileNameFromURI(Target.URI) + " isn't available", NULL);
+ return false;
+ }
+
+ FileFd fd(PartialFile, FileFd::ReadOnly, FileFd::Extension);
+ Hashes LocalHashesCalc;
+ LocalHashesCalc.AddFD(fd);
+ HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
+
+ if(Debug)
+ std::clog << "QueueNextDiff: " << PartialFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
+
+ HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
+ if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
+ {
+ Failed("Local/Expected hashes are not usable for " + PartialFile, NULL);
+ return false;
+ }
+
+ // final file reached before all patches are applied
+ if(LocalHashes == TargetFileHashes)
+ {
+ Finish(true);
+ return true;
+ }
+
+ // remove all patches until the next matching patch is found
+ // this requires the Index file to be ordered
+ available_patches.erase(available_patches.begin(),
+ std::find_if(available_patches.begin(), available_patches.end(), [&](DiffInfo const &I) {
+ return I.result_hashes == LocalHashes;
+ }));
+
+ // error checking and falling back if no patch was found
+ if(available_patches.empty() == true)
+ {
+ Failed("No patches left to reach target for " + PartialFile, NULL);
+ return false;
+ }
+
+ // queue the right diff
+ Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
+ Desc.Description = Target.Description + " " + available_patches[0].file + string(".pdiff");
+ DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file), Target);
+
+ if(Debug)
+ std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
+
+ QueueURI(Desc);
+
+ return true;
+}
+ /*}}}*/
+void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ if (Debug)
+ std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
+
+ Item::Done(Message, Hashes, Cnf);
+
+ std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
+ std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
+ std::string const PatchFile = GetDiffsPatchFileName(UnpatchedFile);
+ std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
+
+ switch (State)
+ {
+ // success in downloading a diff, enter ApplyDiff state
+ case StateFetchDiff:
+ Rename(DestFile, PatchFile);
+ DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
+ if(Debug)
+ std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
+ State = StateApplyDiff;
+ Local = true;
+ Desc.URI = "rred:" + UnpatchedFile;
+ QueueURI(Desc);
+ SetActiveSubprocess("rred");
+ return;
+ // success in download/apply a diff, queue next (if needed)
+ case StateApplyDiff:
+ // remove the just applied patch and base file
+ available_patches.erase(available_patches.begin());
+ RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
+ RemoveFile("pkgAcqIndexDiffs::Done", UnpatchedFile);
+ if(Debug)
+ std::clog << "Moving patched file in place: " << std::endl
+ << DestFile << " -> " << PatchedFile << std::endl;
+ Rename(DestFile, PatchedFile);
+
+ // see if there is more to download
+ if(available_patches.empty() == false)
+ {
+ new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
+ Finish();
+ } else {
+ DestFile = PatchedFile;
+ Finish(true);
+ }
+ return;
+ }
+}
+ /*}}}*/
+std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
+{
+ if(State != StateApplyDiff)
+ return pkgAcqBaseIndex::Custom600Headers();
+ std::ostringstream patchhashes;
+ for (auto && hs : available_patches[0].result_hashes)
+ patchhashes << "\nStart-" << hs.HashType() << "-Hash: " << hs.HashValue();
+ for (auto && hs : available_patches[0].patch_hashes)
+ patchhashes << "\nPatch-0-" << hs.HashType() << "-Hash: " << hs.HashValue();
+ patchhashes << pkgAcqBaseIndex::Custom600Headers();
+ return patchhashes.str();
+}
+ /*}}}*/
+pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
+
+// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
+pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *const Owner,
+ pkgAcqMetaClearSig *const TransactionManager,
+ IndexTarget const &Target,
+ DiffInfo const &patch,
+ std::vector<pkgAcqIndexMergeDiffs *> const *const allPatches)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target),
+ patch(patch), allPatches(allPatches), State(StateFetchDiff)
+{
+ Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
+
+ Desc.Owner = this;
+ Desc.ShortDesc = Target.ShortDesc;
+ Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
+ Desc.Description = Target.Description + " " + patch.file + ".pdiff";
+ DestFile = GetPartialFileNameFromURI(Target.URI + ".diff/" + patch.file + ".gz");
+
+ if(Debug)
+ std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
+
+ QueueURI(Desc);
+}
+ /*}}}*/
+void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
+{
+ if(Debug)
+ std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
+
+ pkgAcqBaseIndex::Failed(Message,Cnf);
+ Status = StatDone;
+
+ // check if we are the first to fail, otherwise we are done here
+ State = StateDoneDiff;
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
+ if ((*I)->State == StateErrorDiff)
+ {
+ State = StateErrorDiff;
+ return;
+ }
+
+ // first failure means we should fallback
+ State = StateErrorDiff;
+ if (Debug)
+ std::clog << "Falling back to normal index file acquire" << std::endl;
+ RenameOnError(PDiffError);
+ std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
+ if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
+ Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
+ DestFile.clear();
+ new pkgAcqIndex(Owner, TransactionManager, Target);
+}
+ /*}}}*/
+void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ if(Debug)
+ std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
+
+ Item::Done(Message, Hashes, Cnf);
+
+ if (std::any_of(allPatches->begin(), allPatches->end(),
+ [](pkgAcqIndexMergeDiffs const * const P) { return P->State == StateErrorDiff; }))
+ {
+ if(Debug)
+ std::clog << "Another patch failed already, no point in processing this one." << std::endl;
+ State = StateErrorDiff;
+ return;
+ }
+
+ std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
+ std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
+ if (UnpatchedFile.empty())
+ {
+ _error->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile.c_str());
+ State = StateErrorDiff;
+ return;
+ }
+ std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file);
+ std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
+
+ switch (State)
+ {
+ case StateFetchDiff:
+ Rename(DestFile, PatchFile);
+
+ // check if this is the last completed diff
+ State = StateDoneDiff;
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
+ if ((*I)->State != StateDoneDiff)
+ {
+ if(Debug)
+ std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
+ return;
+ }
+ // this is the last completed diff, so we are ready to apply now
+ DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
+ if(Debug)
+ std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
+ State = StateApplyDiff;
+ Local = true;
+ Desc.URI = "rred:" + UnpatchedFile;
+ QueueURI(Desc);
+ SetActiveSubprocess("rred");
+ return;
+ case StateApplyDiff:
+ // success in download & apply all diffs, finialize and clean up
+ if(Debug)
+ std::clog << "Queue patched file in place: " << std::endl
+ << DestFile << " -> " << PatchedFile << std::endl;
+
+ // queue for copy by the transaction manager
+ TransactionManager->TransactionStageCopy(this, DestFile, GetKeepCompressedFileName(GetFinalFilename(), Target));
+
+ // ensure the ed's are gone regardless of list-cleanup
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
+ RemoveFile("pkgAcqIndexMergeDiffs::Done", GetMergeDiffsPatchFileName(UnpatchedFile, (*I)->patch.file));
+ RemoveFile("pkgAcqIndexMergeDiffs::Done", UnpatchedFile);
+
+ // all set and done
+ Complete = true;
+ if(Debug)
+ std::clog << "allDone: " << DestFile << "\n" << std::endl;
+ return;
+ case StateDoneDiff: _error->Fatal("Done called for %s which is in an invalid Done state", PatchFile.c_str()); break;
+ case StateErrorDiff: _error->Fatal("Done called for %s which is in an invalid Error state", PatchFile.c_str()); break;
+ }
+}
+ /*}}}*/
+std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
+{
+ if(State != StateApplyDiff)
+ return pkgAcqBaseIndex::Custom600Headers();
+ std::ostringstream patchhashes;
+ unsigned int seen_patches = 0;
+ for (auto && hs : (*allPatches)[0]->patch.result_hashes)
+ patchhashes << "\nStart-" << hs.HashType() << "-Hash: " << hs.HashValue();
+ for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
+ I != allPatches->end(); ++I)
+ {
+ HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
+ for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
+ patchhashes << "\nPatch-" << std::to_string(seen_patches) << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
+ ++seen_patches;
+ }
+ patchhashes << pkgAcqBaseIndex::Custom600Headers();
+ return patchhashes.str();
+}
+ /*}}}*/
+pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
+
+// AcqIndex::AcqIndex - Constructor /*{{{*/
+pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
+ pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target, bool const Derived)
+ : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD),
+ CompressionExtensions(Target.Option(IndexTarget::COMPRESSIONTYPES))
+{
+ if (Derived)
+ return;
+ Init(Target.URI, Target.Description, Target.ShortDesc);
+
+ if(_config->FindB("Debug::Acquire::Transaction", false) == true)
+ std::clog << "New pkgIndex with TransactionManager "
+ << TransactionManager << std::endl;
+}
+ /*}}}*/
+// AcqIndex::Init - deferred Constructor /*{{{*/
+void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
+ string const &ShortDesc)
+{
+ Stage = STAGE_DOWNLOAD;
+
+ DestFile = GetPartialFileNameFromURI(URI);
+ size_t const nextExt = CompressionExtensions.find(' ');
+ if (nextExt == std::string::npos)
+ {
+ CurrentCompressionExtension = CompressionExtensions;
+ CompressionExtensions.clear();
+ }
+ else
+ {
+ CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
+ CompressionExtensions = CompressionExtensions.substr(nextExt+1);
+ }
+
+ if (CurrentCompressionExtension == "uncompressed")
+ {
+ Desc.URI = URI;
+ }
+ else if (unlikely(CurrentCompressionExtension.empty()))
+ return;
+ else
+ {
+ Desc.URI = URI + '.' + CurrentCompressionExtension;
+ DestFile = DestFile + '.' + CurrentCompressionExtension;
+ }
+
+ // store file size of the download to ensure the fetcher gives
+ // accurate progress reporting
+ FileSize = GetExpectedHashes().FileSize();
+
+ Desc.Description = URIDesc;
+ Desc.Owner = this;
+ Desc.ShortDesc = ShortDesc;
+
+ QueueURI(Desc);
+}
+ /*}}}*/
+// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
+// ---------------------------------------------------------------------
+/* The only header we use is the last-modified header. */
+string pkgAcqIndex::Custom600Headers() const
+{
+
+ string msg = "\nIndex-File: true";
+
+ if (TransactionManager->LastMetaIndexParser == NULL)
+ {
+ std::string const Final = GetFinalFilename();
+
+ struct stat Buf;
+ if (stat(Final.c_str(),&Buf) == 0)
+ msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
+ }
+
+ if(Target.IsOptional)
+ msg += "\nFail-Ignore: true";
+
+ return msg;
+}
+ /*}}}*/
+// AcqIndex::Failed - getting the indexfile failed /*{{{*/
+bool pkgAcqIndex::CommonFailed(std::string const &TargetURI,
+ std::string const &Message, pkgAcquire::MethodConfig const *const Cnf)
+{
+ pkgAcqBaseIndex::Failed(Message,Cnf);
+ // authorisation matches will not be fixed by other compression types
+ if (Status != StatAuthError)
+ {
+ if (CompressionExtensions.empty() == false)
+ {
+ Init(TargetURI, Desc.Description, Desc.ShortDesc);
+ Status = StatIdle;
+ return true;
+ }
+ }
+ return false;
+}
+void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
+{
+ if (CommonFailed(Target.URI, Message, Cnf))
+ return;
+
+ if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
+ Status = StatDone;
+ else
+ TransactionManager->AbortTransaction();
+}
+ /*}}}*/
+// AcqIndex::Done - Finished a fetch /*{{{*/
+// ---------------------------------------------------------------------
+/* This goes through a number of states.. On the initial fetch the
+ method could possibly return an alternate filename which points
+ to the uncompressed version of the file. If this is so the file
+ is copied into the partial directory. In all other cases the file
+ is decompressed with a compressed uri. */
+void pkgAcqIndex::Done(string const &Message,
+ HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
+{
+ Item::Done(Message,Hashes,Cfg);
+
+ switch(Stage)
+ {
+ case STAGE_DOWNLOAD:
+ StageDownloadDone(Message);
+ break;
+ case STAGE_DECOMPRESS_AND_VERIFY:
+ StageDecompressDone();
+ break;
+ }
+}
+ /*}}}*/
+// AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
+void pkgAcqIndex::StageDownloadDone(string const &Message)
+{
+ Local = true;
+ Complete = true;
+
+ std::string const AltFilename = LookupTag(Message,"Alt-Filename");
+ std::string Filename = LookupTag(Message,"Filename");
+
+ // we need to verify the file against the current Release file again
+ // on if-modfied-since hit to avoid a stale attack against us
+ if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
+ {
+ // copy FinalFile into partial/ so that we check the hash again
+ string const FinalFile = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
+ if (symlink(FinalFile.c_str(), DestFile.c_str()) != 0)
+ _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile.c_str(), DestFile.c_str());
+ else
+ {
+ EraseFileName = DestFile;
+ Filename = DestFile;
+ }
+ Stage = STAGE_DECOMPRESS_AND_VERIFY;
+ Desc.URI = "store:" + Filename;
+ QueueURI(Desc);
+ SetActiveSubprocess(::URI(Desc.URI).Access);
+ return;
+ }
+ // methods like file:// give us an alternative (uncompressed) file
+ else if (Target.KeepCompressed == false && AltFilename.empty() == false)
+ {
+ Filename = AltFilename;
+ EraseFileName.clear();
+ }
+ // Methods like e.g. "file:" will give us a (compressed) FileName that is
+ // not the "DestFile" we set, in this case we uncompress from the local file
+ else if (Filename != DestFile && RealFileExists(DestFile) == false)
+ {
+ // symlinking ensures that the filename can be used for compression detection
+ // that is e.g. needed for by-hash which has no extension over file
+ if (symlink(Filename.c_str(),DestFile.c_str()) != 0)
+ _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename.c_str(), DestFile.c_str());
+ else
+ {
+ EraseFileName = DestFile;
+ Filename = DestFile;
+ }
+ }
+
+ Stage = STAGE_DECOMPRESS_AND_VERIFY;
+ DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
+ if (Filename != DestFile && flExtension(Filename) == flExtension(DestFile))
+ Desc.URI = "copy:" + Filename;
+ else
+ Desc.URI = "store:" + Filename;
+ if (DestFile == Filename)
+ {
+ if (CurrentCompressionExtension == "uncompressed")
+ return StageDecompressDone();
+ DestFile = "/dev/null";
+ }
+
+ if (EraseFileName.empty() && Filename != AltFilename)
+ EraseFileName = Filename;
+
+ // queue uri for the next stage
+ QueueURI(Desc);
+ SetActiveSubprocess(::URI(Desc.URI).Access);
+}
+ /*}}}*/
+// AcqIndex::StageDecompressDone - Final verification /*{{{*/
+void pkgAcqIndex::StageDecompressDone()
+{
+ if (DestFile == "/dev/null")
+ DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
+
+ // Done, queue for rename on transaction finished
+ TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
+}
+ /*}}}*/
+pkgAcqIndex::~pkgAcqIndex() {}
+
+// AcqArchive::AcqArchive - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* This just sets up the initial fetch environment and queues the first
+ possibilitiy */
+APT_IGNORE_DEPRECATED_PUSH
+pkgAcqArchive::pkgAcqArchive(pkgAcquire *const Owner, pkgSourceList *const Sources,
+ pkgRecords *const Recs, pkgCache::VerIterator const &Version,
+ string &StoreFilename) : Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
+ StoreFilename(StoreFilename), Vf(),
+ Trusted(false)
+{
+ if (Version.Arch() == 0)
+ {
+ _error->Error(_("I wasn't able to locate a file for the %s package. "
+ "This might mean you need to manually fix this package. "
+ "(due to missing arch)"),
+ Version.ParentPkg().FullName().c_str());
+ return;
+ }
+
+ // check if we have one trusted source for the package. if so, switch
+ // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
+ bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
+ bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
+ bool seenUntrusted = false;
+ for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
+ {
+ pkgIndexFile *Index;
+ if (Sources->FindIndex(i.File(),Index) == false)
+ continue;
+
+ if (debugAuth == true)
+ std::cerr << "Checking index: " << Index->Describe()
+ << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
+
+ if (Index->IsTrusted() == true)
+ {
+ Trusted = true;
+ if (allowUnauth == false)
+ break;
+ }
+ else
+ seenUntrusted = true;
+ }
+
+ // "allow-unauthenticated" restores apts old fetching behaviour
+ // that means that e.g. unauthenticated file:// uris are higher
+ // priority than authenticated http:// uris
+ if (allowUnauth == true && seenUntrusted == true)
+ Trusted = false;
+
+ StoreFilename.clear();
+ std::set<string> targetComponents, targetCodenames, targetSuites;
+ std::vector<std::unique_ptr<FileFd>> authconfs;
+ for (auto Vf = Version.FileList(); Vf.end() == false; ++Vf)
+ {
+ auto const PkgF = Vf.File();
+ if (unlikely(PkgF.end()))
+ continue;
+ if (PkgF.Flagged(pkgCache::Flag::NotSource))
+ continue;
+ if (PkgF.Flagged(pkgCache::Flag::PackagesRequireAuthorization) && !IsAuthorized(PkgF, authconfs))
+ continue;
+ pkgIndexFile *Index;
+ if (Sources->FindIndex(PkgF, Index) == false)
+ continue;
+ if (Trusted && Index->IsTrusted() == false)
+ continue;
+
+ pkgRecords::Parser &Parse = Recs->Lookup(Vf);
+ // collect the hashes from the indexes
+ auto hsl = Parse.Hashes();
+ if (ExpectedHashes.empty())
+ ExpectedHashes = hsl;
+ else
+ {
+ // bad things will likely happen, but the user might be "lucky" still
+ // if the sources provide the same hashtypes (so that they aren't mixed)
+ for (auto const &hs : hsl)
+ if (ExpectedHashes.push_back(hs) == false)
+ {
+ _error->Warning("Sources disagree on hashes for supposely identical version '%s' of '%s'.",
+ Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
+ break;
+ }
+ }
+ // only allow local volatile sources to have no hashes
+ if (PkgF.Flagged(pkgCache::Flag::LocalSource))
+ LocalSource = true;
+ else if (hsl.empty())
+ continue;
+
+ std::string poolfilename = Parse.FileName();
+ if (poolfilename.empty())
+ continue;
+
+ std::remove_reference<decltype(ModifyCustomFields())>::type fields;
+ {
+ auto const debIndex = dynamic_cast<pkgDebianIndexTargetFile const *const>(Index);
+ if (debIndex != nullptr)
+ {
+ auto const IT = debIndex->GetIndexTarget();
+ fields.emplace("Target-Repo-URI", IT.Option(IndexTarget::REPO_URI));
+ fields.emplace("Target-Release", IT.Option(IndexTarget::RELEASE));
+ fields.emplace("Target-Site", IT.Option(IndexTarget::SITE));
+ }
+ }
+ fields.emplace("Target-Base-URI", Index->ArchiveURI(""));
+ if (PkgF->Component != 0)
+ fields.emplace("Target-Component", PkgF.Component());
+ auto const RelF = PkgF.ReleaseFile();
+ if (RelF.end() == false)
+ {
+ if (RelF->Codename != 0)
+ fields.emplace("Target-Codename", RelF.Codename());
+ if (RelF->Archive != 0)
+ fields.emplace("Target-Suite", RelF.Archive());
+ }
+ fields.emplace("Target-Architecture", Version.Arch());
+ fields.emplace("Target-Type", flExtension(poolfilename));
+
+ if (StoreFilename.empty())
+ {
+ /* We pick a filename based on the information we have for the version,
+ but we don't know what extension such a file should have, so we look
+ at the filenames used online and assume that they are the same for
+ all repositories containing this file */
+ StoreFilename = QuoteString(Version.ParentPkg().Name(), "_:") + '_' +
+ QuoteString(Version.VerStr(), "_:") + '_' +
+ QuoteString(Version.Arch(), "_:.") +
+ "." + flExtension(poolfilename);
+
+ Desc.URI = Index->ArchiveURI(poolfilename);
+ Desc.Description = Index->ArchiveInfo(Version);
+ Desc.Owner = this;
+ Desc.ShortDesc = Version.ParentPkg().FullName(true);
+ auto &customfields = ModifyCustomFields();
+ for (auto const &f : fields)
+ customfields[f.first] = f.second;
+ FileSize = Version->Size;
+ }
+ else
+ PushAlternativeURI(Index->ArchiveURI(poolfilename), std::move(fields), true);
+ }
+ if (StoreFilename.empty())
+ {
+ _error->Error(_("Can't find a source to download version '%s' of '%s'"),
+ Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
+ return;
+ }
+
+ // Check if we already downloaded the file
+ struct stat Buf;
+ auto FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
+ if (stat(FinalFile.c_str(), &Buf) == 0)
+ {
+ // Make sure the size matches
+ if ((unsigned long long)Buf.st_size == Version->Size)
+ {
+ Complete = true;
+ Local = true;
+ Status = StatDone;
+ StoreFilename = DestFile = FinalFile;
+ return;
+ }
+
+ /* Hmm, we have a file and its size does not match, this shouldn't
+ happen.. */
+ RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
+ }
+
+ // Check the destination file
+ DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
+ if (stat(DestFile.c_str(), &Buf) == 0)
+ {
+ // Hmm, the partial file is too big, erase it
+ if ((unsigned long long)Buf.st_size > Version->Size)
+ RemoveFile("pkgAcqArchive::QueueNext", DestFile);
+ else
+ PartialSize = Buf.st_size;
+ }
+
+ // Disables download of archives - useful if no real installation follows,
+ // e.g. if we are just interested in proposed installation order
+ if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
+ {
+ Complete = true;
+ Local = true;
+ Status = StatDone;
+ StoreFilename = DestFile = FinalFile;
+ return;
+ }
+
+ // Create the item
+ Local = false;
+ QueueURI(Desc);
+}
+APT_IGNORE_DEPRECATED_POP
+ /*}}}*/
+bool pkgAcqArchive::QueueNext() /*{{{*/
+{
+ return false;
+}
+ /*}}}*/
+// AcqArchive::Done - Finished fetching /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cfg)
+{
+ Item::Done(Message, Hashes, Cfg);
+
+ // Grab the output filename
+ std::string const FileName = LookupTag(Message,"Filename");
+ if (DestFile != FileName && RealFileExists(DestFile) == false)
+ {
+ StoreFilename = DestFile = FileName;
+ Local = true;
+ Complete = true;
+ return;
+ }
+
+ // Done, move it into position
+ string const FinalFile = GetFinalFilename();
+ Rename(DestFile,FinalFile);
+ StoreFilename = DestFile = FinalFile;
+ Complete = true;
+}
+ /*}}}*/
+// AcqArchive::Failed - Failure handler /*{{{*/
+// ---------------------------------------------------------------------
+/* Here we try other sources */
+void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Failed(Message,Cnf);
+}
+ /*}}}*/
+APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
+{
+ return Trusted;
+}
+ /*}}}*/
+void pkgAcqArchive::Finished() /*{{{*/
+{
+ if (Status == pkgAcquire::Item::StatDone &&
+ Complete == true)
+ return;
+ StoreFilename = string();
+}
+ /*}}}*/
+std::string pkgAcqArchive::DescURI() const /*{{{*/
+{
+ return Desc.URI;
+}
+ /*}}}*/
+std::string pkgAcqArchive::ShortDesc() const /*{{{*/
+{
+ return Desc.ShortDesc;
+}
+ /*}}}*/
+pkgAcqArchive::~pkgAcqArchive() {}
+
+// AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
+class pkgAcqChangelog::Private
+{
+ public:
+ std::string FinalFile;
+};
+pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
+ std::string const &DestDir, std::string const &DestFilename) :
+ pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
+{
+ Desc.URI = URI(Ver);
+ Init(DestDir, DestFilename);
+}
+// some parameters are char* here as they come likely from char* interfaces – which can also return NULL
+pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
+ char const * const Component, char const * const SrcName, char const * const SrcVersion,
+ const string &DestDir, const string &DestFilename) :
+ pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
+{
+ Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
+ Init(DestDir, DestFilename);
+}
+pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
+ std::string const &URI, char const * const SrcName, char const * const SrcVersion,
+ const string &DestDir, const string &DestFilename) :
+ pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
+{
+ Desc.URI = URI;
+ Init(DestDir, DestFilename);
+}
+void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
+{
+ if (Desc.URI.empty())
+ {
+ Status = StatError;
+ // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
+ strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
+ // Let the error message print something sensible rather than "Failed to fetch /"
+ if (DestFilename.empty())
+ DestFile = SrcName + ".changelog";
+ else
+ DestFile = DestFilename;
+ Desc.URI = "changelog:/" + DestFile;
+ return;
+ }
+
+ std::string DestFileName;
+ if (DestFilename.empty())
+ DestFileName = flCombine(DestFile, SrcName + ".changelog");
+ else
+ DestFileName = flCombine(DestFile, DestFilename);
+
+ std::string const SandboxUser = _config->Find("APT::Sandbox::User");
+ std::string const systemTemp = GetTempDir(SandboxUser);
+ char tmpname[1000];
+ snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
+ if (NULL == mkdtemp(tmpname))
+ {
+ _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
+ Status = StatError;
+ return;
+ }
+ TemporaryDirectory = tmpname;
+
+ ChangeOwnerAndPermissionOfFile("pkgAcqChangelog::Init", TemporaryDirectory.c_str(),
+ SandboxUser.c_str(), ROOT_GROUP, 0700);
+
+ DestFile = flCombine(TemporaryDirectory, DestFileName);
+ if (DestDir.empty() == false)
+ {
+ d->FinalFile = flCombine(DestDir, DestFileName);
+ if (RealFileExists(d->FinalFile))
+ {
+ FileFd file1, file2;
+ if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) &&
+ file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1))
+ {
+ ChangeOwnerAndPermissionOfFile("pkgAcqChangelog::Init", DestFile.c_str(), "root", ROOT_GROUP, 0644);
+ struct timeval times[2];
+ times[0].tv_sec = times[1].tv_sec = file2.ModificationTime();
+ times[0].tv_usec = times[1].tv_usec = 0;
+ utimes(DestFile.c_str(), times);
+ }
+ }
+ }
+
+ Desc.ShortDesc = "Changelog";
+ strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
+ Desc.Owner = this;
+ QueueURI(Desc);
+}
+ /*}}}*/
+std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
+{
+ std::string const confOnline = "Acquire::Changelogs::AlwaysOnline";
+ bool AlwaysOnline = _config->FindB(confOnline, false);
+ if (AlwaysOnline == false)
+ for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
+ {
+ pkgCache::PkgFileIterator const PF = VF.File();
+ if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
+ continue;
+ pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
+ if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false))
+ {
+ AlwaysOnline = true;
+ break;
+ }
+ }
+ if (AlwaysOnline == false)
+ {
+ pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
+ if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
+ {
+ std::string const root = _config->FindDir("Dir");
+ std::string const basename = root + std::string("usr/share/doc/") + Pkg.Name() + "/changelog";
+ std::string const debianname = basename + ".Debian";
+ if (FileExists(debianname))
+ return "copy://" + debianname;
+ else if (FileExists(debianname + ".gz"))
+ return "store://" + debianname + ".gz";
+ else if (FileExists(basename))
+ return "copy://" + basename;
+ else if (FileExists(basename + ".gz"))
+ return "store://" + basename + ".gz";
+ }
+ }
+
+ char const * const SrcName = Ver.SourcePkgName();
+ char const * const SrcVersion = Ver.SourceVerStr();
+ // find the first source for this version which promises a changelog
+ for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
+ {
+ pkgCache::PkgFileIterator const PF = VF.File();
+ if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
+ continue;
+ pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
+ std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
+ if (uri.empty())
+ continue;
+ return uri;
+ }
+ return "";
+}
+std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
+{
+ if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
+ return "";
+ std::string const serverConfig = "Acquire::Changelogs::URI";
+ std::string server;
+#define APT_EMPTY_SERVER \
+ if (server.empty() == false) \
+ { \
+ if (server != "no") \
+ return server; \
+ return ""; \
+ }
+#define APT_CHECK_SERVER(X, Y) \
+ if (Rls->X != 0) \
+ { \
+ std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
+ server = _config->Find(specialServerConfig); \
+ APT_EMPTY_SERVER \
+ }
+ // this way e.g. Debian-Security can fallback to Debian
+ APT_CHECK_SERVER(Label, "Override::")
+ APT_CHECK_SERVER(Origin, "Override::")
+
+ if (RealFileExists(Rls.FileName()))
+ {
+ _error->PushToStack();
+ FileFd rf;
+ /* This can be costly. A caller wanting to get millions of URIs might
+ want to do this on its own once and use Override settings.
+ We don't do this here as Origin/Label are not as unique as they
+ should be so this could produce request order-dependent anomalies */
+ if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
+ {
+ pkgTagFile TagFile(&rf, rf.Size());
+ pkgTagSection Section;
+ if (TagFile.Step(Section) == true)
+ server = Section.FindS("Changelogs");
+ }
+ _error->RevertToStack();
+ APT_EMPTY_SERVER
+ }
+
+ APT_CHECK_SERVER(Label, "")
+ APT_CHECK_SERVER(Origin, "")
+#undef APT_CHECK_SERVER
+#undef APT_EMPTY_SERVER
+ return "";
+}
+std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
+ char const * const Component, char const * const SrcName,
+ char const * const SrcVersion)
+{
+ return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
+}
+std::string pkgAcqChangelog::URI(std::string const &Template,
+ char const * const Component, char const * const SrcName,
+ char const * const SrcVersion)
+{
+ if (Template.find("@CHANGEPATH@") == std::string::npos)
+ return "";
+
+ // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/apt_1.1 or contrib/liba/libapt/libapt_2.0
+ std::string Src = SrcName;
+ std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
+ path.append("/").append(Src).append("/");
+ path.append(Src).append("_").append(StripEpoch(SrcVersion));
+ // we omit component for releases without one (= flat-style repositories)
+ if (Component != NULL && strlen(Component) != 0)
+ path = std::string(Component) + "/" + path;
+
+ return SubstVar(Template, "@CHANGEPATH@", path);
+}
+ /*}}}*/
+// AcqChangelog::Failed - Failure handler /*{{{*/
+void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Failed(Message,Cnf);
+
+ std::string errText;
+ // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
+ strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
+
+ // Error is probably something techy like 404 Not Found
+ if (ErrorText.empty())
+ ErrorText = errText;
+ else
+ ErrorText = errText + " (" + ErrorText + ")";
+}
+ /*}}}*/
+// AcqChangelog::Done - Item downloaded OK /*{{{*/
+void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Done(Message,CalcHashes,Cnf);
+ if (d->FinalFile.empty() == false)
+ {
+ if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false ||
+ Rename(DestFile, d->FinalFile) == false)
+ Status = StatError;
+ }
+
+ Complete = true;
+}
+ /*}}}*/
+pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
+{
+ if (TemporaryDirectory.empty() == false)
+ {
+ RemoveFile("~pkgAcqChangelog", DestFile);
+ rmdir(TemporaryDirectory.c_str());
+ }
+ delete d;
+}
+ /*}}}*/
+
+// AcqFile::pkgAcqFile - Constructor /*{{{*/
+APT_IGNORE_DEPRECATED_PUSH
+pkgAcqFile::pkgAcqFile(pkgAcquire *const Owner, string const &URI, HashStringList const &Hashes,
+ unsigned long long const Size, string const &Dsc, string const &ShortDesc,
+ const string &DestDir, const string &DestFilename,
+ bool const IsIndexFile) : Item(Owner), d(NULL), Retries(0), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
+{
+ if(!DestFilename.empty())
+ DestFile = DestFilename;
+ else if(!DestDir.empty())
+ DestFile = DestDir + "/" + flNotDir(URI);
+ else
+ DestFile = flNotDir(URI);
+
+ // Create the item
+ Desc.URI = URI;
+ Desc.Description = Dsc;
+ Desc.Owner = this;
+
+ // Set the short description to the archive component
+ Desc.ShortDesc = ShortDesc;
+
+ // Get the transfer sizes
+ FileSize = Size;
+ struct stat Buf;
+ if (stat(DestFile.c_str(),&Buf) == 0)
+ {
+ // Hmm, the partial file is too big, erase it
+ if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
+ RemoveFile("pkgAcqFile", DestFile);
+ else
+ PartialSize = Buf.st_size;
+ }
+
+ QueueURI(Desc);
+}
+APT_IGNORE_DEPRECATED_POP
+ /*}}}*/
+// AcqFile::Done - Item downloaded OK /*{{{*/
+void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const * const Cnf)
+{
+ Item::Done(Message,CalcHashes,Cnf);
+
+ std::string const FileName = LookupTag(Message,"Filename");
+ Complete = true;
+
+ // The files timestamp matches
+ if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
+ return;
+
+ // We have to copy it into place
+ if (RealFileExists(DestFile.c_str()) == false)
+ {
+ Local = true;
+ if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
+ Cnf->Removable == true)
+ {
+ Desc.URI = "copy:" + FileName;
+ QueueURI(Desc);
+ return;
+ }
+
+ // Erase the file if it is a symlink so we can overwrite it
+ struct stat St;
+ if (lstat(DestFile.c_str(),&St) == 0)
+ {
+ if (S_ISLNK(St.st_mode) != 0)
+ RemoveFile("pkgAcqFile::Done", DestFile);
+ }
+
+ // Symlink the file
+ if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
+ {
+ _error->PushToStack();
+ _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
+ std::stringstream msg;
+ _error->DumpErrors(msg, GlobalError::DEBUG, false);
+ _error->RevertToStack();
+ ErrorText = msg.str();
+ Status = StatError;
+ Complete = false;
+ }
+ }
+}
+ /*}}}*/
+void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const *const Cnf) /*{{{*/
+{
+ // FIXME: Remove this pointless overload on next ABI break
+ Item::Failed(Message, Cnf);
+}
+ /*}}}*/
+string pkgAcqFile::Custom600Headers() const /*{{{*/
+{
+ if (IsIndexFile)
+ return "\nIndex-File: true";
+ return "";
+}
+ /*}}}*/
+pkgAcqFile::~pkgAcqFile() {}
+
+void pkgAcqAuxFile::Failed(std::string const &Message, pkgAcquire::MethodConfig const *const Cnf) /*{{{*/
+{
+ pkgAcqFile::Failed(Message, Cnf);
+ if (Status == StatIdle)
+ return;
+ if (RealFileExists(DestFile))
+ Rename(DestFile, DestFile + ".FAILED");
+ Worker->ReplyAux(Desc);
+}
+ /*}}}*/
+void pkgAcqAuxFile::Done(std::string const &Message, HashStringList const &CalcHashes, /*{{{*/
+ pkgAcquire::MethodConfig const *const Cnf)
+{
+ pkgAcqFile::Done(Message, CalcHashes, Cnf);
+ if (Status == StatDone)
+ Worker->ReplyAux(Desc);
+ else if (Status == StatAuthError || Status == StatError)
+ Worker->ReplyAux(Desc);
+}
+ /*}}}*/
+std::string pkgAcqAuxFile::Custom600Headers() const /*{{{*/
+{
+ if (MaximumSize == 0)
+ return pkgAcqFile::Custom600Headers();
+ std::string maxsize;
+ strprintf(maxsize, "\nMaximum-Size: %llu", MaximumSize);
+ return pkgAcqFile::Custom600Headers().append(maxsize);
+}
+ /*}}}*/
+void pkgAcqAuxFile::Finished() /*{{{*/
+{
+ auto dirname = flCombine(_config->FindDir("Dir::State::lists"), "auxfiles/");
+ if (APT::String::Startswith(DestFile, dirname))
+ {
+ // the file is never returned by method requesting it, so fix up the permission now
+ if (FileExists(DestFile))
+ {
+ ChangeOwnerAndPermissionOfFile("pkgAcqAuxFile", DestFile.c_str(), "root", ROOT_GROUP, 0644);
+ if (Status == StatDone)
+ return;
+ }
+ }
+ else
+ {
+ dirname = flNotFile(DestFile);
+ RemoveFile("pkgAcqAuxFile::Finished", DestFile);
+ RemoveFile("pkgAcqAuxFile::Finished", DestFile + ".FAILED");
+ rmdir(dirname.c_str());
+ }
+ DestFile.clear();
+}
+ /*}}}*/
+// GetAuxFileNameFromURI /*{{{*/
+static std::string GetAuxFileNameFromURIInLists(std::string const &uri)
+{
+ // check if we have write permission for our usual location.
+ auto const dirname = flCombine(_config->FindDir("Dir::State::lists"), "auxfiles/");
+ char const * const filetag = ".apt-acquire-privs-test.XXXXXX";
+ std::string const tmpfile_tpl = flCombine(dirname, filetag);
+ std::unique_ptr<char, decltype(std::free) *> tmpfile { strdup(tmpfile_tpl.c_str()), std::free };
+ int const fd = mkstemp(tmpfile.get());
+ if (fd == -1)
+ return "";
+ RemoveFile("GetAuxFileNameFromURI", tmpfile.get());
+ close(fd);
+ return flCombine(dirname, URItoFileName(uri));
+}
+static std::string GetAuxFileNameFromURI(std::string const &uri)
+{
+ auto const lists = GetAuxFileNameFromURIInLists(uri);
+ if (lists.empty() == false)
+ return lists;
+
+ std::string tmpdir_tpl;
+ strprintf(tmpdir_tpl, "%s/apt-auxfiles-XXXXXX", GetTempDir().c_str());
+ std::unique_ptr<char, decltype(std::free) *> tmpdir { strndup(tmpdir_tpl.data(), tmpdir_tpl.length()), std::free };
+ if (mkdtemp(tmpdir.get()) == nullptr)
+ {
+ _error->Errno("GetAuxFileNameFromURI", "mkdtemp of %s failed", tmpdir.get());
+ return flCombine("/nonexistent/auxfiles/", URItoFileName(uri));
+ }
+ chmod(tmpdir.get(), 0755);
+ auto const filename = flCombine(tmpdir.get(), URItoFileName(uri));
+ _error->PushToStack();
+ FileFd in(flCombine(flCombine(_config->FindDir("Dir::State::lists"), "auxfiles/"), URItoFileName(uri)), FileFd::ReadOnly);
+ if (in.IsOpen())
+ {
+ FileFd out(filename, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive);
+ CopyFile(in, out);
+ ChangeOwnerAndPermissionOfFile("GetAuxFileNameFromURI", filename.c_str(), "root", ROOT_GROUP, 0644);
+ }
+ _error->RevertToStack();
+ return filename;
+}
+ /*}}}*/
+pkgAcqAuxFile::pkgAcqAuxFile(pkgAcquire::Item *const Owner, pkgAcquire::Worker *const Worker,
+ std::string const &ShortDesc, std::string const &Desc, std::string const &URI,
+ HashStringList const &Hashes, unsigned long long const MaximumSize) : pkgAcqFile(Owner->GetOwner(), URI, Hashes, Hashes.FileSize(), Desc, ShortDesc, "", GetAuxFileNameFromURI(URI), false),
+ Owner(Owner), Worker(Worker), MaximumSize(MaximumSize)
+{
+ /* very bad failures can happen while constructing which causes
+ us to hang as the aux request is never answered (e.g. method not available)
+ Ideally we catch failures earlier, but a safe guard can't hurt. */
+ if (Status == pkgAcquire::Item::StatIdle || Status == pkgAcquire::Item::StatFetching)
+ return;
+ Failed(std::string("400 URI Failure\n") +
+ "URI: " + URI + "\n" +
+ "Filename: " + DestFile,
+ nullptr);
+}
+pkgAcqAuxFile::~pkgAcqAuxFile() {}
diff --git a/apt-pkg/acquire-item.h b/apt-pkg/acquire-item.h
new file mode 100644
index 0000000..70651d9
--- /dev/null
+++ b/apt-pkg/acquire-item.h
@@ -0,0 +1,1250 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Item - Item to acquire
+
+ When an item is instantiated it will add it self to the local list in
+ the Owner Acquire class. Derived classes will then call QueueURI to
+ register all the URI's they wish to fetch at the initial moment.
+
+ Three item classes are provided to provide functionality for
+ downloading of Index, Translation and Packages files.
+
+ A Archive class is provided for downloading .deb files. It does Hash
+ checking and source location as well as a retry algorithm.
+
+ ##################################################################### */
+ /*}}}*/
+#ifndef PKGLIB_ACQUIRE_ITEM_H
+#define PKGLIB_ACQUIRE_ITEM_H
+
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/indexfile.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/weakptr.h>
+
+#include <map>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/pkgrecords.h>
+#include <apt-pkg/sourcelist.h>
+#endif
+
+/** \addtogroup acquire
+ * @{
+ *
+ * \file acquire-item.h
+ */
+
+class pkgRecords;
+class pkgSourceList;
+class pkgAcqMetaClearSig;
+class pkgAcqIndexMergeDiffs;
+class metaIndex;
+
+class pkgAcquire::Item : public WeakPointable /*{{{*/
+/** \brief Represents the process by which a pkgAcquire object should
+ * retrieve a file or a collection of files.
+ *
+ * By convention, Item subclasses should insert themselves into the
+ * acquire queue when they are created by calling QueueURI(), and
+ * remove themselves by calling Dequeue() when either Done() or
+ * Failed() is invoked. Item objects are also responsible for
+ * notifying the download progress indicator (accessible via
+ * #Owner->Log) of their status.
+ *
+ * \see pkgAcquire
+ */
+{
+ public:
+
+ /** \brief The current status of this item. */
+ enum ItemState
+ {
+ /** \brief The item is waiting to be downloaded. */
+ StatIdle,
+
+ /** \brief The item is currently being downloaded. */
+ StatFetching,
+
+ /** \brief The item has been successfully downloaded. */
+ StatDone,
+
+ /** \brief An error was encountered while downloading this
+ * item.
+ */
+ StatError,
+
+ /** \brief The item was downloaded but its authenticity could
+ * not be verified.
+ */
+ StatAuthError,
+
+ /** \brief The item was could not be downloaded because of
+ * a transient network error (e.g. network down)
+ */
+ StatTransientNetworkError,
+ } Status;
+
+ /** \brief Contains a textual description of the error encountered
+ * if #ItemState is #StatError or #StatAuthError.
+ */
+ std::string ErrorText;
+
+ /** \brief The size of the object to fetch. */
+ unsigned long long FileSize;
+
+ /** \brief How much of the object was already fetched. */
+ unsigned long long PartialSize;
+
+ /** \brief If not \b NULL, contains the name of a subprocess that
+ * is operating on this object (for instance, "gzip" or "gpgv").
+ */
+ APT_DEPRECATED_MSG("Use the std::string member ActiveSubprocess instead") const char *Mode;
+
+ /** \brief contains the name of the subprocess that is operating on this object
+ * (for instance, "gzip", "rred" or "gpgv"). This is obsoleting #Mode from above
+ * as it can manage the lifetime of included string properly. */
+ std::string ActiveSubprocess;
+
+ /** \brief A client-supplied unique identifier.
+ *
+ * This field is initialized to 0; it is meant to be filled in by
+ * clients that wish to use it to uniquely identify items.
+ *
+ * APT progress reporting will store an ID there as shown in "Get:42 …"
+ */
+ unsigned long ID;
+
+ /** \brief If \b true, the entire object has been successfully fetched.
+ *
+ * Subclasses should set this to \b true when appropriate.
+ */
+ bool Complete;
+
+ /** \brief If \b true, the URI of this object is "local".
+ *
+ * The only effect of this field is to exclude the object from the
+ * download progress indicator's overall statistics.
+ */
+ bool Local;
+
+ std::string UsedMirror;
+
+ /** \brief The number of fetch queues into which this item has been
+ * inserted.
+ *
+ * There is one queue for each source from which an item could be
+ * downloaded.
+ *
+ * \sa pkgAcquire
+ */
+ unsigned int QueueCounter;
+
+ /** \brief The number of additional fetch items that are expected
+ * once this item is done.
+ *
+ * Some items like pkgAcqMeta{Index,Sig} will queue additional
+ * items. This variable can be set by the methods if it knows
+ * in advance how many items to expect to get a more accurate
+ * progress.
+ */
+ unsigned int ExpectedAdditionalItems;
+
+ /** \brief The name of the file into which the retrieved object
+ * will be written.
+ */
+ std::string DestFile;
+
+ /** \brief Invoked by the acquire worker when the object couldn't
+ * be fetched.
+ *
+ * This is a branch of the continuation of the fetch process.
+ *
+ * \param Message An RFC822-formatted message from the acquire
+ * method describing what went wrong. Use LookupTag() to parse
+ * it.
+ *
+ * \param Cnf The method via which the worker tried to fetch this object.
+ *
+ * \sa pkgAcqMethod
+ */
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf);
+ APT_HIDDEN void FailMessage(std::string const &Message);
+
+ /** \brief Invoked by the acquire worker to check if the successfully
+ * fetched object is also the objected we wanted to have.
+ *
+ * Note that the object might \e not have been written to
+ * DestFile; check for the presence of an Alt-Filename entry in
+ * Message to find the file to which it was really written.
+ *
+ * This is called before Done is called and can prevent it by returning
+ * \b false which will result in Failed being called instead.
+ *
+ * You should prefer to use this method over calling Failed() from Done()
+ * as this has e.g. the wrong progress reporting.
+ *
+ * \param Message Data from the acquire method. Use LookupTag()
+ * to parse it.
+ * \param Cnf The method via which the object was fetched.
+ *
+ * \sa pkgAcqMethod
+ */
+ virtual bool VerifyDone(std::string const &Message,
+ pkgAcquire::MethodConfig const * const Cnf);
+
+ /** \brief Invoked by the acquire worker when the object was
+ * fetched successfully.
+ *
+ * Note that the object might \e not have been written to
+ * DestFile; check for the presence of an Alt-Filename entry in
+ * Message to find the file to which it was really written.
+ *
+ * Done is often used to switch from one stage of the processing
+ * to the next (e.g. fetching, unpacking, copying). It is one
+ * branch of the continuation of the fetch process.
+ *
+ * \param Message Data from the acquire method. Use LookupTag()
+ * to parse it.
+ * \param Hashes The HashSums of the object that was fetched.
+ * \param Cnf The method via which the object was fetched.
+ *
+ * \sa pkgAcqMethod
+ */
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf);
+
+ /** \brief Invoked when the worker starts to fetch this object.
+ *
+ * \param Message RFC822-formatted data from the worker process.
+ * Use LookupTag() to parse it.
+ *
+ * \param Hashes The expected hashes of the object being fetched.
+ *
+ * \sa pkgAcqMethod
+ */
+ virtual void Start(std::string const &Message, unsigned long long const Size);
+
+ /** \brief Custom headers to be sent to the fetch process.
+ *
+ * \return a string containing RFC822-style headers that are to be
+ * inserted into the 600 URI Acquire message sent to the fetch
+ * subprocess. The headers are inserted after a newline-less
+ * line, so they should (if nonempty) have a leading newline and
+ * no trailing newline.
+ */
+ virtual std::string Custom600Headers() const;
+ // Retries should really be a member of the Item, but can't be for ABI reasons
+ APT_HIDDEN unsigned int &ModifyRetries();
+ // this is more a hack than a proper external interface, hence hidden
+ APT_HIDDEN std::unordered_map<std::string, std::string> &ModifyCustomFields();
+ // this isn't the super nicest interface either…
+ APT_HIDDEN bool PopAlternativeURI(std::string &NewURI);
+ APT_HIDDEN bool IsGoodAlternativeURI(std::string const &AltUri) const;
+ APT_HIDDEN void PushAlternativeURI(std::string &&NewURI, std::unordered_map<std::string, std::string> &&fields, bool const at_the_back);
+ APT_HIDDEN void RemoveAlternativeSite(std::string &&OldSite);
+
+ /** \brief A "descriptive" URI-like string.
+ *
+ * \return a URI that should be used to describe what is being fetched.
+ */
+ virtual std::string DescURI() const = 0;
+ /** \brief Short item description.
+ *
+ * \return a brief description of the object being fetched.
+ */
+ virtual std::string ShortDesc() const;
+
+ /** \brief Invoked by the worker when the download is completely done. */
+ virtual void Finished();
+
+ /** \return HashSums the DestFile is supposed to have in this stage */
+ virtual HashStringList GetExpectedHashes() const = 0;
+ /** \return the 'best' hash for display proposes like --print-uris */
+ std::string HashSum() const;
+
+ /** \return if having no hashes is a hard failure or not
+ *
+ * Idealy this is always \b true for every subclass, but thanks to
+ * historical grow we don't have hashes for all files in all cases
+ * in all steps, so it is slightly more complicated than it should be.
+ */
+ virtual bool HashesRequired() const { return true; }
+
+ /** \return the acquire process with which this item is associated. */
+ pkgAcquire *GetOwner() const;
+ pkgAcquire::ItemDesc &GetItemDesc();
+
+ /** \return \b true if this object is being fetched from a trusted source. */
+ virtual bool IsTrusted() const;
+
+ /** \brief Report mirror problem
+ *
+ * This allows reporting mirror failures back to a centralized
+ * server. The apt-report-mirror-failure script is called for this
+ *
+ * \param FailCode A short failure string that is send
+ */
+ APT_DEPRECATED_MSG("Item::Failed does this for you") void ReportMirrorFailure(std::string const &FailCode);
+
+ /** \brief Set the name of the current active subprocess
+ *
+ * See also #ActiveSubprocess
+ */
+ void SetActiveSubprocess(std::string const &subprocess);
+
+ /** \brief Initialize an item.
+ *
+ * Adds the item to the list of items known to the acquire
+ * process, but does not place it into any fetch queues (you must
+ * manually invoke QueueURI() to do so).
+ *
+ * \param Owner The new owner of this item.
+ */
+ explicit Item(pkgAcquire * const Owner);
+
+ /** \brief Remove this item from its owner's queue by invoking
+ * pkgAcquire::Remove.
+ */
+ virtual ~Item();
+
+ bool APT_HIDDEN IsRedirectionLoop(std::string const &NewURI);
+ /** \brief The priority of the item, used for queuing */
+ int APT_HIDDEN Priority();
+
+ protected:
+ /** \brief The acquire object with which this item is associated. */
+ pkgAcquire * const Owner;
+
+ /** \brief The item that is currently being downloaded. */
+ pkgAcquire::ItemDesc Desc;
+
+ enum RenameOnErrorState {
+ HashSumMismatch,
+ SizeMismatch,
+ InvalidFormat,
+ SignatureError,
+ NotClearsigned,
+ MaximumSizeExceeded,
+ PDiffError,
+ };
+
+ /** \brief Rename failed file and set error
+ *
+ * \param state respresenting the error we encountered
+ */
+ bool RenameOnError(RenameOnErrorState const state);
+
+ /** \brief Insert this item into its owner's queue.
+ *
+ * The method is designed to check if the request would end
+ * in an IMSHit and if it determines that it would, it isn't
+ * queueing the Item and instead sets it to completion instantly.
+ *
+ * \param Item Metadata about this item (its URI and
+ * description).
+ * \return true if the item was inserted, false if IMSHit was detected
+ */
+ virtual bool QueueURI(ItemDesc &Item);
+
+ /** \brief Remove this item from its owner's queue. */
+ void Dequeue();
+
+ /** \brief Rename a file without modifying its timestamp.
+ *
+ * Many item methods call this as their final action.
+ *
+ * \param From The file to be renamed.
+ *
+ * \param To The new name of \a From. If \a To exists it will be
+ * overwritten. If \a From and \a To are equal nothing happens.
+ */
+ bool Rename(std::string const &From, std::string const &To);
+
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const;
+
+ private:
+ class Private;
+ Private * const d;
+
+ friend class pkgAcqMetaBase;
+ friend class pkgAcqMetaClearSig;
+};
+ /*}}}*/
+class APT_HIDDEN pkgAcqTransactionItem: public pkgAcquire::Item /*{{{*/
+/** \brief baseclass for the indexes files to manage them all together */
+{
+ void * const d;
+ protected:
+ HashStringList GetExpectedHashesFor(std::string const &MetaKey) const;
+
+ bool QueueURI(pkgAcquire::ItemDesc &Item) APT_OVERRIDE;
+
+ public:
+ IndexTarget const Target;
+
+ /** \brief storge name until a transaction is finished */
+ std::string PartialFile;
+
+ /** \brief TransactionManager */
+ pkgAcqMetaClearSig * const TransactionManager;
+
+ enum TransactionStates {
+ TransactionStarted,
+ TransactionCommit,
+ TransactionAbort,
+ };
+ virtual bool TransactionState(TransactionStates const state);
+
+ virtual std::string DescURI() const APT_OVERRIDE { return Target.URI; }
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE;
+ virtual std::string GetMetaKey() const;
+ virtual bool HashesRequired() const APT_OVERRIDE;
+ virtual bool AcquireByHash() const;
+
+ pkgAcqTransactionItem(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager, IndexTarget const &Target) APT_NONNULL(2, 3);
+ virtual ~pkgAcqTransactionItem();
+
+ friend class pkgAcqMetaBase;
+ friend class pkgAcqMetaClearSig;
+};
+ /*}}}*/
+class APT_HIDDEN pkgAcqMetaBase : public pkgAcqTransactionItem /*{{{*/
+/** \brief the manager of a transaction */
+{
+ void * const d;
+ protected:
+ std::vector<pkgAcqTransactionItem*> Transaction;
+
+ /** \brief If \b true, the index's signature is currently being verified.
+ */
+ bool AuthPass;
+
+ /** \brief Called when a file is finished being retrieved.
+ *
+ * If the file was not downloaded to DestFile, a copy process is
+ * set up to copy it to DestFile; otherwise, Complete is set to \b
+ * true and the file is moved to its final location.
+ *
+ * \param Message The message block received from the fetch
+ * subprocess.
+ */
+ bool CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const;
+
+ /** \brief Queue the downloaded Signature for verification */
+ void QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature);
+
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+
+ /** \brief Called when authentication succeeded.
+ *
+ * Sanity-checks the authenticated file, queues up the individual
+ * index files for download, and saves the signature in the lists
+ * directory next to the authenticated list file.
+ *
+ * \param Message The message block received from the fetch
+ * subprocess.
+ * \param Cnf The method and its configuration which handled the request
+ */
+ bool CheckAuthDone(std::string const &Message, pkgAcquire::MethodConfig const *const Cnf);
+
+ /** Check if the current item should fail at this point */
+ bool CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message);
+
+ /** \brief Check that the release file is a release file for the
+ * correct distribution.
+ *
+ * \return \b true if no fatal errors were encountered.
+ */
+ bool VerifyVendor(std::string const &Message);
+
+ virtual bool TransactionState(TransactionStates const state) APT_OVERRIDE;
+
+ public:
+ // This refers more to the Transaction-Manager than the actual file
+ bool IMSHit;
+ TransactionStates State;
+ std::string BaseURI;
+
+ virtual bool QueueURI(pkgAcquire::ItemDesc &Item) APT_OVERRIDE;
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE;
+ virtual bool HashesRequired() const APT_OVERRIDE;
+
+ // transaction code
+ void Add(pkgAcqTransactionItem * const I);
+ void AbortTransaction();
+ bool TransactionHasError() const;
+ void CommitTransaction();
+
+ /** \brief Stage (queue) a copy action when the transaction is committed
+ */
+ void TransactionStageCopy(pkgAcqTransactionItem * const I,
+ const std::string &From,
+ const std::string &To);
+ /** \brief Stage (queue) a removal action when the transaction is committed
+ */
+ void TransactionStageRemoval(pkgAcqTransactionItem * const I, const std::string &FinalFile);
+
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const APT_OVERRIDE;
+
+ pkgAcqMetaBase(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &DataTarget) APT_NONNULL(2, 3);
+ virtual ~pkgAcqMetaBase();
+};
+ /*}}}*/
+/** \brief An item that is responsible for downloading the meta-index {{{
+ * file (i.e., Release) itself and verifying its signature.
+ *
+ * Once the download and verification are complete, the downloads of
+ * the individual index files are queued up using pkgAcqDiffIndex.
+ * If the meta-index file had a valid signature, the expected hashsums
+ * of the index files will be the md5sums listed in the meta-index;
+ * otherwise, the expected hashsums will be "" (causing the
+ * authentication of the index files to be bypassed).
+ */
+class APT_HIDDEN pkgAcqMetaIndex : public pkgAcqMetaBase
+{
+ void * const d;
+ protected:
+ IndexTarget const DetachedSigTarget;
+
+ /** \brief delayed constructor */
+ void Init(std::string const &URIDesc, std::string const &ShortDesc);
+
+ public:
+ virtual std::string DescURI() const APT_OVERRIDE;
+
+ // Specialized action members
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+
+ /** \brief Create a new pkgAcqMetaIndex. */
+ pkgAcqMetaIndex(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &DataTarget, IndexTarget const &DetachedSigTarget) APT_NONNULL(2, 3);
+ virtual ~pkgAcqMetaIndex();
+
+ friend class pkgAcqMetaSig;
+};
+ /*}}}*/
+/** \brief An acquire item that downloads the detached signature {{{
+ * of a meta-index (Release) file, then queues up the release
+ * file itself.
+ *
+ * \todo Why protected members?
+ *
+ * \sa pkgAcqMetaIndex
+ */
+class APT_HIDDEN pkgAcqMetaSig : public pkgAcqTransactionItem
+{
+ void * const d;
+
+ pkgAcqMetaIndex * const MetaIndex;
+
+ /** \brief The file we use to verify the MetaIndexFile with (not always set!) */
+ std::string MetaIndexFileSignature;
+
+ protected:
+
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const APT_OVERRIDE;
+
+ public:
+ virtual bool HashesRequired() const APT_OVERRIDE { return false; }
+
+ // Specialized action members
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+
+ /** \brief Create a new pkgAcqMetaSig. */
+ pkgAcqMetaSig(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target, pkgAcqMetaIndex * const MetaIndex) APT_NONNULL(2, 3, 5);
+ virtual ~pkgAcqMetaSig();
+};
+ /*}}}*/
+/** \brief An item responsible for downloading clearsigned metaindexes {{{*/
+class APT_HIDDEN pkgAcqMetaClearSig : public pkgAcqMetaIndex
+{
+ void * const d;
+ IndexTarget const DetachedDataTarget;
+
+ public:
+ /** \brief A package-system-specific parser for the meta-index file. */
+ metaIndex *MetaIndexParser;
+ metaIndex *LastMetaIndexParser;
+
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+ virtual bool VerifyDone(std::string const &Message, pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Finished() APT_OVERRIDE;
+
+ /** \brief Starts downloading the individual index files.
+ *
+ * \param verify If \b true, only indices whose expected hashsum
+ * can be determined from the meta-index will be downloaded, and
+ * the hashsums of indices will be checked (reporting
+ * #StatAuthError if there is a mismatch). If verify is \b false,
+ * no hashsum checking will be performed.
+ */
+ void QueueIndexes(bool const verify);
+
+ /** \brief Create a new pkgAcqMetaClearSig. */
+ pkgAcqMetaClearSig(pkgAcquire * const Owner,
+ IndexTarget const &ClearsignedTarget,
+ IndexTarget const &DetachedDataTarget,
+ IndexTarget const &DetachedSigTarget,
+ metaIndex * const MetaIndexParser);
+ virtual ~pkgAcqMetaClearSig();
+};
+ /*}}}*/
+/** \brief Common base class for all classes that deal with fetching indexes {{{*/
+class APT_HIDDEN pkgAcqBaseIndex : public pkgAcqTransactionItem
+{
+ void * const d;
+
+ public:
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const APT_OVERRIDE;
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+
+ pkgAcqBaseIndex(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target) APT_NONNULL(2, 3);
+ virtual ~pkgAcqBaseIndex();
+};
+ /*}}}*/
+/** \brief An acquire item that is responsible for fetching an index {{{
+ * file (e.g., Packages or Sources).
+ *
+ * \sa pkgAcqDiffIndex, pkgAcqIndexDiffs, pkgAcqIndexTrans
+ *
+ * \todo Why does pkgAcqIndex have protected members?
+ */
+class APT_HIDDEN pkgAcqIndex : public pkgAcqBaseIndex
+{
+ void * const d;
+
+ protected:
+
+ /** \brief The stages the method goes through
+ *
+ * The method first downloads the indexfile, then its decompressed (or
+ * copied) and verified
+ */
+ enum AllStages {
+ STAGE_DOWNLOAD,
+ STAGE_DECOMPRESS_AND_VERIFY,
+ };
+ AllStages Stage;
+
+ /** \brief Handle what needs to be done when the download is done */
+ void StageDownloadDone(std::string const &Message);
+
+ /** \brief Handle what needs to be done when the decompression/copy is
+ * done
+ */
+ void StageDecompressDone();
+
+ /** \brief If \b set, this partially downloaded file will be
+ * removed when the download completes.
+ */
+ std::string EraseFileName;
+
+ /** \brief The compression-related file extensions that are being
+ * added to the downloaded file one by one if first fails (e.g., "gz bz2").
+ */
+ std::string CompressionExtensions;
+
+ /** \brief The actual compression extension currently used */
+ std::string CurrentCompressionExtension;
+
+ /** \brief Do the changes needed to fetch via AptByHash (if needed) */
+ void InitByHashIfNeeded();
+
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const APT_OVERRIDE;
+
+ virtual bool TransactionState(TransactionStates const state) APT_OVERRIDE;
+
+ public:
+ // Specialized action members
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE {return Desc.URI;};
+ virtual std::string GetMetaKey() const APT_OVERRIDE;
+
+ pkgAcqIndex(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target, bool const Derived = false) APT_NONNULL(2, 3);
+ virtual ~pkgAcqIndex();
+
+ protected:
+ APT_HIDDEN void Init(std::string const &URI, std::string const &URIDesc,
+ std::string const &ShortDesc);
+ APT_HIDDEN bool CommonFailed(std::string const &TargetURI,
+ std::string const &Message, pkgAcquire::MethodConfig const *const Cnf);
+};
+ /*}}}*/
+struct APT_HIDDEN DiffInfo { /*{{{*/
+ /** The filename of the diff. */
+ std::string file;
+
+ /** The hashes of the file after the diff is applied */
+ HashStringList result_hashes;
+
+ /** The hashes of the diff */
+ HashStringList patch_hashes;
+
+ /** The hashes of the compressed diff */
+ HashStringList download_hashes;
+};
+ /*}}}*/
+/** \brief An item that is responsible for fetching an index file of {{{
+ * package list diffs and starting the package list's download.
+ *
+ * This item downloads the Index file and parses it, then enqueues
+ * additional downloads of either the individual patches (using
+ * pkgAcqIndexDiffs) or the entire Packages file (using pkgAcqIndex).
+ *
+ * \sa pkgAcqIndexDiffs, pkgAcqIndex
+ */
+class APT_HIDDEN pkgAcqDiffIndex : public pkgAcqIndex
+{
+ void * const d;
+ std::vector<pkgAcqIndexMergeDiffs*> * diffs;
+ std::vector<DiffInfo> available_patches;
+ bool pdiff_merge;
+
+ protected:
+ /** \brief If \b true, debugging information will be written to std::clog. */
+ bool Debug;
+
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const APT_OVERRIDE;
+
+ virtual bool QueueURI(pkgAcquire::ItemDesc &Item) APT_OVERRIDE;
+
+ virtual bool TransactionState(TransactionStates const state) APT_OVERRIDE;
+ public:
+ // Specialized action members
+ virtual void Failed(std::string const &Message, pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual bool VerifyDone(std::string const &Message, pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE {return Target.URI + "Index";};
+ virtual std::string GetMetaKey() const APT_OVERRIDE;
+
+ /** \brief Parse the Index file for a set of Packages diffs.
+ *
+ * Parses the Index file and creates additional download items as
+ * necessary.
+ *
+ * \param IndexDiffFile The name of the Index file.
+ *
+ * \return \b true if the Index file was successfully parsed, \b
+ * false otherwise.
+ */
+ bool ParseDiffIndex(std::string const &IndexDiffFile);
+
+ /** \brief Create a new pkgAcqDiffIndex.
+ *
+ * \param Owner The Acquire object that owns this item.
+ *
+ * \param URI The URI of the list file to download.
+ *
+ * \param URIDesc A long description of the list file to download.
+ *
+ * \param ShortDesc A short description of the list file to download.
+ */
+ pkgAcqDiffIndex(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager,
+ IndexTarget const &Target) APT_NONNULL(2, 3);
+ virtual ~pkgAcqDiffIndex();
+ private:
+ APT_HIDDEN void QueueOnIMSHit() const;
+};
+ /*}}}*/
+/** \brief An item that is responsible for fetching client-merge patches {{{
+ * that need to be applied to a given package index file.
+ *
+ * Instead of downloading and applying each patch one by one like its
+ * sister #pkgAcqIndexDiffs this class will download all patches at once
+ * and call rred with all the patches downloaded once. Rred will then
+ * merge and apply them in one go, which should be a lot faster – but is
+ * incompatible with server-based merges of patches like reprepro can do.
+ *
+ * \sa pkgAcqDiffIndex, pkgAcqIndex
+ */
+class APT_HIDDEN pkgAcqIndexMergeDiffs : public pkgAcqBaseIndex
+{
+ protected:
+
+ /** \brief If \b true, debugging output will be written to
+ * std::clog.
+ */
+ bool Debug;
+
+ /** \brief information about the current patch */
+ struct DiffInfo const patch;
+
+ /** \brief list of all download items for the patches */
+ std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches;
+
+ /** The current status of this patch. */
+ enum DiffState
+ {
+ /** \brief The diff is currently being fetched. */
+ StateFetchDiff,
+
+ /** \brief The diff is currently being applied. */
+ StateApplyDiff,
+
+ /** \brief the work with this diff is done */
+ StateDoneDiff,
+
+ /** \brief something bad happened and fallback was triggered */
+ StateErrorDiff
+ } State;
+
+ public:
+ /** \brief Called when the patch file failed to be downloaded.
+ *
+ * This method will fall back to downloading the whole index file
+ * outright; its arguments are ignored.
+ */
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE {return Target.URI + "Index";};
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE;
+ virtual bool HashesRequired() const APT_OVERRIDE;
+ virtual bool AcquireByHash() const APT_OVERRIDE;
+
+ /** \brief Create an index merge-diff item.
+ *
+ * \param Owner The pkgAcquire object that owns this item.
+ * \param TransactionManager responsible for this item
+ * \param Target we intend to built via pdiff patching
+ * \param baseURI is the URI used for the Index, but stripped down to Target
+ * \param DiffInfo of the patch in question
+ * \param patch contains infos about the patch this item is supposed
+ * to download which were read from the index
+ * \param allPatches contains all related items so that each item can
+ * check if it was the last one to complete the download step
+ */
+ pkgAcqIndexMergeDiffs(pkgAcquire *const Owner, pkgAcqMetaClearSig *const TransactionManager,
+ IndexTarget const &Target, DiffInfo const &patch,
+ std::vector<pkgAcqIndexMergeDiffs *> const *const allPatches) APT_NONNULL(2, 3, 6);
+ virtual ~pkgAcqIndexMergeDiffs();
+};
+ /*}}}*/
+/** \brief An item that is responsible for fetching server-merge patches {{{
+ * that need to be applied to a given package index file.
+ *
+ * After downloading and applying a single patch, this item will
+ * enqueue a new pkgAcqIndexDiffs to download and apply the remaining
+ * patches. If no patch can be found that applies to an intermediate
+ * file or if one of the patches cannot be downloaded, falls back to
+ * downloading the entire package index file using pkgAcqIndex.
+ *
+ * \sa pkgAcqDiffIndex, pkgAcqIndex
+ */
+class APT_HIDDEN pkgAcqIndexDiffs : public pkgAcqBaseIndex
+{
+ private:
+
+ /** \brief Queue up the next diff download.
+ *
+ * Search for the next available diff that applies to the file
+ * that currently exists on disk, and enqueue it by calling
+ * QueueURI().
+ *
+ * \return \b true if an applicable diff was found, \b false
+ * otherwise.
+ */
+ APT_HIDDEN bool QueueNextDiff();
+
+ /** \brief Handle tasks that must be performed after the item
+ * finishes downloading.
+ *
+ * Dequeues the item and checks the resulting file's hashsums
+ * against ExpectedHashes after the last patch was applied.
+ * There is no need to check the md5/sha1 after a "normal"
+ * patch because QueueNextDiff() will check the sha1 later.
+ *
+ * \param allDone If \b true, the file was entirely reconstructed,
+ * and its md5sum is verified.
+ */
+ APT_HIDDEN void Finish(bool const allDone=false);
+
+ protected:
+
+ /** \brief If \b true, debugging output will be written to
+ * std::clog.
+ */
+ bool Debug;
+
+ /** The patches that remain to be downloaded, including the patch
+ * being downloaded right now. This list should be ordered so
+ * that each diff appears before any diff that depends on it.
+ *
+ * \todo These are indexed by sha1sum; why not use some sort of
+ * dictionary instead of relying on ordering and stripping them
+ * off the front?
+ */
+ std::vector<DiffInfo> available_patches;
+
+ /** The current status of this patch. */
+ enum DiffState
+ {
+ /** \brief The diff is currently being fetched. */
+ StateFetchDiff,
+
+ /** \brief The diff is currently being applied. */
+ StateApplyDiff
+ } State;
+
+ public:
+
+ /** \brief Called when the patch file failed to be downloaded.
+ *
+ * This method will fall back to downloading the whole index file
+ * outright; its arguments are ignored.
+ */
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE {return Target.URI + "IndexDiffs";};
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE;
+ virtual bool HashesRequired() const APT_OVERRIDE;
+ virtual bool AcquireByHash() const APT_OVERRIDE;
+
+ /** \brief Create an index diff item.
+ *
+ * After filling in its basic fields, this invokes Finish(true) if
+ * \a diffs is empty, or QueueNextDiff() otherwise.
+ *
+ * \param Owner The pkgAcquire object that owns this item.
+ * \param TransactionManager responsible for this item
+ * \param Target we want to built via pdiff patching
+ * \param baseURI is the URI used for the Index, but stripped down to Target
+ * \param diffs The remaining diffs from the index of diffs. They
+ * should be ordered so that each diff appears before any diff
+ * that depends on it.
+ */
+ pkgAcqIndexDiffs(pkgAcquire *const Owner, pkgAcqMetaClearSig *const TransactionManager,
+ IndexTarget const &Target,
+ std::vector<DiffInfo> const &diffs = std::vector<DiffInfo>()) APT_NONNULL(2, 3);
+ virtual ~pkgAcqIndexDiffs();
+};
+ /*}}}*/
+/** \brief An item that is responsible for fetching a package file. {{{
+ *
+ * If the package file already exists in the cache, nothing will be
+ * done.
+ */
+class pkgAcqArchive : public pkgAcquire::Item
+{
+ void * const d;
+
+ bool LocalSource;
+ HashStringList ExpectedHashes;
+
+ protected:
+ /** \brief The package version being fetched. */
+ pkgCache::VerIterator Version;
+
+ /** \brief The list of sources from which to pick archives to
+ * download this package from.
+ */
+ pkgSourceList *Sources;
+
+ /** \brief A package records object, used to look up the file
+ * corresponding to each version of the package.
+ */
+ pkgRecords *Recs;
+
+ /** \brief A location in which the actual filename of the package
+ * should be stored.
+ */
+ std::string &StoreFilename;
+
+ /** \brief The next file for this version to try to download. */
+ APT_DEPRECATED_MSG("Unused member")
+ pkgCache::VerFileIterator Vf;
+
+ /** \brief How many (more) times to try to find a new source from
+ * which to download this package version if it fails.
+ *
+ * Set from Acquire::Retries.
+ */
+ APT_DEPRECATED_MSG("Unused member. See pkgAcqItem::Retries.")
+ unsigned int Retries;
+
+ /** \brief \b true if this version file is being downloaded from a
+ * trusted source.
+ */
+ bool Trusted;
+
+ /** \brief Queue up the next available file for this version. */
+ bool QueueNext();
+
+ /** \brief Get the full pathname of the final file for the current URI */
+ virtual std::string GetFinalFilename() const APT_OVERRIDE;
+
+ public:
+
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &Hashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE;
+ virtual std::string ShortDesc() const APT_OVERRIDE;
+ virtual void Finished() APT_OVERRIDE;
+ virtual bool IsTrusted() const APT_OVERRIDE;
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE;
+ virtual bool HashesRequired() const APT_OVERRIDE;
+
+ /** \brief Create a new pkgAcqArchive.
+ *
+ * \param Owner The pkgAcquire object with which this item is
+ * associated.
+ *
+ * \param Sources The sources from which to download version
+ * files.
+ *
+ * \param Recs A package records object, used to look up the file
+ * corresponding to each version of the package.
+ *
+ * \param Version The package version to download.
+ *
+ * \param[out] StoreFilename A location in which the actual filename of
+ * the package should be stored. It will be set to a guessed
+ * basename in the constructor, and filled in with a fully
+ * qualified filename once the download finishes.
+ */
+ pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
+ pkgRecords * const Recs,pkgCache::VerIterator const &Version,
+ std::string &StoreFilename);
+ virtual ~pkgAcqArchive();
+};
+ /*}}}*/
+/** \brief Retrieve the changelog for the given version {{{
+ *
+ * Downloads the changelog to a temporary file it will also remove again
+ * while it is deconstructed or downloads it to a named location.
+ */
+class pkgAcqChangelog : public pkgAcquire::Item
+{
+ class Private;
+ Private * const d;
+ std::string TemporaryDirectory;
+ std::string const SrcName;
+ std::string const SrcVersion;
+
+ public:
+ // we will never have hashes for changelogs.
+ // If you need verified ones, download the deb and extract the changelog.
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE { return HashStringList(); }
+ virtual bool HashesRequired() const APT_OVERRIDE { return false; }
+
+ // Specialized action members
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE {return Desc.URI;};
+
+ /** returns the URI to the changelog of this version
+ *
+ * @param Ver is the version to get the changelog for
+ * @return the URI which will be used to acquire the changelog
+ */
+ static std::string URI(pkgCache::VerIterator const &Ver);
+
+ /** returns the URI to the changelog of this version
+ *
+ * \param Rls is the Release file the package comes from
+ * \param Component in which the package resides, can be empty
+ * \param SrcName is the source package name
+ * \param SrcVersion is the source package version
+ * @return the URI which will be used to acquire the changelog
+ */
+ static std::string URI(pkgCache::RlsFileIterator const &Rls,
+ char const * const Component, char const * const SrcName,
+ char const * const SrcVersion);
+
+ /** returns the URI to the changelog of this version
+ *
+ * \param Template URI where @CHANGEPATH@ has to be filled in
+ * \param Component in which the package resides, can be empty
+ * \param SrcName is the source package name
+ * \param SrcVersion is the source package version
+ * @return the URI which will be used to acquire the changelog
+ */
+ static std::string URI(std::string const &Template,
+ char const * const Component, char const * const SrcName,
+ char const * const SrcVersion);
+
+ /** returns the URI template for this release file
+ *
+ * \param Rls is a Release file
+ * @return the URI template to use for this release file
+ */
+ static std::string URITemplate(pkgCache::RlsFileIterator const &Rls);
+
+ /** \brief Create a new pkgAcqChangelog object.
+ *
+ * \param Owner The pkgAcquire object with which this object is
+ * associated.
+ * \param Ver is the version to get the changelog for
+ * \param DestDir The directory the file should be downloaded into.
+ * Will be an autocreated (and cleaned up) temporary directory if not set.
+ * \param DestFilename The filename the file should have in #DestDir
+ * Defaults to sourcepackagename.changelog if not set.
+ */
+ pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
+ std::string const &DestDir="", std::string const &DestFilename="");
+
+ /** \brief Create a new pkgAcqChangelog object.
+ *
+ * \param Owner The pkgAcquire object with which this object is
+ * associated.
+ * \param Rls is the Release file the package comes from
+ * \param Component in which the package resides, can be empty
+ * \param SrcName is the source package name
+ * \param SrcVersion is the source package version
+ * \param DestDir The directory the file should be downloaded into.
+ * Will be an autocreated (and cleaned up) temporary directory if not set.
+ * \param DestFilename The filename the file should have in #DestDir
+ * Defaults to sourcepackagename.changelog if not set.
+ */
+ pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &Rls,
+ char const * const Component, char const * const SrcName, char const * const SrcVersion,
+ std::string const &DestDir="", std::string const &DestFilename="");
+
+ /** \brief Create a new pkgAcqChangelog object.
+ *
+ * \param Owner The pkgAcquire object with which this object is
+ * associated.
+ * \param URI is to be used to get the changelog
+ * \param SrcName is the source package name
+ * \param SrcVersion is the source package version
+ * \param DestDir The directory the file should be downloaded into.
+ * Will be an autocreated (and cleaned up) temporary directory if not set.
+ * \param DestFilename The filename the file should have in #DestDir
+ * Defaults to sourcepackagename.changelog if not set.
+ */
+ pkgAcqChangelog(pkgAcquire * const Owner, std::string const &URI,
+ char const * const SrcName, char const * const SrcVersion,
+ std::string const &DestDir="", std::string const &DestFilename="");
+
+ virtual ~pkgAcqChangelog();
+
+private:
+ APT_HIDDEN void Init(std::string const &DestDir, std::string const &DestFilename);
+};
+ /*}}}*/
+/** \brief Retrieve an arbitrary file to the current directory. {{{
+ *
+ * The file is retrieved even if it is accessed via a URL type that
+ * normally is a NOP, such as "file". If the download fails, the
+ * partial file is renamed to get a ".FAILED" extension.
+ */
+class pkgAcqFile : public pkgAcquire::Item
+{
+ void * const d;
+
+ /** \brief How many times to retry the download, set from
+ * Acquire::Retries.
+ */
+ APT_DEPRECATED_MSG("Unused member. See pkgAcqItem::Retries.")
+ unsigned int Retries;
+
+ /** \brief Should this file be considered a index file */
+ bool IsIndexFile;
+
+ HashStringList const ExpectedHashes;
+ public:
+ virtual HashStringList GetExpectedHashes() const APT_OVERRIDE;
+ virtual bool HashesRequired() const APT_OVERRIDE;
+
+ // Specialized action members
+ virtual void Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const * const Cnf) APT_OVERRIDE;
+ virtual std::string DescURI() const APT_OVERRIDE {return Desc.URI;};
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+
+ /** \brief Create a new pkgAcqFile object.
+ *
+ * \param Owner The pkgAcquire object with which this object is
+ * associated.
+ *
+ * \param URI The URI to download.
+ *
+ * \param Hashes The hashsums of the file to download, if they are known;
+ * otherwise empty list.
+ *
+ * \param Size The size of the file to download, if it is known;
+ * otherwise 0.
+ *
+ * \param Desc A description of the file being downloaded.
+ *
+ * \param ShortDesc A brief description of the file being
+ * downloaded.
+ *
+ * \param DestDir The directory the file should be downloaded into.
+ *
+ * \param DestFilename The filename+path the file is downloaded to.
+ *
+ * \param IsIndexFile The file is considered a IndexFile and cache-control
+ * headers like "cache-control: max-age=0" are send
+ *
+ * If DestFilename is empty, download to DestDir/\<basename\> if
+ * DestDir is non-empty, $CWD/\<basename\> otherwise. If
+ * DestFilename is NOT empty, DestDir is ignored and DestFilename
+ * is the absolute name to which the file should be downloaded.
+ */
+
+ pkgAcqFile(pkgAcquire * const Owner, std::string const &URI, HashStringList const &Hashes, unsigned long long const Size,
+ std::string const &Desc, std::string const &ShortDesc,
+ std::string const &DestDir="", std::string const &DestFilename="",
+ bool const IsIndexFile=false);
+ virtual ~pkgAcqFile();
+};
+ /*}}}*/
+class APT_HIDDEN pkgAcqAuxFile : public pkgAcqFile /*{{{*/
+{
+ pkgAcquire::Item *const Owner;
+ pkgAcquire::Worker *const Worker;
+ unsigned long long MaximumSize;
+
+ public:
+ virtual void Failed(std::string const &Message, pkgAcquire::MethodConfig const *const Cnf) APT_OVERRIDE;
+ virtual void Done(std::string const &Message, HashStringList const &CalcHashes,
+ pkgAcquire::MethodConfig const *const Cnf) APT_OVERRIDE;
+ virtual std::string Custom600Headers() const APT_OVERRIDE;
+ virtual void Finished() APT_OVERRIDE;
+
+ pkgAcqAuxFile(pkgAcquire::Item *const Owner, pkgAcquire::Worker *const Worker,
+ std::string const &ShortDesc, std::string const &Desc, std::string const &URI,
+ HashStringList const &Hashes, unsigned long long const MaximumSize);
+ virtual ~pkgAcqAuxFile();
+};
+ /*}}}*/
+/** @} */
+
+#endif
diff --git a/apt-pkg/acquire-method.cc b/apt-pkg/acquire-method.cc
new file mode 100644
index 0000000..ae5ae4a
--- /dev/null
+++ b/apt-pkg/acquire-method.cc
@@ -0,0 +1,565 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Method
+
+ This is a skeleton class that implements most of the functionality
+ of a method and some useful functions to make method implementation
+ simpler. The methods all derive this and specialize it. The most
+ complex implementation is the http method which needs to provide
+ pipelining, it runs the message engine at the same time it is
+ downloading files..
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-method.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/md5.h>
+#include <apt-pkg/sha1.h>
+#include <apt-pkg/sha2.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+ /*}}}*/
+
+using namespace std;
+
+// poor mans unordered_map::try_emplace for C++11 as it is a C++17 feature /*{{{*/
+template <typename Arg>
+static void try_emplace(std::unordered_map<std::string, std::string> &fields, std::string &&name, Arg &&value)
+{
+ if (fields.find(name) == fields.end())
+ fields.emplace(std::move(name), std::forward<Arg>(value));
+}
+ /*}}}*/
+
+// AcqMethod::pkgAcqMethod - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* This constructs the initialization text */
+pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
+{
+ std::unordered_map<std::string, std::string> fields;
+ try_emplace(fields, "Version", Ver);
+ if ((Flags & SingleInstance) == SingleInstance)
+ try_emplace(fields, "Single-Instance", "true");
+
+ if ((Flags & Pipeline) == Pipeline)
+ try_emplace(fields, "Pipeline", "true");
+
+ if ((Flags & SendConfig) == SendConfig)
+ try_emplace(fields, "Send-Config", "true");
+
+ if ((Flags & LocalOnly) == LocalOnly)
+ try_emplace(fields, "Local-Only", "true");
+
+ if ((Flags & NeedsCleanup) == NeedsCleanup)
+ try_emplace(fields, "Needs-Cleanup", "true");
+
+ if ((Flags & Removable) == Removable)
+ try_emplace(fields, "Removable", "true");
+
+ if ((Flags & AuxRequests) == AuxRequests)
+ try_emplace(fields, "AuxRequests", "true");
+
+ SendMessage("100 Capabilities", std::move(fields));
+
+ SetNonBlock(STDIN_FILENO,true);
+
+ Queue = 0;
+ QueueBack = 0;
+}
+ /*}}}*/
+void pkgAcqMethod::SendMessage(std::string const &header, std::unordered_map<std::string, std::string> &&fields) /*{{{*/
+{
+ auto CheckKey = [](std::string const &str) {
+ // Space, hyphen-minus, and alphanum are allowed for keys/headers.
+ return str.find_first_not_of(" -0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") == std::string::npos;
+ };
+
+ auto CheckValue = [](std::string const &str) {
+ return std::all_of(str.begin(), str.end(), [](unsigned char c) -> bool {
+ return c > 127 // unicode
+ || (c > 31 && c < 127) // printable chars
+ || c == '\n' || c == '\t'; // special whitespace
+ });
+ };
+
+ auto Error = [this]() {
+ _error->Error("SECURITY: Message contains control characters, rejecting.");
+ _error->DumpErrors();
+ SendMessage("400 URI Failure", {{"URI", "<UNKNOWN>"}, {"Message", "SECURITY: Message contains control characters, rejecting."}});
+ abort();
+ };
+
+ if (!CheckKey(header))
+ return Error();
+
+ for (auto const &f : fields)
+ {
+ if (!CheckKey(f.first))
+ return Error();
+ if (!CheckValue(f.second))
+ return Error();
+ }
+
+ std::cout << header << '\n';
+ for (auto const &f : fields)
+ {
+ if (f.second.empty())
+ continue;
+ std::cout << f.first << ": ";
+ auto const lines = VectorizeString(f.second, '\n');
+ if (likely(lines.empty() == false))
+ {
+ std::copy(lines.begin(), std::prev(lines.end()), std::ostream_iterator<std::string>(std::cout, "\n "));
+ std::cout << *lines.rbegin();
+ }
+ std::cout << '\n';
+ }
+ std::cout << '\n'
+ << std::flush;
+}
+ /*}}}*/
+// AcqMethod::Fail - A fetch has failed /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqMethod::Fail(bool Transient)
+{
+ string Err = "Undetermined Error";
+ if (_error->empty() == false)
+ {
+ Err.clear();
+ while (_error->empty() == false)
+ {
+ std::string msg;
+ if (_error->PopMessage(msg))
+ {
+ if (Err.empty() == false)
+ Err.append("\n");
+ Err.append(msg);
+ }
+ }
+ }
+ Fail(Err, Transient);
+}
+ /*}}}*/
+// AcqMethod::Fail - A fetch has failed /*{{{*/
+void pkgAcqMethod::Fail(string Err,bool Transient)
+{
+ // Strip out junk from the error messages
+ std::transform(Err.begin(), Err.end(), Err.begin(), [](char const c) {
+ if (c == '\r' || c == '\n')
+ return ' ';
+ return c;
+ });
+ if (IP.empty() == false && _config->FindB("Acquire::Failure::ShowIP", true) == true)
+ Err.append(" ").append(IP);
+
+ std::unordered_map<std::string, std::string> fields;
+ if (Queue != nullptr)
+ try_emplace(fields, "URI", Queue->Uri);
+ else
+ try_emplace(fields, "URI", "<UNKNOWN>");
+ try_emplace(fields, "Message", Err);
+
+ if(FailReason.empty() == false)
+ try_emplace(fields, "FailReason", FailReason);
+ if (UsedMirror.empty() == false)
+ try_emplace(fields, "UsedMirror", UsedMirror);
+ if (Transient == true)
+ try_emplace(fields, "Transient-Failure", "true");
+
+ SendMessage("400 URI Failure", std::move(fields));
+
+ if (Queue != nullptr)
+ Dequeue();
+}
+ /*}}}*/
+// AcqMethod::DropPrivsOrDie - Drop privileges or die /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqMethod::DropPrivsOrDie()
+{
+ if (!DropPrivileges()) {
+ Fail(false);
+ exit(112); /* call the european emergency number */
+ }
+}
+
+ /*}}}*/
+// AcqMethod::URIStart - Indicate a download is starting /*{{{*/
+void pkgAcqMethod::URIStart(FetchResult &Res)
+{
+ if (Queue == 0)
+ abort();
+
+ std::unordered_map<std::string, std::string> fields;
+ try_emplace(fields, "URI", Queue->Uri);
+ if (Res.Size != 0)
+ try_emplace(fields, "Size", std::to_string(Res.Size));
+ if (Res.LastModified != 0)
+ try_emplace(fields, "Last-Modified", TimeRFC1123(Res.LastModified, true));
+ if (Res.ResumePoint != 0)
+ try_emplace(fields, "Resume-Point", std::to_string(Res.ResumePoint));
+ if (UsedMirror.empty() == false)
+ try_emplace(fields, "UsedMirror", UsedMirror);
+
+ SendMessage("200 URI Start", std::move(fields));
+}
+ /*}}}*/
+// AcqMethod::URIDone - A URI is finished /*{{{*/
+static void printHashStringList(std::unordered_map<std::string, std::string> &fields, std::string const &Prefix, HashStringList const &list)
+{
+ for (auto const &hash : list)
+ {
+ // very old compatibility name for MD5Sum
+ if (hash.HashType() == "MD5Sum")
+ try_emplace(fields, Prefix + "MD5-Hash", hash.HashValue());
+ try_emplace(fields, Prefix + hash.HashType() + "-Hash", hash.HashValue());
+ }
+}
+void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
+{
+ if (Queue == 0)
+ abort();
+
+ std::unordered_map<std::string, std::string> fields;
+ try_emplace(fields, "URI", Queue->Uri);
+ if (Res.Filename.empty() == false)
+ try_emplace(fields, "Filename", Res.Filename);
+ if (Res.Size != 0)
+ try_emplace(fields, "Size", std::to_string(Res.Size));
+ if (Res.LastModified != 0)
+ try_emplace(fields, "Last-Modified", TimeRFC1123(Res.LastModified, true));
+ printHashStringList(fields, "", Res.Hashes);
+
+ if (UsedMirror.empty() == false)
+ try_emplace(fields, "UsedMirror", UsedMirror);
+ if (Res.GPGVOutput.empty() == false)
+ {
+ std::ostringstream os;
+ std::copy(Res.GPGVOutput.begin(), Res.GPGVOutput.end() - 1, std::ostream_iterator<std::string>(os, "\n"));
+ os << *Res.GPGVOutput.rbegin();
+ try_emplace(fields, "GPGVOutput", os.str());
+ }
+ if (Res.ResumePoint != 0)
+ try_emplace(fields, "Resume-Point", std::to_string(Res.ResumePoint));
+ if (Res.IMSHit == true)
+ try_emplace(fields, "IMS-Hit", "true");
+
+ if (Alt != nullptr)
+ {
+ if (Alt->Filename.empty() == false)
+ try_emplace(fields, "Alt-Filename", Alt->Filename);
+ if (Alt->Size != 0)
+ try_emplace(fields, "Alt-Size", std::to_string(Alt->Size));
+ if (Alt->LastModified != 0)
+ try_emplace(fields, "Alt-Last-Modified", TimeRFC1123(Alt->LastModified, true));
+ if (Alt->IMSHit == true)
+ try_emplace(fields, "Alt-IMS-Hit", "true");
+ printHashStringList(fields, "Alt-", Alt->Hashes);
+ }
+
+ SendMessage("201 URI Done", std::move(fields));
+ Dequeue();
+}
+ /*}}}*/
+// AcqMethod::MediaFail - Synchronous request for new media /*{{{*/
+// ---------------------------------------------------------------------
+/* This sends a 403 Media Failure message to the APT and waits for it
+ to be ackd */
+bool pkgAcqMethod::MediaFail(string Required,string Drive)
+{
+ fprintf(stdout, "403 Media Failure\nMedia: %s\nDrive: %s\n",
+ Required.c_str(),Drive.c_str());
+ std::cout << "\n" << std::flush;
+
+ vector<string> MyMessages;
+
+ /* Here we read messages until we find a 603, each non 603 message is
+ appended to the main message list for later processing */
+ while (1)
+ {
+ if (WaitFd(STDIN_FILENO) == false)
+ return false;
+
+ if (ReadMessages(STDIN_FILENO,MyMessages) == false)
+ return false;
+
+ string Message = MyMessages.front();
+ MyMessages.erase(MyMessages.begin());
+
+ // Fetch the message number
+ char *End;
+ int Number = strtol(Message.c_str(),&End,10);
+ if (End == Message.c_str())
+ {
+ cerr << "Malformed message!" << endl;
+ exit(100);
+ }
+
+ // Change ack
+ if (Number == 603)
+ {
+ while (MyMessages.empty() == false)
+ {
+ Messages.push_back(MyMessages.front());
+ MyMessages.erase(MyMessages.begin());
+ }
+
+ return !StringToBool(LookupTag(Message,"Failed"),false);
+ }
+
+ Messages.push_back(Message);
+ }
+}
+ /*}}}*/
+// AcqMethod::Configuration - Handle the configuration message /*{{{*/
+// ---------------------------------------------------------------------
+/* This parses each configuration entry and puts it into the _config
+ Configuration class. */
+bool pkgAcqMethod::Configuration(string Message)
+{
+ ::Configuration &Cnf = *_config;
+
+ const char *I = Message.c_str();
+ const char *MsgEnd = I + Message.length();
+
+ unsigned int Length = strlen("Config-Item");
+ for (; I + Length < MsgEnd; I++)
+ {
+ // Not a config item
+ if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
+ continue;
+
+ I += Length + 1;
+
+ for (; I < MsgEnd && *I == ' '; I++);
+ const char *Equals = (const char*) memchr(I, '=', MsgEnd - I);
+ if (Equals == NULL)
+ return false;
+ const char *End = (const char*) memchr(Equals, '\n', MsgEnd - Equals);
+ if (End == NULL)
+ End = MsgEnd;
+
+ Cnf.Set(DeQuoteString(string(I,Equals-I)),
+ DeQuoteString(string(Equals+1,End-Equals-1)));
+ I = End;
+ }
+
+ return true;
+}
+ /*}}}*/
+// AcqMethod::Run - Run the message engine /*{{{*/
+// ---------------------------------------------------------------------
+/* Fetch any messages and execute them. In single mode it returns 1 if
+ there are no more available messages - any other result is a
+ fatal failure code! */
+int pkgAcqMethod::Run(bool Single)
+{
+ while (1)
+ {
+ // Block if the message queue is empty
+ if (Messages.empty() == true)
+ {
+ if (Single == false)
+ if (WaitFd(STDIN_FILENO) == false)
+ break;
+ if (ReadMessages(STDIN_FILENO,Messages) == false)
+ break;
+ }
+
+ // Single mode exits if the message queue is empty
+ if (Single == true && Messages.empty() == true)
+ return -1;
+
+ string Message = Messages.front();
+ Messages.erase(Messages.begin());
+
+ // Fetch the message number
+ char *End;
+ int Number = strtol(Message.c_str(),&End,10);
+ if (End == Message.c_str())
+ {
+ cerr << "Malformed message!" << endl;
+ return 100;
+ }
+
+ switch (Number)
+ {
+ case 601:
+ if (Configuration(Message) == false)
+ return 100;
+ break;
+
+ case 600:
+ {
+ FetchItem *Tmp = new FetchItem;
+
+ Tmp->Uri = LookupTag(Message,"URI");
+ Tmp->Proxy(LookupTag(Message, "Proxy"));
+ Tmp->DestFile = LookupTag(Message,"FileName");
+ if (RFC1123StrToTime(LookupTag(Message,"Last-Modified").c_str(),Tmp->LastModified) == false)
+ Tmp->LastModified = 0;
+ Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
+ Tmp->FailIgnore = StringToBool(LookupTag(Message,"Fail-Ignore"),false);
+ Tmp->ExpectedHashes = HashStringList();
+ for (char const * const * t = HashString::SupportedHashes(); *t != NULL; ++t)
+ {
+ std::string tag = "Expected-";
+ tag.append(*t);
+ std::string const hash = LookupTag(Message, tag.c_str());
+ if (hash.empty() == false)
+ Tmp->ExpectedHashes.push_back(HashString(*t, hash));
+ }
+ char *End;
+ if (Tmp->ExpectedHashes.FileSize() > 0)
+ Tmp->MaximumSize = Tmp->ExpectedHashes.FileSize();
+ else
+ Tmp->MaximumSize = strtoll(LookupTag(Message, "Maximum-Size", "0").c_str(), &End, 10);
+ Tmp->Next = 0;
+
+ // Append it to the list
+ FetchItem **I = &Queue;
+ for (; *I != 0; I = &(*I)->Next);
+ *I = Tmp;
+ if (QueueBack == 0)
+ QueueBack = Tmp;
+
+ // Notify that this item is to be fetched.
+ if (URIAcquire(Message, Tmp) == false)
+ Fail();
+
+ break;
+ }
+ }
+ }
+
+ Exit();
+ return 0;
+}
+ /*}}}*/
+// AcqMethod::PrintStatus - privately really send a log/status message /*{{{*/
+void pkgAcqMethod::PrintStatus(char const * const header, const char* Format,
+ va_list &args) const
+{
+ string CurrentURI = "<UNKNOWN>";
+ if (Queue != 0)
+ CurrentURI = Queue->Uri;
+ if (UsedMirror.empty() == true)
+ fprintf(stdout, "%s\nURI: %s\nMessage: ",
+ header, CurrentURI.c_str());
+ else
+ fprintf(stdout, "%s\nURI: %s\nUsedMirror: %s\nMessage: ",
+ header, CurrentURI.c_str(), UsedMirror.c_str());
+ vfprintf(stdout,Format,args);
+ std::cout << "\n\n" << std::flush;
+}
+ /*}}}*/
+// AcqMethod::Log - Send a log message /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqMethod::Log(const char *Format,...)
+{
+ va_list args;
+ va_start(args,Format);
+ PrintStatus("101 Log", Format, args);
+ va_end(args);
+}
+ /*}}}*/
+// AcqMethod::Status - Send a status message /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcqMethod::Status(const char *Format,...)
+{
+ va_list args;
+ va_start(args,Format);
+ PrintStatus("102 Status", Format, args);
+ va_end(args);
+}
+ /*}}}*/
+// AcqMethod::Redirect - Send a redirect message /*{{{*/
+// ---------------------------------------------------------------------
+/* This method sends the redirect message and dequeues the item as
+ * the worker will enqueue again later on to the right queue */
+void pkgAcqMethod::Redirect(const string &NewURI)
+{
+ if (NewURI.find_first_not_of(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") != std::string::npos)
+ {
+ _error->Error("SECURITY: URL redirect target contains control characters, rejecting.");
+ Fail();
+ return;
+ }
+ std::unordered_map<std::string, std::string> fields;
+ try_emplace(fields, "URI", Queue->Uri);
+ try_emplace(fields, "New-URI", NewURI);
+ SendMessage("103 Redirect", std::move(fields));
+ Dequeue();
+}
+ /*}}}*/
+// AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
+ IMSHit(false), Size(0), ResumePoint(0), d(NULL)
+{
+}
+ /*}}}*/
+// AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
+// ---------------------------------------------------------------------
+/* This hides the number of hashes we are supporting from the caller.
+ It just deals with the hash class. */
+void pkgAcqMethod::FetchResult::TakeHashes(class Hashes &Hash)
+{
+ Hashes = Hash.GetHashStringList();
+}
+ /*}}}*/
+void pkgAcqMethod::Dequeue() { /*{{{*/
+ FetchItem const * const Tmp = Queue;
+ Queue = Queue->Next;
+ if (Tmp == QueueBack)
+ QueueBack = Queue;
+ delete Tmp;
+}
+ /*}}}*/
+pkgAcqMethod::~pkgAcqMethod() {}
+
+struct pkgAcqMethod::FetchItem::Private
+{
+ std::string Proxy;
+};
+
+pkgAcqMethod::FetchItem::FetchItem() : Next(nullptr), DestFileFd(-1), LastModified(0), IndexFile(false),
+ FailIgnore(false), MaximumSize(0), d(new Private)
+{}
+
+std::string pkgAcqMethod::FetchItem::Proxy()
+{
+ return d->Proxy;
+}
+
+void pkgAcqMethod::FetchItem::Proxy(std::string const &Proxy)
+{
+ d->Proxy = Proxy;
+}
+
+pkgAcqMethod::FetchItem::~FetchItem() { delete d; }
+
+pkgAcqMethod::FetchResult::~FetchResult() {}
diff --git a/apt-pkg/acquire-method.h b/apt-pkg/acquire-method.h
new file mode 100644
index 0000000..664b95c
--- /dev/null
+++ b/apt-pkg/acquire-method.h
@@ -0,0 +1,140 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Method - Method helper class + functions
+
+ These functions are designed to be used within the method task to
+ ease communication with APT.
+
+ ##################################################################### */
+ /*}}}*/
+
+/** \addtogroup acquire
+ * @{
+ *
+ * \file acquire-method.h
+ */
+
+#ifndef PKGLIB_ACQUIRE_METHOD_H
+#define PKGLIB_ACQUIRE_METHOD_H
+
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/macros.h>
+
+#include <stdarg.h>
+#include <time.h>
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#ifndef APT_8_CLEANER_HEADERS
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/strutl.h>
+#endif
+
+class pkgAcqMethod
+{
+ protected:
+
+ struct FetchItem
+ {
+ FetchItem *Next;
+
+ std::string Uri;
+ std::string DestFile;
+ int DestFileFd;
+ time_t LastModified;
+ bool IndexFile;
+ bool FailIgnore;
+ HashStringList ExpectedHashes;
+ // a maximum size we will download, this can be the exact filesize
+ // for when we know it or a arbitrary limit when we don't know the
+ // filesize (like a InRelease file)
+ unsigned long long MaximumSize;
+
+ FetchItem();
+ virtual ~FetchItem();
+ std::string Proxy(); // For internal use only.
+ void Proxy(std::string const &Proxy) APT_HIDDEN;
+
+ private:
+ struct Private;
+ Private *const d;
+ };
+
+ struct FetchResult
+ {
+ HashStringList Hashes;
+ std::vector<std::string> GPGVOutput;
+ time_t LastModified;
+ bool IMSHit;
+ std::string Filename;
+ unsigned long long Size;
+ unsigned long long ResumePoint;
+
+ void TakeHashes(class Hashes &Hash);
+ FetchResult();
+ virtual ~FetchResult();
+ private:
+ void * const d;
+ };
+
+ // State
+ std::vector<std::string> Messages;
+ FetchItem *Queue;
+ FetchItem *QueueBack;
+ std::string FailReason;
+ std::string UsedMirror;
+ std::string IP;
+
+ // Handlers for messages
+ virtual bool Configuration(std::string Message);
+ virtual bool Fetch(FetchItem * /*Item*/) {return true;};
+ virtual bool URIAcquire(std::string const &/*Message*/, FetchItem *Itm) { return Fetch(Itm); };
+
+ // Outgoing messages
+ void Fail(bool Transient = false);
+ inline void Fail(const char *Why, bool Transient = false) {Fail(std::string(Why),Transient);};
+ virtual void Fail(std::string Why, bool Transient = false);
+ virtual void URIStart(FetchResult &Res);
+ virtual void URIDone(FetchResult &Res,FetchResult *Alt = 0);
+ void SendMessage(std::string const &header, std::unordered_map<std::string, std::string> &&fields);
+
+ bool MediaFail(std::string Required,std::string Drive);
+ virtual void Exit() {};
+
+ void PrintStatus(char const * const header, const char* Format, va_list &args) const;
+
+ public:
+ enum CnfFlags
+ {
+ SingleInstance = (1 << 0),
+ Pipeline = (1 << 1),
+ SendConfig = (1 << 2),
+ LocalOnly = (1 << 3),
+ NeedsCleanup = (1 << 4),
+ Removable = (1 << 5),
+ AuxRequests = (1 << 6)
+ };
+
+ void Log(const char *Format,...);
+ void Status(const char *Format,...);
+
+ void Redirect(const std::string &NewURI);
+
+ int Run(bool Single = false);
+ inline void SetFailReason(std::string Msg) {FailReason = Msg;};
+ inline void SetIP(std::string aIP) {IP = aIP;};
+
+ pkgAcqMethod(const char *Ver,unsigned long Flags = 0);
+ virtual ~pkgAcqMethod();
+ void DropPrivsOrDie();
+ private:
+ APT_HIDDEN void Dequeue();
+};
+
+/** @} */
+
+#endif
diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc
new file mode 100644
index 0000000..b361861
--- /dev/null
+++ b/apt-pkg/acquire-worker.cc
@@ -0,0 +1,964 @@
+// -*- mode: cpp; mode: fold -*-
+// Description /*{{{*/
+/* ######################################################################
+
+ Acquire Worker
+
+ The worker process can startup either as a Configuration prober
+ or as a queue runner. As a configuration prober it only reads the
+ configuration message and
+
+ ##################################################################### */
+ /*}}}*/
+// Include Files /*{{{*/
+#include <config.h>
+
+#include <apt-pkg/acquire-item.h>
+#include <apt-pkg/acquire-worker.h>
+#include <apt-pkg/acquire.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/hashes.h>
+#include <apt-pkg/proxy.h>
+#include <apt-pkg/strutl.h>
+
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <sstream>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <apti18n.h>
+ /*}}}*/
+
+using namespace std;
+
+// Worker::Worker - Constructor for Queue startup /*{{{*/
+pkgAcquire::Worker::Worker(Queue *Q, MethodConfig *Cnf, pkgAcquireStatus *log) :
+ d(NULL), OwnerQ(Q), Log(log), Config(Cnf), Access(Cnf->Access),
+ CurrentItem(nullptr), CurrentSize(0), TotalSize(0)
+{
+ Construct();
+}
+ /*}}}*/
+// Worker::Worker - Constructor for method config startup /*{{{*/
+pkgAcquire::Worker::Worker(MethodConfig *Cnf) : Worker(nullptr, Cnf, nullptr)
+{
+}
+ /*}}}*/
+// Worker::Construct - Constructor helper /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+void pkgAcquire::Worker::Construct()
+{
+ NextQueue = 0;
+ NextAcquire = 0;
+ Process = -1;
+ InFd = -1;
+ OutFd = -1;
+ OutReady = false;
+ InReady = false;
+ Debug = _config->FindB("Debug::pkgAcquire::Worker",false);
+}
+ /*}}}*/
+// Worker::~Worker - Destructor /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+pkgAcquire::Worker::~Worker()
+{
+ close(InFd);
+ close(OutFd);
+
+ if (Process > 0)
+ {
+ /* Closing of stdin is the signal to exit and die when the process
+ indicates it needs cleanup */
+ if (Config->NeedsCleanup == false)
+ kill(Process,SIGINT);
+ ExecWait(Process,Access.c_str(),true);
+ }
+}
+ /*}}}*/
+// Worker::Start - Start the worker process /*{{{*/
+// ---------------------------------------------------------------------
+/* This forks the method and inits the communication channel */
+bool pkgAcquire::Worker::Start()
+{
+ // Get the method path
+ constexpr char const * const methodsDir = "Dir::Bin::Methods";
+ std::string const confItem = std::string(methodsDir) + "::" + Access;
+ std::string Method;
+ if (_config->Exists(confItem))
+ Method = _config->FindFile(confItem.c_str());
+ else if (Access == "ftp" || Access == "rsh" || Access == "ssh")
+ return _error->Error(_("The method '%s' is unsupported and disabled by default. Consider switching to http(s). Set Dir::Bin::Methods::%s to \"%s\" to enable it again."), Access.c_str(), Access.c_str(), Access.c_str());
+ else
+ Method = _config->FindDir(methodsDir) + Access;
+ if (FileExists(Method) == false)
+ {
+ if (flNotDir(Method) == "false")
+ {
+ _error->Error(_("The method '%s' is explicitly disabled via configuration."), Access.c_str());
+ if (Access == "http" || Access == "https")
+ _error->Notice(_("If you meant to use Tor remember to use %s instead of %s."), ("tor+" + Access).c_str(), Access.c_str());
+ return false;
+ }
+ _error->Error(_("The method driver %s could not be found."),Method.c_str());
+ std::string const A(Access.cbegin(), std::find(Access.cbegin(), Access.cend(), '+'));
+ std::string pkg;
+ strprintf(pkg, "apt-transport-%s", A.c_str());
+ _error->Notice(_("Is the package %s installed?"), pkg.c_str());
+ return false;
+ }
+ std::string const Calling = _config->FindDir(methodsDir) + Access;
+
+ if (Debug == true)
+ {
+ std::clog << "Starting method '" << Calling << "'";
+ if (Calling != Method)
+ std::clog << " ( via " << Method << " )";
+ std::clog << endl;
+ }
+
+ // Create the pipes
+ int Pipes[4] = {-1,-1,-1,-1};
+ if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
+ {
+ _error->Errno("pipe","Failed to create IPC pipe to subprocess");
+ for (int I = 0; I != 4; I++)
+ close(Pipes[I]);
+ return false;
+ }
+ for (int I = 0; I != 4; I++)
+ SetCloseExec(Pipes[I],true);
+
+ // Fork off the process
+ Process = ExecFork();
+ if (Process == 0)
+ {
+ // Setup the FDs
+ dup2(Pipes[1],STDOUT_FILENO);
+ dup2(Pipes[2],STDIN_FILENO);
+ SetCloseExec(STDOUT_FILENO,false);
+ SetCloseExec(STDIN_FILENO,false);
+ SetCloseExec(STDERR_FILENO,false);
+
+ const char * const Args[] = { Calling.c_str(), nullptr };
+ execv(Method.c_str() ,const_cast<char **>(Args));
+ std::cerr << "Failed to exec method " << Calling << " ( via " << Method << ")" << endl;
+ _exit(100);
+ }
+
+ // Fix up our FDs
+ InFd = Pipes[0];
+ OutFd = Pipes[3];
+ SetNonBlock(Pipes[0],true);
+ SetNonBlock(Pipes[3],true);
+ close(Pipes[1]);
+ close(Pipes[2]);
+ OutReady = false;
+ InReady = true;
+
+ // Read the configuration data
+ if (WaitFd(InFd) == false ||
+ ReadMessages() == false)
+ return _error->Error(_("Method %s did not start correctly"),Method.c_str());
+
+ RunMessages();
+ if (OwnerQ != 0)
+ SendConfiguration();
+
+ return true;
+}
+ /*}}}*/
+// Worker::ReadMessages - Read all pending messages into the list /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgAcquire::Worker::ReadMessages()
+{
+ if (::ReadMessages(InFd,MessageQueue) == false)
+ return MethodFailure();
+ return true;
+}
+ /*}}}*/
+// Worker::RunMessage - Empty the message queue /*{{{*/
+// ---------------------------------------------------------------------
+/* This takes the messages from the message queue and runs them through
+ the parsers in order. */
+enum class APT_HIDDEN MessageType
+{
+ CAPABILITIES = 100,
+ LOG = 101,
+ STATUS = 102,
+ REDIRECT = 103,
+ WARNING = 104,
+ URI_START = 200,
+ URI_DONE = 201,
+ AUX_REQUEST = 351,
+ URI_FAILURE = 400,
+ GENERAL_FAILURE = 401,
+ MEDIA_CHANGE = 403
+};
+static bool isDoomedItem(pkgAcquire::Item const * const Itm)
+{
+ auto const TransItm = dynamic_cast<pkgAcqTransactionItem const * const>(Itm);
+ if (TransItm == nullptr)
+ return false;
+ return TransItm->TransactionManager->State != pkgAcqTransactionItem::TransactionStarted;
+}
+static HashStringList GetHashesFromMessage(std::string const &Prefix, std::string const &Message)
+{
+ HashStringList hsl;
+ for (char const *const *type = HashString::SupportedHashes(); *type != NULL; ++type)
+ {
+ std::string const tagname = Prefix + *type + "-Hash";
+ std::string const hashsum = LookupTag(Message, tagname.c_str());
+ if (hashsum.empty() == false)
+ hsl.push_back(HashString(*type, hashsum));
+ }
+ return hsl;
+}
+static void APT_NONNULL(3) ChangeSiteIsMirrorChange(std::string const