]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
detect redirection loops in acquire instead of workers
[apt.git] / apt-pkg / acquire-item.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
0118833a
AL
4/* ######################################################################
5
6 Acquire Item - Item to acquire
7
8 Each item can download to exactly one file at a time. This means you
9 cannot create an item that fetches two uri's to two files at the same
10 time. The pkgAcqIndex class creates a second class upon instantiation
11 to fetch the other index files because of this.
b185acc2 12
0118833a
AL
13 ##################################################################### */
14 /*}}}*/
15// Include Files /*{{{*/
ea542140
DK
16#include <config.h>
17
0118833a
AL
18#include <apt-pkg/acquire-item.h>
19#include <apt-pkg/configuration.h>
e878aedb 20#include <apt-pkg/aptconfiguration.h>
b2e465d6 21#include <apt-pkg/sourcelist.h>
03e39e59 22#include <apt-pkg/error.h>
cdcc6d34 23#include <apt-pkg/strutl.h>
36375005 24#include <apt-pkg/fileutl.h>
ac5b205a 25#include <apt-pkg/tagfile.h>
5ad0096a 26#include <apt-pkg/metaindex.h>
453b82a3
DK
27#include <apt-pkg/acquire.h>
28#include <apt-pkg/hashes.h>
29#include <apt-pkg/indexfile.h>
30#include <apt-pkg/pkgcache.h>
31#include <apt-pkg/cacheiterators.h>
32#include <apt-pkg/pkgrecords.h>
d56e2917 33#include <apt-pkg/gpgv.h>
453b82a3 34
d7a51997 35#include <algorithm>
453b82a3
DK
36#include <stddef.h>
37#include <stdlib.h>
38#include <string.h>
39#include <iostream>
40#include <vector>
0a8a80e5
AL
41#include <sys/stat.h>
42#include <unistd.h>
c88edf1d 43#include <errno.h>
5819a761 44#include <string>
c88edf1d 45#include <stdio.h>
1ddb8596 46#include <ctime>
ac7f8f79 47#include <sstream>
2f4e4070 48#include <numeric>
ea542140
DK
49
50#include <apti18n.h>
0118833a
AL
51 /*}}}*/
52
b3d44315 53using namespace std;
5819a761 54
8d89cda7 55static void printHashSumComparison(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
b3501edb
DK
56{
57 if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
58 return;
59 std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
60 for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
61 std::cerr << "\t- " << hs->toStr() << std::endl;
62 std::cerr << " Actual Hash: " << std::endl;
63 for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
64 std::cerr << "\t- " << hs->toStr() << std::endl;
65}
66 /*}}}*/
70b63c57 67static std::string GetPartialFileName(std::string const &file) /*{{{*/
5684f71f
DK
68{
69 std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
70 DestFile += file;
71 return DestFile;
72}
70b63c57
DK
73 /*}}}*/
74static std::string GetPartialFileNameFromURI(std::string const &uri) /*{{{*/
5684f71f 75{
ea7682a0 76 return GetPartialFileName(URItoFileName(uri));
5684f71f 77}
70b63c57 78 /*}}}*/
295d848b
DK
79static std::string GetFinalFileNameFromURI(std::string const &uri) /*{{{*/
80{
81 return _config->FindDir("Dir::State::lists") + URItoFileName(uri);
82}
83 /*}}}*/
d7a51997
DK
84static std::string GetKeepCompressedFileName(std::string file, IndexTarget const &Target)/*{{{*/
85{
86 if (Target.KeepCompressed == false)
87 return file;
88
0179cfa8
DK
89 std::string const KeepCompressedAs = Target.Option(IndexTarget::KEEPCOMPRESSEDAS);
90 if (KeepCompressedAs.empty() == false)
d7a51997 91 {
0179cfa8 92 std::string const ext = KeepCompressedAs.substr(0, KeepCompressedAs.find(' '));
d7a51997
DK
93 if (ext != "uncompressed")
94 file.append(".").append(ext);
95 }
96 return file;
97}
98 /*}}}*/
36795154
DK
99static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/
100{
101 // rred expects the patch as $FinalFile.ed.$patchname.gz
102 return Final + ".ed." + Patch + ".gz";
103}
104 /*}}}*/
105static std::string GetDiffsPatchFileName(std::string const &Final) /*{{{*/
106{
107 // rred expects the patch as $FinalFile.ed
108 return Final + ".ed";
109}
110 /*}}}*/
4e3c5633 111static std::string GetExistingFilename(std::string const &File) /*{{{*/
d7a51997 112{
4e3c5633
DK
113 if (RealFileExists(File))
114 return File;
115 for (auto const &type : APT::Configuration::getCompressorExtensions())
d7a51997 116 {
4e3c5633
DK
117 std::string const Final = File + type;
118 if (RealFileExists(Final))
119 return Final;
d7a51997 120 }
4e3c5633 121 return "";
d7a51997
DK
122}
123 /*}}}*/
b7a1076f
DK
124static std::string GetDiffIndexFileName(std::string const &Name) /*{{{*/
125{
126 return Name + ".diff/Index";
127}
128 /*}}}*/
129static std::string GetDiffIndexURI(IndexTarget const &Target) /*{{{*/
130{
131 return Target.URI + ".diff/Index";
132}
133 /*}}}*/
36795154 134
3383ef4d
DK
135static void ReportMirrorFailureToCentral(pkgAcquire::Item const &I, std::string const &FailCode, std::string const &Details)/*{{{*/
136{
137 // we only act if a mirror was used at all
138 if(I.UsedMirror.empty())
139 return;
140#if 0
141 std::cerr << "\nReportMirrorFailure: "
142 << UsedMirror
143 << " Uri: " << DescURI()
144 << " FailCode: "
145 << FailCode << std::endl;
146#endif
147 string const report = _config->Find("Methods::Mirror::ProblemReporting",
148 "/usr/lib/apt/apt-report-mirror-failure");
149 if(!FileExists(report))
150 return;
151
152 std::vector<char const*> const Args = {
153 report.c_str(),
154 I.UsedMirror.c_str(),
155 I.DescURI().c_str(),
156 FailCode.c_str(),
157 Details.c_str(),
158 NULL
159 };
160
161 pid_t pid = ExecFork();
162 if(pid < 0)
163 {
164 _error->Error("ReportMirrorFailure Fork failed");
165 return;
166 }
167 else if(pid == 0)
168 {
169 execvp(Args[0], (char**)Args.data());
170 std::cerr << "Could not exec " << Args[0] << std::endl;
171 _exit(100);
172 }
173 if(!ExecWait(pid, "report-mirror-failure"))
174 _error->Warning("Couldn't report problem to '%s'", report.c_str());
175}
176 /*}}}*/
177
d03b947b 178static APT_NONNULL(2) bool MessageInsecureRepository(bool const isError, char const * const msg, std::string const &repo)/*{{{*/
32532943 179{
b1bdfe68
DK
180 std::string m;
181 strprintf(m, msg, repo.c_str());
f18f2338
DK
182 if (isError)
183 {
b1bdfe68 184 _error->Error("%s", m.c_str());
83960341 185 _error->Notice("%s", _("Updating from such a repository can't be done securely, and is therefore disabled by default."));
f18f2338
DK
186 }
187 else
188 {
b1bdfe68 189 _error->Warning("%s", m.c_str());
d04e44ac 190 _error->Notice("%s", _("Data from such a repository can't be authenticated and is therefore potentially dangerous to use."));
f18f2338 191 }
002b1bc4 192 _error->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
f18f2338 193 return false;
f18f2338
DK
194}
195 /*}}}*/
b1bdfe68
DK
196// AllowInsecureRepositories /*{{{*/
197enum class InsecureType { UNSIGNED, WEAK, NORELEASE };
d03b947b
DK
198static bool TargetIsAllowedToBe(IndexTarget const &Target, InsecureType const type)
199{
200 if (_config->FindB("Acquire::AllowInsecureRepositories"))
201 return true;
202
203 if (Target.OptionBool(IndexTarget::ALLOW_INSECURE))
204 return true;
205
206 switch (type)
207 {
208 case InsecureType::UNSIGNED: break;
209 case InsecureType::NORELEASE: break;
210 case InsecureType::WEAK:
211 if (_config->FindB("Acquire::AllowWeakRepositories"))
212 return true;
213 if (Target.OptionBool(IndexTarget::ALLOW_WEAK))
214 return true;
215 break;
216 }
217 return false;
218}
219static bool APT_NONNULL(3, 4, 5) AllowInsecureRepositories(InsecureType const msg, std::string const &repo,
f18f2338
DK
220 metaIndex const * const MetaIndexParser, pkgAcqMetaClearSig * const TransactionManager, pkgAcquire::Item * const I)
221{
b1bdfe68
DK
222 // we skip weak downgrades as its unlikely that a repository gets really weaker –
223 // its more realistic that apt got pickier in a newer version
224 if (msg != InsecureType::WEAK)
225 {
226 std::string const FinalInRelease = TransactionManager->GetFinalFilename();
227 std::string const FinalReleasegpg = FinalInRelease.substr(0, FinalInRelease.length() - strlen("InRelease")) + "Release.gpg";
228 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
229 {
230 char const * msgstr = nullptr;
231 switch (msg)
232 {
233 case InsecureType::UNSIGNED: msgstr = _("The repository '%s' is no longer signed."); break;
234 case InsecureType::NORELEASE: msgstr = _("The repository '%s' does no longer have a Release file."); break;
235 case InsecureType::WEAK: /* unreachable */ break;
236 }
d03b947b
DK
237 if (_config->FindB("Acquire::AllowDowngradeToInsecureRepositories") ||
238 TransactionManager->Target.OptionBool(IndexTarget::ALLOW_DOWNGRADE_TO_INSECURE))
b1bdfe68
DK
239 {
240 // meh, the users wants to take risks (we still mark the packages
241 // from this repository as unauthenticated)
242 _error->Warning(msgstr, repo.c_str());
243 _error->Warning(_("This is normally not allowed, but the option "
244 "Acquire::AllowDowngradeToInsecureRepositories was "
245 "given to override it."));
246 } else {
247 MessageInsecureRepository(true, msgstr, repo);
248 TransactionManager->AbortTransaction();
249 I->Status = pkgAcquire::Item::StatError;
250 return false;
251 }
252 }
253 }
254
f18f2338 255 if(MetaIndexParser->GetTrusted() == metaIndex::TRI_YES)
32532943
DK
256 return true;
257
b1bdfe68
DK
258 char const * msgstr = nullptr;
259 switch (msg)
260 {
261 case InsecureType::UNSIGNED: msgstr = _("The repository '%s' is not signed."); break;
262 case InsecureType::NORELEASE: msgstr = _("The repository '%s' does not have a Release file."); break;
263 case InsecureType::WEAK: msgstr = _("The repository '%s' provides only weak security information."); break;
264 }
265
d03b947b 266 if (TargetIsAllowedToBe(TransactionManager->Target, msg) == true)
f18f2338 267 {
b1bdfe68 268 MessageInsecureRepository(false, msgstr, repo);
f18f2338
DK
269 return true;
270 }
271
b1bdfe68 272 MessageInsecureRepository(true, msgstr, repo);
32532943
DK
273 TransactionManager->AbortTransaction();
274 I->Status = pkgAcquire::Item::StatError;
275 return false;
276}
277 /*}}}*/
5ad0096a 278static HashStringList GetExpectedHashesFromFor(metaIndex * const Parser, std::string const &MetaKey)/*{{{*/
8d041b4f
DK
279{
280 if (Parser == NULL)
281 return HashStringList();
5ad0096a 282 metaIndex::checkSum * const R = Parser->Lookup(MetaKey);
8d041b4f
DK
283 if (R == NULL)
284 return HashStringList();
285 return R->Hashes;
286}
287 /*}}}*/
32532943 288
448c38bd
DK
289// all ::HashesRequired and ::GetExpectedHashes implementations /*{{{*/
290/* ::GetExpectedHashes is abstract and has to be implemented by all subclasses.
291 It is best to implement it as broadly as possible, while ::HashesRequired defaults
292 to true and should be as restrictive as possible for false cases. Note that if
293 a hash is returned by ::GetExpectedHashes it must match. Only if it doesn't
294 ::HashesRequired is called to evaluate if its okay to have no hashes. */
295APT_CONST bool pkgAcqTransactionItem::HashesRequired() const
296{
297 /* signed repositories obviously have a parser and good hashes.
298 unsigned repositories, too, as even if we can't trust them for security,
299 we can at least trust them for integrity of the download itself.
300 Only repositories without a Release file can (obviously) not have
301 hashes – and they are very uncommon and strongly discouraged */
d03b947b
DK
302 if (TransactionManager->MetaIndexParser->GetLoadedSuccessfully() != metaIndex::TRI_YES)
303 return false;
304 if (TargetIsAllowedToBe(Target, InsecureType::WEAK))
305 {
306 /* If we allow weak hashes, we check that we have some (weak) and then
307 declare hashes not needed. That will tip us in the right direction
308 as if hashes exist, they will be used, even if not required */
309 auto const hsl = GetExpectedHashes();
310 if (hsl.usable())
311 return true;
312 if (hsl.empty() == false)
313 return false;
314 }
315 return true;
448c38bd
DK
316}
317HashStringList pkgAcqTransactionItem::GetExpectedHashes() const
318{
319 return GetExpectedHashesFor(GetMetaKey());
320}
321
322APT_CONST bool pkgAcqMetaBase::HashesRequired() const
323{
324 // Release and co have no hashes 'by design'.
325 return false;
326}
327HashStringList pkgAcqMetaBase::GetExpectedHashes() const
328{
329 return HashStringList();
330}
331
332APT_CONST bool pkgAcqIndexDiffs::HashesRequired() const
333{
4a808dea
DK
334 /* We can't check hashes of rred result as we don't know what the
335 hash of the file will be. We just know the hash of the patch(es),
336 the hash of the file they will apply on and the hash of the resulting
337 file. */
4f51fd86 338 if (State == StateFetchDiff)
4a808dea 339 return true;
448c38bd
DK
340 return false;
341}
342HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const
343{
4f51fd86
DK
344 if (State == StateFetchDiff)
345 return available_patches[0].download_hashes;
448c38bd
DK
346 return HashStringList();
347}
348
349APT_CONST bool pkgAcqIndexMergeDiffs::HashesRequired() const
350{
351 /* @see #pkgAcqIndexDiffs::HashesRequired, with the difference that
352 we can check the rred result after all patches are applied as
353 we know the expected result rather than potentially apply more patches */
4f51fd86 354 if (State == StateFetchDiff)
4a808dea 355 return true;
448c38bd
DK
356 return State == StateApplyDiff;
357}
358HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const
359{
4f51fd86
DK
360 if (State == StateFetchDiff)
361 return patch.download_hashes;
362 else if (State == StateApplyDiff)
dcbbb14d 363 return GetExpectedHashesFor(Target.MetaKey);
448c38bd
DK
364 return HashStringList();
365}
366
367APT_CONST bool pkgAcqArchive::HashesRequired() const
368{
369 return LocalSource == false;
370}
371HashStringList pkgAcqArchive::GetExpectedHashes() const
372{
373 // figured out while parsing the records
374 return ExpectedHashes;
375}
376
377APT_CONST bool pkgAcqFile::HashesRequired() const
378{
379 // supplied as parameter at creation time, so the caller decides
380 return ExpectedHashes.usable();
381}
382HashStringList pkgAcqFile::GetExpectedHashes() const
383{
384 return ExpectedHashes;
385}
386 /*}}}*/
387// Acquire::Item::QueueURI and specialisations from child classes /*{{{*/
388bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc &Item)
389{
390 Owner->Enqueue(Item);
391 return true;
392}
393/* The idea here is that an item isn't queued if it exists on disk and the
394 transition manager was a hit as this means that the files it contains
395 the checksums for can't be updated either (or they are and we are asking
396 for a hashsum mismatch to happen which helps nobody) */
397bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item)
398{
38f8704e
DK
399 if (TransactionManager->State != TransactionStarted)
400 {
401 if (_config->FindB("Debug::Acquire::Transaction", false))
402 std::clog << "Skip " << Target.URI << " as transaction was already dealt with!" << std::endl;
403 return false;
404 }
448c38bd 405 std::string const FinalFile = GetFinalFilename();
b7ec7a80 406 if (TransactionManager->IMSHit == true && FileExists(FinalFile) == true)
448c38bd
DK
407 {
408 PartialFile = DestFile = FinalFile;
409 Status = StatDone;
410 return false;
411 }
9b8034a9 412 // If we got the InRelease file via a mirror, pick all indexes directly from this mirror, too
b7ec7a80 413 if (TransactionManager->BaseURI.empty() == false &&
9b8034a9
DK
414 URI::SiteOnly(Item.URI) != URI::SiteOnly(TransactionManager->BaseURI))
415 {
416 // this ensures we rewrite only once and only the first step
417 auto const OldBaseURI = Target.Option(IndexTarget::BASE_URI);
03a34b88 418 if (OldBaseURI.empty() == false && APT::String::Startswith(Item.URI, OldBaseURI))
9b8034a9
DK
419 {
420 auto const ExtraPath = Item.URI.substr(OldBaseURI.length());
421 Item.URI = flCombine(TransactionManager->BaseURI, ExtraPath);
422 UsedMirror = TransactionManager->UsedMirror;
423 if (Item.Description.find(" ") != string::npos)
424 Item.Description.replace(0, Item.Description.find(" "), UsedMirror);
425 }
426 }
448c38bd
DK
427 return pkgAcquire::Item::QueueURI(Item);
428}
429/* The transition manager InRelease itself (or its older sisters-in-law
430 Release & Release.gpg) is always queued as this allows us to rerun gpgv
431 on it to verify that we aren't stalled with old files */
432bool pkgAcqMetaBase::QueueURI(pkgAcquire::ItemDesc &Item)
433{
434 return pkgAcquire::Item::QueueURI(Item);
435}
436/* the Diff/Index needs to queue also the up-to-date complete index file
437 to ensure that the list cleaner isn't eating it */
438bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc &Item)
439{
440 if (pkgAcqTransactionItem::QueueURI(Item) == true)
441 return true;
442 QueueOnIMSHit();
443 return false;
444}
445 /*}}}*/
446// Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
447std::string pkgAcquire::Item::GetFinalFilename() const
448{
b7a1076f 449 // Beware: Desc.URI is modified by redirections
448c38bd
DK
450 return GetFinalFileNameFromURI(Desc.URI);
451}
452std::string pkgAcqDiffIndex::GetFinalFilename() const
453{
b7a1076f 454 return GetFinalFileNameFromURI(GetDiffIndexURI(Target));
448c38bd
DK
455}
456std::string pkgAcqIndex::GetFinalFilename() const
457{
dcbbb14d 458 std::string const FinalFile = GetFinalFileNameFromURI(Target.URI);
0179cfa8 459 return GetKeepCompressedFileName(FinalFile, Target);
448c38bd
DK
460}
461std::string pkgAcqMetaSig::GetFinalFilename() const
462{
dcbbb14d 463 return GetFinalFileNameFromURI(Target.URI);
448c38bd
DK
464}
465std::string pkgAcqBaseIndex::GetFinalFilename() const
466{
dcbbb14d 467 return GetFinalFileNameFromURI(Target.URI);
448c38bd
DK
468}
469std::string pkgAcqMetaBase::GetFinalFilename() const
470{
dcbbb14d 471 return GetFinalFileNameFromURI(Target.URI);
448c38bd
DK
472}
473std::string pkgAcqArchive::GetFinalFilename() const
474{
475 return _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
476}
477 /*}}}*/
478// pkgAcqTransactionItem::GetMetaKey and specialisations for child classes /*{{{*/
479std::string pkgAcqTransactionItem::GetMetaKey() const
480{
dcbbb14d 481 return Target.MetaKey;
448c38bd
DK
482}
483std::string pkgAcqIndex::GetMetaKey() const
484{
485 if (Stage == STAGE_DECOMPRESS_AND_VERIFY || CurrentCompressionExtension == "uncompressed")
dcbbb14d
DK
486 return Target.MetaKey;
487 return Target.MetaKey + "." + CurrentCompressionExtension;
448c38bd
DK
488}
489std::string pkgAcqDiffIndex::GetMetaKey() const
490{
b7a1076f 491 return GetDiffIndexFileName(Target.MetaKey);
448c38bd
DK
492}
493 /*}}}*/
494//pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
495bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
496{
497 bool const Debug = _config->FindB("Debug::Acquire::Transaction", false);
498 switch(state)
499 {
57f16d51 500 case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
448c38bd
DK
501 case TransactionAbort:
502 if(Debug == true)
503 std::clog << " Cancel: " << DestFile << std::endl;
504 if (Status == pkgAcquire::Item::StatIdle)
505 {
506 Status = pkgAcquire::Item::StatDone;
507 Dequeue();
508 }
509 break;
510 case TransactionCommit:
18662401 511 if(PartialFile.empty() == false)
448c38bd 512 {
0179cfa8
DK
513 bool sameFile = (PartialFile == DestFile);
514 // we use symlinks on IMS-Hit to avoid copies
515 if (RealFileExists(DestFile))
516 {
517 struct stat Buf;
518 if (lstat(PartialFile.c_str(), &Buf) != -1)
519 {
520 if (S_ISLNK(Buf.st_mode) && Buf.st_size > 0)
521 {
522 char partial[Buf.st_size + 1];
523 ssize_t const sp = readlink(PartialFile.c_str(), partial, Buf.st_size);
524 if (sp == -1)
525 _error->Errno("pkgAcqTransactionItem::TransactionState-sp", _("Failed to readlink %s"), PartialFile.c_str());
526 else
527 {
528 partial[sp] = '\0';
529 sameFile = (DestFile == partial);
530 }
531 }
532 }
533 else
534 _error->Errno("pkgAcqTransactionItem::TransactionState-stat", _("Failed to stat %s"), PartialFile.c_str());
535 }
536 if (sameFile == false)
18662401
DK
537 {
538 // ensure that even without lists-cleanup all compressions are nuked
539 std::string FinalFile = GetFinalFileNameFromURI(Target.URI);
540 if (FileExists(FinalFile))
541 {
542 if(Debug == true)
543 std::clog << "rm " << FinalFile << " # " << DescURI() << std::endl;
544 if (RemoveFile("TransactionStates-Cleanup", FinalFile) == false)
545 return false;
546 }
547 for (auto const &ext: APT::Configuration::getCompressorExtensions())
548 {
549 auto const Final = FinalFile + ext;
550 if (FileExists(Final))
551 {
552 if(Debug == true)
553 std::clog << "rm " << Final << " # " << DescURI() << std::endl;
554 if (RemoveFile("TransactionStates-Cleanup", Final) == false)
555 return false;
556 }
557 }
558 if(Debug == true)
559 std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
560 if (Rename(PartialFile, DestFile) == false)
561 return false;
562 }
563 else if(Debug == true)
564 std::clog << "keep " << PartialFile << " # " << DescURI() << std::endl;
448c38bd 565
448c38bd
DK
566 } else {
567 if(Debug == true)
568 std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
e169fa4a 569 if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
18662401 570 return false;
448c38bd
DK
571 }
572 break;
573 }
574 return true;
575}
576bool pkgAcqMetaBase::TransactionState(TransactionStates const state)
577{
578 // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
579 if (TransactionManager->IMSHit == false)
580 return pkgAcqTransactionItem::TransactionState(state);
581 return true;
582}
583bool pkgAcqIndex::TransactionState(TransactionStates const state)
584{
585 if (pkgAcqTransactionItem::TransactionState(state) == false)
586 return false;
587
588 switch (state)
589 {
57f16d51 590 case TransactionStarted: _error->Fatal("AcqIndex %s changed to invalid transaction start state!", Target.URI.c_str()); break;
448c38bd
DK
591 case TransactionAbort:
592 if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
593 {
594 // keep the compressed file, but drop the decompressed
595 EraseFileName.clear();
9bd2313a 596 if (PartialFile.empty() == false && flExtension(PartialFile) != CurrentCompressionExtension)
51818f26 597 RemoveFile("TransactionAbort", PartialFile);
448c38bd
DK
598 }
599 break;
600 case TransactionCommit:
601 if (EraseFileName.empty() == false)
e169fa4a 602 RemoveFile("AcqIndex::TransactionCommit", EraseFileName);
448c38bd
DK
603 break;
604 }
605 return true;
606}
607bool pkgAcqDiffIndex::TransactionState(TransactionStates const state)
608{
609 if (pkgAcqTransactionItem::TransactionState(state) == false)
610 return false;
611
612 switch (state)
613 {
57f16d51 614 case TransactionStarted: _error->Fatal("Item %s changed to invalid transaction start state!", Target.URI.c_str()); break;
448c38bd
DK
615 case TransactionCommit:
616 break;
617 case TransactionAbort:
dcbbb14d 618 std::string const Partial = GetPartialFileNameFromURI(Target.URI);
51818f26 619 RemoveFile("TransactionAbort", Partial);
448c38bd
DK
620 break;
621 }
622
623 return true;
624}
625 /*}}}*/
b3501edb 626
8d041b4f
DK
627class APT_HIDDEN NoActionItem : public pkgAcquire::Item /*{{{*/
628/* The sole purpose of this class is having an item which does nothing to
629 reach its done state to prevent cleanup deleting the mentioned file.
630 Handy in cases in which we know we have the file already, like IMS-Hits. */
631{
dcbbb14d 632 IndexTarget const Target;
8d041b4f 633 public:
3b302846
DK
634 virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
635 virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
8d041b4f 636
e8afd168 637 NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target) :
8d041b4f
DK
638 pkgAcquire::Item(Owner), Target(Target)
639 {
640 Status = StatDone;
dcbbb14d 641 DestFile = GetFinalFileNameFromURI(Target.URI);
8d041b4f 642 }
d7a51997
DK
643 NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target, std::string const &FinalFile) :
644 pkgAcquire::Item(Owner), Target(Target)
645 {
646 Status = StatDone;
647 DestFile = FinalFile;
648 }
8d041b4f
DK
649};
650 /*}}}*/
b58047e0
DK
651class APT_HIDDEN CleanupItem : public pkgAcqTransactionItem /*{{{*/
652/* This class ensures that a file which was configured but isn't downloaded
653 for various reasons isn't kept in an old version in the lists directory.
654 In a way its the reverse of NoActionItem as it helps with removing files
655 even if the lists-cleanup is deactivated. */
656{
657 public:
658 virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
659 virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
660
661 CleanupItem(pkgAcquire * const Owner, pkgAcqMetaClearSig * const TransactionManager, IndexTarget const &Target) :
662 pkgAcqTransactionItem(Owner, TransactionManager, Target)
663 {
664 Status = StatDone;
665 DestFile = GetFinalFileNameFromURI(Target.URI);
666 }
667 bool TransactionState(TransactionStates const state) APT_OVERRIDE
668 {
669 switch (state)
670 {
671 case TransactionStarted:
672 break;
673 case TransactionAbort:
674 break;
675 case TransactionCommit:
676 if (_config->FindB("Debug::Acquire::Transaction", false) == true)
677 std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
678 if (RemoveFile("TransItem::TransactionCommit", DestFile) == false)
679 return false;
680 break;
681 }
682 return true;
683 }
684};
685 /*}}}*/
8d041b4f 686
0118833a 687// Acquire::Item::Item - Constructor /*{{{*/
57401c48
DK
688class pkgAcquire::Item::Private
689{
690public:
691 std::vector<std::string> PastRedirections;
692};
586d8704 693APT_IGNORE_DEPRECATED_PUSH
e8afd168 694pkgAcquire::Item::Item(pkgAcquire * const owner) :
1eb1836f 695 FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false), Local(false),
57401c48 696 QueueCounter(0), ExpectedAdditionalItems(0), Owner(owner), d(new Private())
0118833a
AL
697{
698 Owner->Add(this);
c88edf1d 699 Status = StatIdle;
0118833a 700}
586d8704 701APT_IGNORE_DEPRECATED_POP
0118833a
AL
702 /*}}}*/
703// Acquire::Item::~Item - Destructor /*{{{*/
0118833a
AL
704pkgAcquire::Item::~Item()
705{
706 Owner->Remove(this);
57401c48 707 delete d;
0118833a
AL
708}
709 /*}}}*/
448c38bd
DK
710std::string pkgAcquire::Item::Custom600Headers() const /*{{{*/
711{
712 return std::string();
713}
714 /*}}}*/
715std::string pkgAcquire::Item::ShortDesc() const /*{{{*/
716{
717 return DescURI();
718}
719 /*}}}*/
720APT_CONST void pkgAcquire::Item::Finished() /*{{{*/
721{
722}
723 /*}}}*/
724APT_PURE pkgAcquire * pkgAcquire::Item::GetOwner() const /*{{{*/
725{
726 return Owner;
727}
728 /*}}}*/
c8a4ce6c 729APT_CONST pkgAcquire::ItemDesc &pkgAcquire::Item::GetItemDesc() /*{{{*/
08ea7806
DK
730{
731 return Desc;
732}
733 /*}}}*/
448c38bd
DK
734APT_CONST bool pkgAcquire::Item::IsTrusted() const /*{{{*/
735{
736 return false;
737}
738 /*}}}*/
c88edf1d
AL
739// Acquire::Item::Failed - Item failed to download /*{{{*/
740// ---------------------------------------------------------------------
93bf083d
AL
741/* We return to an idle state if there are still other queues that could
742 fetch this object */
448c38bd 743void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
c88edf1d 744{
c88edf1d 745 if (QueueCounter <= 1)
93bf083d 746 {
a72ace20 747 /* This indicates that the file is not available right now but might
7d8afa39 748 be sometime later. If we do a retry cycle then this should be
17caf1b1 749 retried [CDROMs] */
4dbfe436 750 if (Cnf != NULL && Cnf->LocalOnly == true &&
7d8afa39 751 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
752 {
753 Status = StatIdle;
681d76d0 754 Dequeue();
a72ace20
AL
755 return;
756 }
7e5f33eb 757
58702f85
DK
758 switch (Status)
759 {
760 case StatIdle:
761 case StatFetching:
762 case StatDone:
763 Status = StatError;
764 break;
765 case StatAuthError:
766 case StatError:
767 case StatTransientNetworkError:
768 break;
769 }
4dbfe436 770 Complete = false;
681d76d0 771 Dequeue();
4dbfe436 772 }
23c5897c 773
30979dd7 774 string const FailReason = LookupTag(Message, "FailReason");
57401c48 775 enum { MAXIMUM_SIZE_EXCEEDED, HASHSUM_MISMATCH, WEAK_HASHSUMS, REDIRECTION_LOOP, OTHER } failreason = OTHER;
30979dd7
DK
776 if ( FailReason == "MaximumSizeExceeded")
777 failreason = MAXIMUM_SIZE_EXCEEDED;
562f0774
DK
778 else if ( FailReason == "WeakHashSums")
779 failreason = WEAK_HASHSUMS;
57401c48
DK
780 else if (FailReason == "RedirectionLoop")
781 failreason = REDIRECTION_LOOP;
30979dd7
DK
782 else if (Status == StatAuthError)
783 failreason = HASHSUM_MISMATCH;
784
0340069c
DK
785 if(ErrorText.empty())
786 {
57401c48
DK
787 std::ostringstream out;
788 switch (failreason)
789 {
790 case HASHSUM_MISMATCH:
791 out << _("Hash Sum mismatch") << std::endl;
792 break;
793 case WEAK_HASHSUMS:
794 out << _("Insufficient information available to perform this download securely") << std::endl;
795 break;
796 case REDIRECTION_LOOP:
797 out << "Redirection loop encountered" << std::endl;
798 break;
799 case MAXIMUM_SIZE_EXCEEDED:
800 out << LookupTag(Message, "Message") << std::endl;
801 break;
802 case OTHER:
803 out << LookupTag(Message, "Message");
804 break;
805 }
806
0340069c
DK
807 if (Status == StatAuthError)
808 {
30979dd7
DK
809 auto const ExpectedHashes = GetExpectedHashes();
810 if (ExpectedHashes.empty() == false)
0340069c 811 {
30979dd7
DK
812 out << "Hashes of expected file:" << std::endl;
813 for (auto const &hs: ExpectedHashes)
d3003692
DK
814 {
815 out << " - " << hs.toStr();
816 if (hs.usable() == false)
817 out << " [weak]";
818 out << std::endl;
819 }
30979dd7
DK
820 }
821 if (failreason == HASHSUM_MISMATCH)
822 {
823 out << "Hashes of received file:" << std::endl;
824 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
825 {
826 std::string const tagname = std::string(*type) + "-Hash";
827 std::string const hashsum = LookupTag(Message, tagname.c_str());
828 if (hashsum.empty() == false)
d3003692
DK
829 {
830 auto const hs = HashString(*type, hashsum);
831 out << " - " << hs.toStr();
832 if (hs.usable() == false)
833 out << " [weak]";
834 out << std::endl;
835 }
30979dd7
DK
836 }
837 out << "Last modification reported: " << LookupTag(Message, "Last-Modified", "<none>") << std::endl;
0340069c 838 }
0340069c 839 }
57401c48 840 ErrorText = out.str();
0340069c
DK
841 }
842
30979dd7
DK
843 switch (failreason)
844 {
845 case MAXIMUM_SIZE_EXCEEDED: RenameOnError(MaximumSizeExceeded); break;
846 case HASHSUM_MISMATCH: RenameOnError(HashSumMismatch); break;
562f0774 847 case WEAK_HASHSUMS: break;
57401c48 848 case REDIRECTION_LOOP: break;
30979dd7
DK
849 case OTHER: break;
850 }
0340069c
DK
851
852 if (FailReason.empty() == false)
3383ef4d 853 ReportMirrorFailureToCentral(*this, FailReason, ErrorText);
f0b509cd 854 else
3383ef4d 855 ReportMirrorFailureToCentral(*this, ErrorText, ErrorText);
146f7715 856
448c38bd
DK
857 if (QueueCounter > 1)
858 Status = StatIdle;
146f7715
DK
859}
860 /*}}}*/
8267fe24
AL
861// Acquire::Item::Start - Item has begun to download /*{{{*/
862// ---------------------------------------------------------------------
448c38bd 863/* Stash status and the file size. Note that setting Complete means
17caf1b1 864 sub-phases of the acquire process such as decompresion are operating */
448c38bd 865void pkgAcquire::Item::Start(string const &/*Message*/, unsigned long long const Size)
8267fe24
AL
866{
867 Status = StatFetching;
03aa0847 868 ErrorText.clear();
8267fe24
AL
869 if (FileSize == 0 && Complete == false)
870 FileSize = Size;
871}
872 /*}}}*/
dd676dc7
DK
873// Acquire::Item::VerifyDone - check if Item was downloaded OK /*{{{*/
874/* Note that hash-verification is 'hardcoded' in acquire-worker and has
875 * already passed if this method is called. */
876bool pkgAcquire::Item::VerifyDone(std::string const &Message,
877 pkgAcquire::MethodConfig const * const /*Cnf*/)
878{
879 std::string const FileName = LookupTag(Message,"Filename");
880 if (FileName.empty() == true)
881 {
882 Status = StatError;
883 ErrorText = "Method gave a blank filename";
884 return false;
885 }
886
887 return true;
888}
889 /*}}}*/
c88edf1d 890// Acquire::Item::Done - Item downloaded OK /*{{{*/
a4b8112b 891void pkgAcquire::Item::Done(string const &/*Message*/, HashStringList const &Hashes,
448c38bd 892 pkgAcquire::MethodConfig const * const /*Cnf*/)
c88edf1d 893{
b98f2859 894 // We just downloaded something..
ff86d7df 895 if (FileSize == 0)
b98f2859 896 {
ff86d7df
DK
897 unsigned long long const downloadedSize = Hashes.FileSize();
898 if (downloadedSize != 0)
448c38bd 899 {
ff86d7df 900 FileSize = downloadedSize;
448c38bd 901 }
448c38bd 902 }
c88edf1d
AL
903 Status = StatDone;
904 ErrorText = string();
905 Owner->Dequeue(this);
906}
907 /*}}}*/
8b89e57f
AL
908// Acquire::Item::Rename - Rename a file /*{{{*/
909// ---------------------------------------------------------------------
1e3f4083 910/* This helper function is used by a lot of item methods as their final
8b89e57f 911 step */
448c38bd 912bool pkgAcquire::Item::Rename(string const &From,string const &To)
8b89e57f 913{
ba6b79bd 914 if (From == To || rename(From.c_str(),To.c_str()) == 0)
cecc5532
DK
915 return true;
916
917 std::string S;
918 strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
919 From.c_str(),To.c_str());
920 Status = StatError;
8eafc759
DK
921 if (ErrorText.empty())
922 ErrorText = S;
923 else
924 ErrorText = ErrorText + ": " + S;
cecc5532 925 return false;
8b89e57f
AL
926}
927 /*}}}*/
448c38bd 928void pkgAcquire::Item::Dequeue() /*{{{*/
5684f71f 929{
448c38bd 930 Owner->Dequeue(this);
ba6b79bd 931}
448c38bd
DK
932 /*}}}*/
933bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
3c8030a4 934{
03aa0847 935 if (RealFileExists(DestFile))
3c8030a4
DK
936 Rename(DestFile, DestFile + ".FAILED");
937
448c38bd 938 std::string errtext;
3c8030a4
DK
939 switch (error)
940 {
941 case HashSumMismatch:
448c38bd 942 errtext = _("Hash Sum mismatch");
3c8030a4
DK
943 break;
944 case SizeMismatch:
448c38bd 945 errtext = _("Size mismatch");
3c8030a4 946 Status = StatAuthError;
3c8030a4
DK
947 break;
948 case InvalidFormat:
448c38bd 949 errtext = _("Invalid file format");
3c8030a4
DK
950 Status = StatError;
951 // do not report as usually its not the mirrors fault, but Portal/Proxy
952 break;
631a7dc7 953 case SignatureError:
448c38bd 954 errtext = _("Signature error");
631a7dc7
MV
955 Status = StatError;
956 break;
957 case NotClearsigned:
dd676dc7
DK
958 strprintf(errtext, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NOSPLIT");
959 Status = StatAuthError;
03aa0847
DK
960 break;
961 case MaximumSizeExceeded:
962 // the method is expected to report a good error for this
631a7dc7 963 break;
146f7715
DK
964 case PDiffError:
965 // no handling here, done by callers
966 break;
3c8030a4 967 }
448c38bd
DK
968 if (ErrorText.empty())
969 ErrorText = errtext;
3c8030a4
DK
970 return false;
971}
972 /*}}}*/
8267fbd9 973void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
eeac6897
MV
974{
975 ActiveSubprocess = subprocess;
586d8704 976 APT_IGNORE_DEPRECATED(Mode = ActiveSubprocess.c_str();)
eeac6897 977}
8267fbd9 978 /*}}}*/
c91d9a63 979// Acquire::Item::ReportMirrorFailure /*{{{*/
3383ef4d 980void pkgAcquire::Item::ReportMirrorFailure(std::string const &FailCode)
36280399 981{
3383ef4d 982 ReportMirrorFailureToCentral(*this, FailCode, FailCode);
36280399 983}
c91d9a63 984 /*}}}*/
448c38bd 985std::string pkgAcquire::Item::HashSum() const /*{{{*/
ac5b205a 986{
448c38bd
DK
987 HashStringList const hashes = GetExpectedHashes();
988 HashString const * const hs = hashes.find(NULL);
989 return hs != NULL ? hs->toStr() : "";
990}
991 /*}}}*/
57401c48
DK
992bool pkgAcquire::Item::IsRedirectionLoop(std::string const &NewURI) /*{{{*/
993{
994 if (d->PastRedirections.empty())
995 {
996 d->PastRedirections.push_back(NewURI);
997 return false;
998 }
999 auto const LastURI = std::prev(d->PastRedirections.end());
1000 // redirections to the same file are a way of restarting/resheduling,
1001 // individual methods will have to make sure that they aren't looping this way
1002 if (*LastURI == NewURI)
1003 return false;
1004 if (std::find(d->PastRedirections.begin(), LastURI, NewURI) != LastURI)
1005 return true;
1006 d->PastRedirections.push_back(NewURI);
1007 return false;
1008}
1009 /*}}}*/
2237bd01 1010
448c38bd 1011pkgAcqTransactionItem::pkgAcqTransactionItem(pkgAcquire * const Owner, /*{{{*/
3d8232bf 1012 pkgAcqMetaClearSig * const transactionManager, IndexTarget const &target) :
6c55f07a 1013 pkgAcquire::Item(Owner), d(NULL), Target(target), TransactionManager(transactionManager)
448c38bd
DK
1014{
1015 if (TransactionManager != this)
1016 TransactionManager->Add(this);
1017}
1018 /*}}}*/
1019pkgAcqTransactionItem::~pkgAcqTransactionItem() /*{{{*/
1020{
1021}
1022 /*}}}*/
e8afd168 1023HashStringList pkgAcqTransactionItem::GetExpectedHashesFor(std::string const &MetaKey) const /*{{{*/
448c38bd 1024{
8d041b4f 1025 return GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, MetaKey);
448c38bd
DK
1026}
1027 /*}}}*/
ac5b205a 1028
d3222349
DK
1029static void LoadLastMetaIndexParser(pkgAcqMetaClearSig * const TransactionManager, std::string const &FinalRelease, std::string const &FinalInRelease)/*{{{*/
1030{
1031 if (TransactionManager->IMSHit == true)
1032 return;
1033 if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
1034 {
1035 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
1036 if (TransactionManager->LastMetaIndexParser != NULL)
1037 {
1038 _error->PushToStack();
1039 if (RealFileExists(FinalInRelease))
1040 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
1041 else
1042 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
1043 // its unlikely to happen, but if what we have is bad ignore it
1044 if (_error->PendingError())
1045 {
1046 delete TransactionManager->LastMetaIndexParser;
1047 TransactionManager->LastMetaIndexParser = NULL;
1048 }
1049 _error->RevertToStack();
1050 }
1051 }
1052}
1053 /*}}}*/
1054
448c38bd
DK
1055// AcqMetaBase - Constructor /*{{{*/
1056pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire * const Owner,
3d8232bf 1057 pkgAcqMetaClearSig * const TransactionManager,
3d8232bf 1058 IndexTarget const &DataTarget)
6c55f07a 1059: pkgAcqTransactionItem(Owner, TransactionManager, DataTarget), d(NULL),
57f16d51 1060 AuthPass(false), IMSHit(false), State(TransactionStarted)
448c38bd
DK
1061{
1062}
1063 /*}}}*/
1064// AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
1065void pkgAcqMetaBase::Add(pkgAcqTransactionItem * const I)
1066{
1067 Transaction.push_back(I);
1068}
1069 /*}}}*/
1070// AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
1071void pkgAcqMetaBase::AbortTransaction()
1072{
1073 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1074 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
ac5b205a 1075
57f16d51
DK
1076 switch (TransactionManager->State)
1077 {
1078 case TransactionStarted: break;
1079 case TransactionAbort: _error->Fatal("Transaction %s was already aborted and is aborted again", TransactionManager->Target.URI.c_str()); return;
cbe5f098 1080 case TransactionCommit: _error->Fatal("Transaction %s was already aborted and is now committed", TransactionManager->Target.URI.c_str()); return;
57f16d51
DK
1081 }
1082 TransactionManager->State = TransactionAbort;
1083
448c38bd
DK
1084 // ensure the toplevel is in error state too
1085 for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
1086 I != Transaction.end(); ++I)
2ac3eeb6 1087 {
38f8704e
DK
1088 if ((*I)->Status != pkgAcquire::Item::StatFetching)
1089 Owner->Dequeue(*I);
448c38bd 1090 (*I)->TransactionState(TransactionAbort);
ac5b205a 1091 }
448c38bd
DK
1092 Transaction.clear();
1093}
1094 /*}}}*/
1095// AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
1096APT_PURE bool pkgAcqMetaBase::TransactionHasError() const
1097{
1098 for (std::vector<pkgAcqTransactionItem*>::const_iterator I = Transaction.begin();
1099 I != Transaction.end(); ++I)
1100 {
1101 switch((*I)->Status) {
1102 case StatDone: break;
1103 case StatIdle: break;
1104 case StatAuthError: return true;
1105 case StatError: return true;
1106 case StatTransientNetworkError: return true;
1107 case StatFetching: break;
1108 }
1109 }
1110 return false;
1111}
1112 /*}}}*/
1113// AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
1114void pkgAcqMetaBase::CommitTransaction()
1115{
1116 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1117 std::clog << "CommitTransaction: " << this << std::endl;
ac5b205a 1118
57f16d51
DK
1119 switch (TransactionManager->State)
1120 {
1121 case TransactionStarted: break;
cbe5f098
DK
1122 case TransactionAbort: _error->Fatal("Transaction %s was already committed and is now aborted", TransactionManager->Target.URI.c_str()); return;
1123 case TransactionCommit: _error->Fatal("Transaction %s was already committed and is again committed", TransactionManager->Target.URI.c_str()); return;
57f16d51
DK
1124 }
1125 TransactionManager->State = TransactionCommit;
1126
448c38bd
DK
1127 // move new files into place *and* remove files that are not
1128 // part of the transaction but are still on disk
1129 for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
1130 I != Transaction.end(); ++I)
1131 {
1132 (*I)->TransactionState(TransactionCommit);
1133 }
1134 Transaction.clear();
295d848b
DK
1135}
1136 /*}}}*/
448c38bd
DK
1137// AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
1138void pkgAcqMetaBase::TransactionStageCopy(pkgAcqTransactionItem * const I,
1139 const std::string &From,
1140 const std::string &To)
295d848b 1141{
448c38bd
DK
1142 I->PartialFile = From;
1143 I->DestFile = To;
ac5b205a 1144}
92fcbfc1 1145 /*}}}*/
448c38bd
DK
1146// AcqMetaBase::TransactionStageRemoval - Stage a file for removal /*{{{*/
1147void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I,
1148 const std::string &FinalFile)
1149{
1150 I->PartialFile = "";
1151 I->DestFile = FinalFile;
1152}
1153 /*}}}*/
1154// AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
3383ef4d
DK
1155/* This method is called from ::Failed handlers. If it returns true,
1156 no fallback to other files or modi is performed */
448c38bd
DK
1157bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message)
1158{
448c38bd 1159 string const Final = I->GetFinalFilename();
3383ef4d
DK
1160 std::string const GPGError = LookupTag(Message, "Message");
1161 if (FileExists(Final))
448c38bd
DK
1162 {
1163 I->Status = StatTransientNetworkError;
3383ef4d
DK
1164 _error->Warning(_("An error occurred during the signature verification. "
1165 "The repository is not updated and the previous index files will be used. "
1166 "GPG error: %s: %s"),
1167 Desc.Description.c_str(),
1168 GPGError.c_str());
448c38bd
DK
1169 RunScripts("APT::Update::Auth-Failure");
1170 return true;
1171 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1172 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1173 _error->Error(_("GPG error: %s: %s"),
3383ef4d
DK
1174 Desc.Description.c_str(),
1175 GPGError.c_str());
dd676dc7 1176 I->Status = StatAuthError;
448c38bd
DK
1177 return true;
1178 } else {
1179 _error->Warning(_("GPG error: %s: %s"),
3383ef4d
DK
1180 Desc.Description.c_str(),
1181 GPGError.c_str());
448c38bd
DK
1182 }
1183 // gpgv method failed
3383ef4d 1184 ReportMirrorFailureToCentral(*this, "GPGFailure", GPGError);
448c38bd
DK
1185 return false;
1186}
1187 /*}}}*/
1188// AcqMetaBase::Custom600Headers - Get header for AcqMetaBase /*{{{*/
6cb30d01 1189// ---------------------------------------------------------------------
448c38bd 1190string pkgAcqMetaBase::Custom600Headers() const
6cb30d01 1191{
448c38bd
DK
1192 std::string Header = "\nIndex-File: true";
1193 std::string MaximumSize;
1194 strprintf(MaximumSize, "\nMaximum-Size: %i",
1195 _config->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
1196 Header += MaximumSize;
4d0818cc 1197
448c38bd 1198 string const FinalFile = GetFinalFilename();
6cb30d01 1199 struct stat Buf;
448c38bd 1200 if (stat(FinalFile.c_str(),&Buf) == 0)
0b45b6e5 1201 Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
448c38bd
DK
1202
1203 return Header;
6cb30d01 1204}
92fcbfc1 1205 /*}}}*/
448c38bd
DK
1206// AcqMetaBase::QueueForSignatureVerify /*{{{*/
1207void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature)
ba6b79bd 1208{
448c38bd
DK
1209 AuthPass = true;
1210 I->Desc.URI = "gpgv:" + Signature;
1211 I->DestFile = File;
1212 QueueURI(I->Desc);
1213 I->SetActiveSubprocess("gpgv");
ba6b79bd
DK
1214}
1215 /*}}}*/
448c38bd
DK
1216// AcqMetaBase::CheckDownloadDone /*{{{*/
1217bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const
2237bd01 1218{
448c38bd
DK
1219 // We have just finished downloading a Release file (it is not
1220 // verified yet)
f6d4ab9a 1221
9b8034a9
DK
1222 // Save the final base URI we got this Release file from
1223 if (I->UsedMirror.empty() == false && _config->FindB("Acquire::SameMirrorForAllIndexes", true))
1224 {
1225 if (APT::String::Endswith(I->Desc.URI, "InRelease"))
1226 TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("InRelease"));
03a34b88 1227 else if (APT::String::Endswith(I->Desc.URI, "Release"))
9b8034a9
DK
1228 TransactionManager->BaseURI = I->Desc.URI.substr(0, I->Desc.URI.length() - strlen("Release"));
1229 }
1230
dd676dc7 1231 std::string const FileName = LookupTag(Message,"Filename");
08ea7806 1232 if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
f6d4ab9a 1233 {
448c38bd
DK
1234 I->Local = true;
1235 I->Desc.URI = "copy:" + FileName;
1236 I->QueueURI(I->Desc);
f6d4ab9a
DK
1237 return false;
1238 }
2237bd01 1239
448c38bd
DK
1240 // make sure to verify against the right file on I-M-S hit
1241 bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
1242 if (IMSHit == false && Hashes.usable())
f6d4ab9a 1243 {
448c38bd
DK
1244 // detect IMS-Hits servers haven't detected by Hash comparison
1245 std::string const FinalFile = I->GetFinalFilename();
1246 if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
2ac3eeb6 1247 {
448c38bd 1248 IMSHit = true;
51818f26 1249 RemoveFile("CheckDownloadDone", I->DestFile);
5e1ed088 1250 }
f6d4ab9a
DK
1251 }
1252
448c38bd 1253 if(IMSHit == true)
f6d4ab9a 1254 {
448c38bd
DK
1255 // for simplicity, the transaction manager is always InRelease
1256 // even if it doesn't exist.
b7ec7a80 1257 TransactionManager->IMSHit = true;
448c38bd 1258 I->PartialFile = I->DestFile = I->GetFinalFilename();
f6d4ab9a
DK
1259 }
1260
448c38bd
DK
1261 // set Item to complete as the remaining work is all local (verify etc)
1262 I->Complete = true;
f6d4ab9a 1263
448c38bd
DK
1264 return true;
1265}
1266 /*}}}*/
1267bool pkgAcqMetaBase::CheckAuthDone(string const &Message) /*{{{*/
1268{
1269 // At this point, the gpgv method has succeeded, so there is a
1270 // valid signature from a key in the trusted keyring. We
1271 // perform additional verification of its contents, and use them
1272 // to verify the indexes we are about to download
ab94dcec
DK
1273 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1274 std::cerr << "Signature verification succeeded: " << DestFile << std::endl;
f6d4ab9a 1275
448c38bd 1276 if (TransactionManager->IMSHit == false)
f6d4ab9a 1277 {
448c38bd
DK
1278 // open the last (In)Release if we have it
1279 std::string const FinalFile = GetFinalFilename();
1280 std::string FinalRelease;
1281 std::string FinalInRelease;
1282 if (APT::String::Endswith(FinalFile, "InRelease"))
2ac3eeb6 1283 {
448c38bd
DK
1284 FinalInRelease = FinalFile;
1285 FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
1286 }
1287 else
1288 {
1289 FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
1290 FinalRelease = FinalFile;
1291 }
d3222349 1292 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
f6d4ab9a
DK
1293 }
1294
ab94dcec 1295 bool const GoodAuth = TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText);
b1bdfe68 1296 if (GoodAuth == false && AllowInsecureRepositories(InsecureType::WEAK, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == false)
f6d4ab9a 1297 {
448c38bd 1298 Status = StatAuthError;
f6d4ab9a
DK
1299 return false;
1300 }
1301
448c38bd 1302 if (!VerifyVendor(Message))
f6d4ab9a 1303 {
448c38bd
DK
1304 Status = StatAuthError;
1305 return false;
1306 }
f6d4ab9a 1307
448c38bd 1308 // Download further indexes with verification
ab94dcec 1309 TransactionManager->QueueIndexes(GoodAuth);
2237bd01 1310
ab94dcec 1311 return GoodAuth;
448c38bd
DK
1312}
1313 /*}}}*/
1eba782f 1314void pkgAcqMetaClearSig::QueueIndexes(bool const verify) /*{{{*/
448c38bd
DK
1315{
1316 // at this point the real Items are loaded in the fetcher
1317 ExpectedAdditionalItems = 0;
1318
7f2d1eef 1319 std::set<std::string> targetsSeen;
ab94dcec
DK
1320 bool const hasReleaseFile = TransactionManager->MetaIndexParser != NULL;
1321 bool const metaBaseSupportsByHash = hasReleaseFile && TransactionManager->MetaIndexParser->GetSupportsAcquireByHash();
1322 bool hasHashes = true;
1323 auto IndexTargets = TransactionManager->MetaIndexParser->GetIndexTargets();
1324 if (hasReleaseFile && verify == false)
1325 hasHashes = std::any_of(IndexTargets.begin(), IndexTargets.end(),
1326 [&](IndexTarget const &Target) { return TransactionManager->MetaIndexParser->Exists(Target.MetaKey); });
1327 for (auto&& Target: IndexTargets)
448c38bd 1328 {
7f2d1eef
DK
1329 // if we have seen a target which is created-by a target this one here is declared a
1330 // fallback to, we skip acquiring the fallback (but we make sure we clean up)
1331 if (targetsSeen.find(Target.Option(IndexTarget::FALLBACK_OF)) != targetsSeen.end())
1332 {
1333 targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
1334 new CleanupItem(Owner, TransactionManager, Target);
1335 continue;
1336 }
1dd20368
DK
1337 // all is an implementation detail. Users shouldn't use this as arch
1338 // We need this support trickery here as e.g. Debian has binary-all files already,
1339 // but arch:all packages are still in the arch:any files, so we would waste precious
1340 // download time, bandwidth and diskspace for nothing, BUT Debian doesn't feature all
1341 // in the set of supported architectures, so we can filter based on this property rather
1342 // than invent an entirely new flag we would need to carry for all of eternity.
ab94dcec 1343 if (hasReleaseFile && Target.Option(IndexTarget::ARCHITECTURE) == "all")
a628ca52 1344 {
e8e5d464 1345 if (TransactionManager->MetaIndexParser->IsArchitectureAllSupportedFor(Target) == false)
b58047e0 1346 {
a8f565d3 1347 new CleanupItem(Owner, TransactionManager, Target);
a628ca52 1348 continue;
b58047e0 1349 }
a628ca52 1350 }
1dd20368 1351
a8f565d3 1352 bool trypdiff = Target.OptionBool(IndexTarget::PDIFFS);
ab94dcec 1353 if (hasReleaseFile == true)
2ac3eeb6 1354 {
a8f565d3 1355 if (TransactionManager->MetaIndexParser->Exists(Target.MetaKey) == false)
9b8c28f4
DK
1356 {
1357 // optional targets that we do not have in the Release file are skipped
ab94dcec 1358 if (hasHashes == true && Target.IsOptional)
b58047e0 1359 {
a8f565d3 1360 new CleanupItem(Owner, TransactionManager, Target);
9b8c28f4 1361 continue;
b58047e0 1362 }
47d2bc78 1363
a8f565d3 1364 std::string const &arch = Target.Option(IndexTarget::ARCHITECTURE);
1dd20368
DK
1365 if (arch.empty() == false)
1366 {
1367 if (TransactionManager->MetaIndexParser->IsArchitectureSupported(arch) == false)
1368 {
a8f565d3 1369 new CleanupItem(Owner, TransactionManager, Target);
1dd20368 1370 _error->Notice(_("Skipping acquire of configured file '%s' as repository '%s' doesn't support architecture '%s'"),
a8f565d3 1371 Target.MetaKey.c_str(), TransactionManager->Target.Description.c_str(), arch.c_str());
1dd20368
DK
1372 continue;
1373 }
1374 // if the architecture is officially supported but currently no packages for it available,
1375 // ignore silently as this is pretty much the same as just shipping an empty file.
1376 // if we don't know which architectures are supported, we do NOT ignore it to notify user about this
ab94dcec 1377 if (hasHashes == true && TransactionManager->MetaIndexParser->IsArchitectureSupported("*undefined*") == false)
b58047e0 1378 {
a8f565d3 1379 new CleanupItem(Owner, TransactionManager, Target);
1dd20368 1380 continue;
b58047e0 1381 }
1dd20368
DK
1382 }
1383
ab94dcec
DK
1384 if (hasHashes == true)
1385 {
1386 Status = StatAuthError;
1387 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target.MetaKey.c_str());
1388 return;
1389 }
1390 else
1391 {
1392 new pkgAcqIndex(Owner, TransactionManager, Target);
1393 continue;
1394 }
9b8c28f4 1395 }
ab94dcec 1396 else if (verify)
bd4a8f51 1397 {
a8f565d3 1398 auto const hashes = GetExpectedHashesFor(Target.MetaKey);
b2fd8524 1399 if (hashes.empty() == false)
bd4a8f51 1400 {
d03b947b 1401 if (hashes.usable() == false && TargetIsAllowedToBe(TransactionManager->Target, InsecureType::WEAK) == false)
b2fd8524 1402 {
a8f565d3 1403 new CleanupItem(Owner, TransactionManager, Target);
b2fd8524 1404 _error->Warning(_("Skipping acquire of configured file '%s' as repository '%s' provides only weak security information for it"),
a8f565d3 1405 Target.MetaKey.c_str(), TransactionManager->Target.Description.c_str());
b2fd8524
DK
1406 continue;
1407 }
1408 // empty files are skipped as acquiring the very small compressed files is a waste of time
1409 else if (hashes.FileSize() == 0)
b58047e0 1410 {
a8f565d3 1411 new CleanupItem(Owner, TransactionManager, Target);
7f2d1eef 1412 targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
b2fd8524 1413 continue;
b58047e0 1414 }
bd4a8f51
DK
1415 }
1416 }
9b8c28f4 1417
d7a51997 1418 // autoselect the compression method
a8f565d3 1419 std::vector<std::string> types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
d7a51997
DK
1420 types.erase(std::remove_if(types.begin(), types.end(), [&](std::string const &t) {
1421 if (t == "uncompressed")
a8f565d3
DK
1422 return TransactionManager->MetaIndexParser->Exists(Target.MetaKey) == false;
1423 std::string const MetaKey = Target.MetaKey + "." + t;
d7a51997
DK
1424 return TransactionManager->MetaIndexParser->Exists(MetaKey) == false;
1425 }), types.end());
1426 if (types.empty() == false)
8d041b4f 1427 {
d7a51997 1428 std::ostringstream os;
af81ab90 1429 // add the special compressiontype byhash first if supported
a8f565d3 1430 std::string const useByHashConf = Target.Option(IndexTarget::BY_HASH);
af81ab90
DK
1431 bool useByHash = false;
1432 if(useByHashConf == "force")
1433 useByHash = true;
1434 else
1435 useByHash = StringToBool(useByHashConf) == true && metaBaseSupportsByHash;
1436 if (useByHash == true)
1437 os << "by-hash ";
d7a51997
DK
1438 std::copy(types.begin(), types.end()-1, std::ostream_iterator<std::string>(os, " "));
1439 os << *types.rbegin();
a8f565d3 1440 Target.Options["COMPRESSIONTYPES"] = os.str();
d7a51997
DK
1441 }
1442 else
a8f565d3 1443 Target.Options["COMPRESSIONTYPES"].clear();
d7a51997 1444
a8f565d3 1445 std::string filename = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
d7a51997
DK
1446 if (filename.empty() == false)
1447 {
1448 // if the Release file is a hit and we have an index it must be the current one
1449 if (TransactionManager->IMSHit == true)
1450 ;
1451 else if (TransactionManager->LastMetaIndexParser != NULL)
1196da2e 1452 {
d7a51997
DK
1453 // see if the file changed since the last Release file
1454 // we use the uncompressed files as we might compress differently compared to the server,
1455 // so the hashes might not match, even if they contain the same data.
a8f565d3
DK
1456 HashStringList const newFile = GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, Target.MetaKey);
1457 HashStringList const oldFile = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
d7a51997
DK
1458 if (newFile != oldFile)
1459 filename.clear();
1196da2e 1460 }
d7a51997
DK
1461 else
1462 filename.clear();
8d041b4f
DK
1463 }
1464 else
1465 trypdiff = false; // no file to patch
1466
d7a51997
DK
1467 if (filename.empty() == false)
1468 {
a8f565d3
DK
1469 new NoActionItem(Owner, Target, filename);
1470 std::string const idxfilename = GetFinalFileNameFromURI(GetDiffIndexURI(Target));
3d1e34b0 1471 if (FileExists(idxfilename))
a8f565d3 1472 new NoActionItem(Owner, Target, idxfilename);
7f2d1eef 1473 targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
d7a51997
DK
1474 continue;
1475 }
1476
9b8c28f4 1477 // check if we have patches available
a8f565d3 1478 trypdiff &= TransactionManager->MetaIndexParser->Exists(GetDiffIndexFileName(Target.MetaKey));
2237bd01 1479 }
d7a51997
DK
1480 else
1481 {
1482 // if we have no file to patch, no point in trying
a8f565d3 1483 trypdiff &= (GetExistingFilename(GetFinalFileNameFromURI(Target.URI)).empty() == false);
d7a51997 1484 }
448c38bd 1485
9b8c28f4
DK
1486 // no point in patching from local sources
1487 if (trypdiff)
1488 {
a8f565d3 1489 std::string const proto = Target.URI.substr(0, strlen("file:/"));
9b8c28f4
DK
1490 if (proto == "file:/" || proto == "copy:/" || proto == "cdrom:")
1491 trypdiff = false;
1492 }
1493
1494 // Queue the Index file (Packages, Sources, Translation-$foo, …)
7f2d1eef 1495 targetsSeen.emplace(Target.Option(IndexTarget::CREATED_BY));
9b8c28f4 1496 if (trypdiff)
a8f565d3 1497 new pkgAcqDiffIndex(Owner, TransactionManager, Target);
448c38bd 1498 else
a8f565d3 1499 new pkgAcqIndex(Owner, TransactionManager, Target);
2237bd01 1500 }
448c38bd
DK
1501}
1502 /*}}}*/
fb7b11eb 1503bool pkgAcqMetaBase::VerifyVendor(string const &) /*{{{*/
448c38bd 1504{
448c38bd
DK
1505 string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
1506
1507 if (Transformed == "../project/experimental")
f6d4ab9a 1508 {
448c38bd 1509 Transformed = "experimental";
f6d4ab9a
DK
1510 }
1511
fb7b11eb 1512 auto pos = Transformed.rfind('/');
448c38bd 1513 if (pos != string::npos)
f6d4ab9a 1514 {
448c38bd 1515 Transformed = Transformed.substr(0, pos);
f6d4ab9a
DK
1516 }
1517
448c38bd 1518 if (Transformed == ".")
f6d4ab9a 1519 {
448c38bd 1520 Transformed = "";
f6d4ab9a
DK
1521 }
1522
0741daeb
DK
1523 if (TransactionManager->MetaIndexParser->GetValidUntil() > 0)
1524 {
448c38bd
DK
1525 time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
1526 if (invalid_since > 0)
1527 {
1528 std::string errmsg;
1529 strprintf(errmsg,
1530 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
3d8232bf 1531 // the time since then the file is invalid - formatted in the same way as in
448c38bd
DK
1532 // the download progress display (e.g. 7d 3h 42min 1s)
1533 _("Release file for %s is expired (invalid since %s). "
1534 "Updates for this repository will not be applied."),
dcbbb14d 1535 Target.URI.c_str(), TimeToStr(invalid_since).c_str());
448c38bd
DK
1536 if (ErrorText.empty())
1537 ErrorText = errmsg;
1538 return _error->Error("%s", errmsg.c_str());
1539 }
1540 }
f6d4ab9a 1541
448c38bd
DK
1542 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1543 as a prevention of downgrading us to older (still valid) files */
1544 if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
1545 TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
f6d4ab9a 1546 {
448c38bd 1547 TransactionManager->IMSHit = true;
51818f26 1548 RemoveFile("VerifyVendor", DestFile);
448c38bd 1549 PartialFile = DestFile = GetFinalFilename();
5ad0096a
DK
1550 // load the 'old' file in the 'new' one instead of flipping pointers as
1551 // the new one isn't owned by us, while the old one is so cleanup would be confused.
1552 TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
1553 delete TransactionManager->LastMetaIndexParser;
448c38bd 1554 TransactionManager->LastMetaIndexParser = NULL;
f6d4ab9a
DK
1555 }
1556
448c38bd 1557 if (_config->FindB("Debug::pkgAcquire::Auth", false))
f6d4ab9a 1558 {
5ad0096a 1559 std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
448c38bd
DK
1560 std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
1561 std::cerr << "Transformed Dist: " << Transformed << std::endl;
f6d4ab9a 1562 }
448c38bd
DK
1563
1564 if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
f6d4ab9a 1565 {
448c38bd
DK
1566 // This might become fatal one day
1567// Status = StatAuthError;
1568// ErrorText = "Conflicting distribution; expected "
1569// + MetaIndexParser->GetExpectedDist() + " but got "
5ad0096a 1570// + MetaIndexParser->GetCodename();
448c38bd
DK
1571// return false;
1572 if (!Transformed.empty())
1573 {
1574 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1575 Desc.Description.c_str(),
1576 Transformed.c_str(),
5ad0096a 1577 TransactionManager->MetaIndexParser->GetCodename().c_str());
448c38bd 1578 }
f6d4ab9a
DK
1579 }
1580
f6d4ab9a 1581 return true;
2237bd01 1582}
92fcbfc1 1583 /*}}}*/
3d8232bf
DK
1584pkgAcqMetaBase::~pkgAcqMetaBase()
1585{
1586}
2237bd01 1587
448c38bd
DK
1588pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
1589 IndexTarget const &ClearsignedTarget,
1590 IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
5ad0096a 1591 metaIndex * const MetaIndexParser) :
a8f565d3 1592 pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget),
d03b947b 1593 d(NULL), DetachedDataTarget(DetachedDataTarget),
3d8232bf 1594 MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
448c38bd
DK
1595{
1596 // index targets + (worst case:) Release/Release.gpg
1eba782f 1597 ExpectedAdditionalItems = std::numeric_limits<decltype(ExpectedAdditionalItems)>::max();
448c38bd 1598 TransactionManager->Add(this);
2237bd01 1599}
92fcbfc1 1600 /*}}}*/
448c38bd 1601pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
146f7715 1602{
3d8232bf
DK
1603 if (LastMetaIndexParser != NULL)
1604 delete LastMetaIndexParser;
146f7715
DK
1605}
1606 /*}}}*/
448c38bd
DK
1607// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1608string pkgAcqMetaClearSig::Custom600Headers() const
2237bd01 1609{
448c38bd
DK
1610 string Header = pkgAcqMetaBase::Custom600Headers();
1611 Header += "\nFail-Ignore: true";
b0d40854
DK
1612 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1613 if (key.empty() == false)
1614 Header += "\nSigned-By: " + key;
1615
448c38bd
DK
1616 return Header;
1617}
1618 /*}}}*/
57f16d51
DK
1619void pkgAcqMetaClearSig::Finished() /*{{{*/
1620{
1621 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1622 std::clog << "Finished: " << DestFile <<std::endl;
b7ec7a80 1623 if(TransactionManager->State == TransactionStarted &&
57f16d51
DK
1624 TransactionManager->TransactionHasError() == false)
1625 TransactionManager->CommitTransaction();
1626}
1627 /*}}}*/
24e8f24e 1628bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message, /*{{{*/
dd676dc7
DK
1629 pkgAcquire::MethodConfig const * const Cnf)
1630{
1631 Item::VerifyDone(Message, Cnf);
1632
1633 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
1634 return RenameOnError(NotClearsigned);
1635
1636 return true;
1637}
24e8f24e 1638 /*}}}*/
448c38bd 1639// pkgAcqMetaClearSig::Done - We got a file /*{{{*/
448c38bd
DK
1640void pkgAcqMetaClearSig::Done(std::string const &Message,
1641 HashStringList const &Hashes,
1642 pkgAcquire::MethodConfig const * const Cnf)
1643{
1644 Item::Done(Message, Hashes, Cnf);
8d266656 1645
448c38bd
DK
1646 if(AuthPass == false)
1647 {
1648 if(CheckDownloadDone(this, Message, Hashes) == true)
1649 QueueForSignatureVerify(this, DestFile, DestFile);
1650 return;
1651 }
1652 else if(CheckAuthDone(Message) == true)
1653 {
1654 if (TransactionManager->IMSHit == false)
1655 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1656 else if (RealFileExists(GetFinalFilename()) == false)
1657 {
1658 // We got an InRelease file IMSHit, but we haven't one, which means
1659 // we had a valid Release/Release.gpg combo stepping in, which we have
1660 // to 'acquire' now to ensure list cleanup isn't removing them
dcbbb14d
DK
1661 new NoActionItem(Owner, DetachedDataTarget);
1662 new NoActionItem(Owner, DetachedSigTarget);
448c38bd
DK
1663 }
1664 }
ab94dcec
DK
1665 else if (Status != StatAuthError)
1666 {
1667 string const FinalFile = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1668 string const OldFile = GetFinalFilename();
1669 if (TransactionManager->IMSHit == false)
1670 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1671 else if (RealFileExists(OldFile) == false)
1672 new NoActionItem(Owner, DetachedDataTarget);
1673 else
1674 TransactionManager->TransactionStageCopy(this, OldFile, FinalFile);
1675 }
2237bd01 1676}
92fcbfc1 1677 /*}}}*/
448c38bd 1678void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
2237bd01 1679{
448c38bd 1680 Item::Failed(Message, Cnf);
2237bd01 1681
448c38bd 1682 if (AuthPass == false)
2ac3eeb6 1683 {
f18f2338 1684 if (Status == StatAuthError || Status == StatTransientNetworkError)
dd676dc7 1685 {
f18f2338
DK
1686 // if we expected a ClearTextSignature (InRelease) but got a network
1687 // error or got a file, but it wasn't valid, we end up here (see VerifyDone).
dd676dc7
DK
1688 // As these is usually called by web-portals we do not try Release/Release.gpg
1689 // as this is gonna fail anyway and instead abort our try (LP#346386)
1690 TransactionManager->AbortTransaction();
1691 return;
1692 }
1693
448c38bd
DK
1694 // Queue the 'old' InRelease file for removal if we try Release.gpg
1695 // as otherwise the file will stay around and gives a false-auth
1696 // impression (CVE-2012-0214)
1697 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1698 Status = StatDone;
1699
a8f565d3 1700 new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget);
2ac3eeb6
MV
1701 }
1702 else
1703 {
448c38bd
DK
1704 if(CheckStopAuthentication(this, Message))
1705 return;
1706
d03b947b 1707 if(AllowInsecureRepositories(InsecureType::UNSIGNED, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
146f7715 1708 {
448c38bd
DK
1709 Status = StatDone;
1710
1711 /* InRelease files become Release files, otherwise
1712 * they would be considered as trusted later on */
1713 string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1714 string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
1715 string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
1716 string const FinalInRelease = GetFinalFilename();
1717 Rename(DestFile, PartialRelease);
1718 TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
d3222349 1719 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
146f7715 1720
448c38bd
DK
1721 // we parse the indexes here because at this point the user wanted
1722 // a repository that may potentially harm him
5ad0096a 1723 if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
448c38bd
DK
1724 /* expired Release files are still a problem you need extra force for */;
1725 else
1eba782f 1726 TransactionManager->QueueIndexes(true);
448c38bd 1727 }
2237bd01
MV
1728 }
1729}
92fcbfc1 1730 /*}}}*/
03aa0847 1731
448c38bd 1732pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
3d8232bf 1733 pkgAcqMetaClearSig * const TransactionManager,
448c38bd 1734 IndexTarget const &DataTarget,
a8f565d3
DK
1735 IndexTarget const &DetachedSigTarget) :
1736 pkgAcqMetaBase(Owner, TransactionManager, DataTarget), d(NULL),
448c38bd 1737 DetachedSigTarget(DetachedSigTarget)
ac5b205a 1738{
448c38bd
DK
1739 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1740 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1741 << this->TransactionManager << std::endl;
2d4722e2 1742
448c38bd 1743 DestFile = GetPartialFileNameFromURI(DataTarget.URI);
fa3a96a1 1744
448c38bd
DK
1745 // Create the item
1746 Desc.Description = DataTarget.Description;
1747 Desc.Owner = this;
1748 Desc.ShortDesc = DataTarget.ShortDesc;
1749 Desc.URI = DataTarget.URI;
448c38bd 1750 QueueURI(Desc);
ac5b205a 1751}
92fcbfc1 1752 /*}}}*/
448c38bd
DK
1753void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
1754 HashStringList const &Hashes,
1755 pkgAcquire::MethodConfig const * const Cfg)
ac5b205a 1756{
448c38bd 1757 Item::Done(Message,Hashes,Cfg);
03bfbc96 1758
448c38bd 1759 if(CheckDownloadDone(this, Message, Hashes))
03bfbc96 1760 {
448c38bd
DK
1761 // we have a Release file, now download the Signature, all further
1762 // verify/queue for additional downloads will be done in the
1763 // pkgAcqMetaSig::Done() code
dcbbb14d 1764 new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
03bfbc96 1765 }
448c38bd
DK
1766}
1767 /*}}}*/
1768// pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1769void pkgAcqMetaIndex::Failed(string const &Message,
1770 pkgAcquire::MethodConfig const * const Cnf)
1771{
1772 pkgAcquire::Item::Failed(Message, Cnf);
1773 Status = StatDone;
94dc9d7d 1774
448c38bd
DK
1775 // No Release file was present so fall
1776 // back to queueing Packages files without verification
d04e44ac 1777 // only allow going further if the user explicitly wants it
b1bdfe68 1778 if(AllowInsecureRepositories(InsecureType::NORELEASE, Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
f6d4ab9a 1779 {
448c38bd
DK
1780 // ensure old Release files are removed
1781 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
03bfbc96 1782
448c38bd 1783 // queue without any kind of hashsum support
1eba782f 1784 TransactionManager->QueueIndexes(false);
59a704f0 1785 }
448c38bd
DK
1786}
1787 /*}}}*/
448c38bd
DK
1788std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
1789{
dcbbb14d 1790 return Target.URI;
448c38bd
DK
1791}
1792 /*}}}*/
c8a4ce6c 1793pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
94dc9d7d 1794
448c38bd
DK
1795// AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1796pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
3d8232bf 1797 pkgAcqMetaClearSig * const TransactionManager,
e8afd168 1798 IndexTarget const &Target,
448c38bd 1799 pkgAcqMetaIndex * const MetaIndex) :
6c55f07a 1800 pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
448c38bd 1801{
dcbbb14d 1802 DestFile = GetPartialFileNameFromURI(Target.URI);
6cb30d01 1803
448c38bd
DK
1804 // remove any partial downloaded sig-file in partial/.
1805 // it may confuse proxies and is too small to warrant a
1806 // partial download anyway
51818f26 1807 RemoveFile("pkgAcqMetaSig", DestFile);
ac5b205a 1808
448c38bd
DK
1809 // set the TransactionManager
1810 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1811 std::clog << "New pkgAcqMetaSig with TransactionManager "
1812 << TransactionManager << std::endl;
f6d4ab9a 1813
448c38bd 1814 // Create the item
dcbbb14d 1815 Desc.Description = Target.Description;
448c38bd 1816 Desc.Owner = this;
dcbbb14d
DK
1817 Desc.ShortDesc = Target.ShortDesc;
1818 Desc.URI = Target.URI;
ac5b205a 1819
448c38bd
DK
1820 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1821 // so we skip the download step and go instantly to verification
1822 if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
1823 {
1824 Complete = true;
1825 Status = StatDone;
1826 PartialFile = DestFile = GetFinalFilename();
1827 MetaIndexFileSignature = DestFile;
1828 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1829 }
1830 else
1831 QueueURI(Desc);
ac5b205a 1832}
92fcbfc1 1833 /*}}}*/
448c38bd 1834pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
ac5b205a 1835{
b0d40854
DK
1836}
1837 /*}}}*/
1838// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1839std::string pkgAcqMetaSig::Custom600Headers() const
1840{
1841 std::string Header = pkgAcqTransactionItem::Custom600Headers();
1842 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1843 if (key.empty() == false)
1844 Header += "\nSigned-By: " + key;
1845 return Header;
448c38bd
DK
1846}
1847 /*}}}*/
1848// AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1849void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
1850 pkgAcquire::MethodConfig const * const Cfg)
1851{
1852 if (MetaIndexFileSignature.empty() == false)
4a0a786f 1853 {
448c38bd
DK
1854 DestFile = MetaIndexFileSignature;
1855 MetaIndexFileSignature.clear();
1856 }
1857 Item::Done(Message, Hashes, Cfg);
f6d4ab9a 1858
448c38bd
DK
1859 if(MetaIndex->AuthPass == false)
1860 {
1861 if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
f6d4ab9a 1862 {
448c38bd
DK
1863 // destfile will be modified to point to MetaIndexFile for the
1864 // gpgv method, so we need to save it here
1865 MetaIndexFileSignature = DestFile;
1866 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1867 }
1868 return;
1869 }
1870 else if(MetaIndex->CheckAuthDone(Message) == true)
1871 {
1872 if (TransactionManager->IMSHit == false)
1873 {
1874 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1875 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
f6d4ab9a 1876 }
448c38bd 1877 }
ab94dcec
DK
1878 else if (MetaIndex->Status != StatAuthError)
1879 {
1880 std::string const FinalFile = MetaIndex->GetFinalFilename();
1881 if (TransactionManager->IMSHit == false)
1882 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, FinalFile);
1883 else
1884 TransactionManager->TransactionStageCopy(MetaIndex, FinalFile, FinalFile);
1885 }
448c38bd
DK
1886}
1887 /*}}}*/
1888void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1889{
1890 Item::Failed(Message,Cnf);
4a0a786f 1891
448c38bd
DK
1892 // check if we need to fail at this point
1893 if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
1894 return;
4a0a786f 1895
448c38bd
DK
1896 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1897 TransactionManager->TransactionStageRemoval(this, DestFile);
4a0a786f 1898
d04e44ac 1899 // only allow going further if the user explicitly wants it
b1bdfe68 1900 if (AllowInsecureRepositories(InsecureType::UNSIGNED, MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
4a0a786f 1901 {
b1bdfe68
DK
1902 string const FinalRelease = MetaIndex->GetFinalFilename();
1903 string const FinalInRelease = TransactionManager->GetFinalFilename();
d3222349 1904 LoadLastMetaIndexParser(TransactionManager, FinalRelease, FinalInRelease);
4a0a786f 1905
448c38bd
DK
1906 // we parse the indexes here because at this point the user wanted
1907 // a repository that may potentially harm him
f01f5d91
DK
1908 bool const GoodLoad = TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText);
1909 if (MetaIndex->VerifyVendor(Message) == false)
448c38bd
DK
1910 /* expired Release files are still a problem you need extra force for */;
1911 else
1eba782f 1912 TransactionManager->QueueIndexes(GoodLoad);
448c38bd 1913
b1bdfe68 1914 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, FinalRelease);
448c38bd 1915 }
b1bdfe68
DK
1916 else if (TransactionManager->IMSHit == false)
1917 Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
448c38bd
DK
1918
1919 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1920 if (Cnf->LocalOnly == true ||
1921 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1922 {
1923 // Ignore this
1924 Status = StatDone;
ac5b205a 1925 }
ac5b205a 1926}
92fcbfc1 1927 /*}}}*/
448c38bd
DK
1928
1929
1930// AcqBaseIndex - Constructor /*{{{*/
1931pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
3d8232bf 1932 pkgAcqMetaClearSig * const TransactionManager,
e8afd168 1933 IndexTarget const &Target)
6c55f07a 1934: pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
448c38bd 1935{
0340069c
DK
1936}
1937 /*}}}*/
1938void pkgAcqBaseIndex::Failed(std::string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1939{
1940 pkgAcquire::Item::Failed(Message, Cnf);
b7ec7a80 1941 if (Status != StatAuthError)
0340069c
DK
1942 return;
1943
1944 ErrorText.append("Release file created at: ");
1945 auto const timespec = TransactionManager->MetaIndexParser->GetDate();
1946 if (timespec == 0)
1947 ErrorText.append("<unknown>");
1948 else
0b45b6e5 1949 ErrorText.append(TimeRFC1123(timespec, true));
0340069c 1950 ErrorText.append("\n");
448c38bd
DK
1951}
1952 /*}}}*/
c8a4ce6c 1953pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
448c38bd
DK
1954
1955// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1956// ---------------------------------------------------------------------
1957/* Get the DiffIndex file first and see if there are patches available
1958 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1959 * patches. If anything goes wrong in that process, it will fall back to
1960 * the original packages file
1961 */
1962pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
3d8232bf 1963 pkgAcqMetaClearSig * const TransactionManager,
e8afd168 1964 IndexTarget const &Target)
3d8232bf 1965 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), diffs(NULL)
47d2bc78 1966{
1eba782f
DK
1967 // FIXME: Magic number as an upper bound on pdiffs we will reasonably acquire
1968 ExpectedAdditionalItems = 40;
1969
47d2bc78
DK
1970 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1971
47d2bc78 1972 Desc.Owner = this;
b7a1076f 1973 Desc.Description = GetDiffIndexFileName(Target.Description);
dcbbb14d 1974 Desc.ShortDesc = Target.ShortDesc;
b7a1076f 1975 Desc.URI = GetDiffIndexURI(Target);
47d2bc78 1976
448c38bd
DK
1977 DestFile = GetPartialFileNameFromURI(Desc.URI);
1978
1979 if(Debug)
1980 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
5684f71f 1981
47d2bc78
DK
1982 QueueURI(Desc);
1983}
1984 /*}}}*/
448c38bd
DK
1985// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1986// ---------------------------------------------------------------------
1987/* The only header we use is the last-modified header. */
1988string pkgAcqDiffIndex::Custom600Headers() const
47d2bc78 1989{
abd6af5a
DK
1990 if (TransactionManager->LastMetaIndexParser != NULL)
1991 return "\nIndex-File: true";
1992
448c38bd 1993 string const Final = GetFinalFilename();
47d2bc78 1994
448c38bd
DK
1995 if(Debug)
1996 std::clog << "Custom600Header-IMS: " << Final << std::endl;
47d2bc78 1997
448c38bd
DK
1998 struct stat Buf;
1999 if (stat(Final.c_str(),&Buf) != 0)
2000 return "\nIndex-File: true";
2001
0b45b6e5 2002 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
448c38bd
DK
2003}
2004 /*}}}*/
2005void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
2006{
2007 // list cleanup needs to know that this file as well as the already
2008 // present index is ours, so we create an empty diff to save it for us
2009 new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
47d2bc78
DK
2010}
2011 /*}}}*/
0e071dfe
DK
2012static bool RemoveFileForBootstrapLinking(bool const Debug, std::string const &For, std::string const &Boot)/*{{{*/
2013{
2014 if (FileExists(Boot) && RemoveFile("Bootstrap-linking", Boot) == false)
2015 {
2016 if (Debug)
2017 std::clog << "Bootstrap-linking for patching " << For
2018 << " by removing stale " << Boot << " failed!" << std::endl;
2019 return false;
2020 }
2021 return true;
2022}
2023 /*}}}*/
448c38bd 2024bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
47d2bc78 2025{
1eba782f 2026 ExpectedAdditionalItems = 0;
448c38bd
DK
2027 // failing here is fine: our caller will take care of trying to
2028 // get the complete file if patching fails
47d2bc78 2029 if(Debug)
448c38bd
DK
2030 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
2031 << std::endl;
f6d4ab9a 2032
448c38bd
DK
2033 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
2034 pkgTagFile TF(&Fd);
95278287 2035 if (Fd.IsOpen() == false || Fd.Failed())
448c38bd 2036 return false;
47d2bc78 2037
448c38bd
DK
2038 pkgTagSection Tags;
2039 if(unlikely(TF.Step(Tags) == false))
2040 return false;
47d2bc78 2041
448c38bd
DK
2042 HashStringList ServerHashes;
2043 unsigned long long ServerSize = 0;
47d2bc78 2044
1136a707 2045 auto const &posix = std::locale("C.UTF-8");
448c38bd
DK
2046 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
2047 {
2048 std::string tagname = *type;
2049 tagname.append("-Current");
2050 std::string const tmp = Tags.FindS(tagname.c_str());
2051 if (tmp.empty() == true)
2052 continue;
146f7715 2053
448c38bd
DK
2054 string hash;
2055 unsigned long long size;
2056 std::stringstream ss(tmp);
1136a707 2057 ss.imbue(posix);
448c38bd
DK
2058 ss >> hash >> size;
2059 if (unlikely(hash.empty() == true))
2060 continue;
2061 if (unlikely(ServerSize != 0 && ServerSize != size))
2062 continue;
2063 ServerHashes.push_back(HashString(*type, hash));
2064 ServerSize = size;
2065 }
47d2bc78 2066
448c38bd
DK
2067 if (ServerHashes.usable() == false)
2068 {
2069 if (Debug == true)
2070 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
2071 return false;
47d2bc78 2072 }
448c38bd 2073
dcbbb14d
DK
2074 std::string const CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
2075 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
448c38bd 2076 if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
47d2bc78 2077 {
448c38bd 2078 if (Debug == true)
47d2bc78 2079 {
448c38bd 2080 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
8d89cda7 2081 printHashSumComparison(CurrentPackagesFile, ServerHashes, TargetFileHashes);
47d2bc78 2082 }
448c38bd
DK
2083 return false;
2084 }
47d2bc78 2085
9b8c28f4
DK
2086 HashStringList LocalHashes;
2087 // try avoiding calculating the hash here as this is costly
2088 if (TransactionManager->LastMetaIndexParser != NULL)
dcbbb14d 2089 LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
9b8c28f4
DK
2090 if (LocalHashes.usable() == false)
2091 {
d7a51997 2092 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly, FileFd::Auto);
9b8c28f4
DK
2093 Hashes LocalHashesCalc(ServerHashes);
2094 LocalHashesCalc.AddFD(fd);
2095 LocalHashes = LocalHashesCalc.GetHashStringList();
2096 }
2097
2098 if (ServerHashes == LocalHashes)
448c38bd
DK
2099 {
2100 // we have the same sha1 as the server so we are done here
47d2bc78 2101 if(Debug)
448c38bd
DK
2102 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
2103 QueueOnIMSHit();
2104 return true;
2105 }
47d2bc78 2106
448c38bd
DK
2107 if(Debug)
2108 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
9b8c28f4 2109 << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
34d6ece7 2110
37141fe4
DK
2111 // historically, older hashes have more info than newer ones, so start
2112 // collecting with older ones first to avoid implementing complicated
2113 // information merging techniques… a failure is after all always
2114 // recoverable with a complete file and hashes aren't changed that often.
2115 std::vector<char const *> types;
2116 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
2117 types.push_back(*type);
2118
448c38bd
DK
2119 // parse all of (provided) history
2120 vector<DiffInfo> available_patches;
2121 bool firstAcceptedHashes = true;
37141fe4 2122 for (auto type = types.crbegin(); type != types.crend(); ++type)
651bddad 2123 {
448c38bd
DK
2124 if (LocalHashes.find(*type) == NULL)
2125 continue;
21638c3a 2126
448c38bd
DK
2127 std::string tagname = *type;
2128 tagname.append("-History");
2129 std::string const tmp = Tags.FindS(tagname.c_str());
2130 if (tmp.empty() == true)
2131 continue;
a64bf0eb 2132
448c38bd
DK
2133 string hash, filename;
2134 unsigned long long size;
2135 std::stringstream ss(tmp);
1136a707 2136 ss.imbue(posix);
56472095 2137
448c38bd 2138 while (ss >> hash >> size >> filename)
651bddad 2139 {
448c38bd
DK
2140 if (unlikely(hash.empty() == true || filename.empty() == true))
2141 continue;
2142
2143 // see if we have a record for this file already
2144 std::vector<DiffInfo>::iterator cur = available_patches.begin();
2145 for (; cur != available_patches.end(); ++cur)
2146 {
4f51fd86 2147 if (cur->file != filename)
448c38bd
DK
2148 continue;
2149 cur->result_hashes.push_back(HashString(*type, hash));
2150 break;
2151 }
2152 if (cur != available_patches.end())
2153 continue;
2154 if (firstAcceptedHashes == true)
2155 {
2156 DiffInfo next;
2157 next.file = filename;
2158 next.result_hashes.push_back(HashString(*type, hash));
4f51fd86 2159 next.result_hashes.FileSize(size);
448c38bd
DK
2160 available_patches.push_back(next);
2161 }
2162 else
2163 {
2164 if (Debug == true)
2165 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2166 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
2167 break;
2168 }
651bddad 2169 }
448c38bd 2170 firstAcceptedHashes = false;
5d885723 2171 }
448c38bd
DK
2172
2173 if (unlikely(available_patches.empty() == true))
5d885723 2174 {
448c38bd
DK
2175 if (Debug)
2176 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
2177 << "Couldn't find any patches for the patch series." << std::endl;
2178 return false;
5d885723 2179 }
8267fe24 2180
37141fe4 2181 for (auto type = types.crbegin(); type != types.crend(); ++type)
b11f9599 2182 {
448c38bd
DK
2183 if (LocalHashes.find(*type) == NULL)
2184 continue;
97b65b10 2185
448c38bd
DK
2186 std::string tagname = *type;
2187 tagname.append("-Patches");
2188 std::string const tmp = Tags.FindS(tagname.c_str());
2189 if (tmp.empty() == true)
2190 continue;
18593cf7 2191
448c38bd
DK
2192 string hash, filename;
2193 unsigned long long size;
2194 std::stringstream ss(tmp);
1136a707 2195 ss.imbue(posix);
03aa0847 2196
448c38bd 2197 while (ss >> hash >> size >> filename)
58702f85 2198 {
448c38bd
DK
2199 if (unlikely(hash.empty() == true || filename.empty() == true))
2200 continue;
146f7715 2201
448c38bd
DK
2202 // see if we have a record for this file already
2203 std::vector<DiffInfo>::iterator cur = available_patches.begin();
2204 for (; cur != available_patches.end(); ++cur)
146f7715 2205 {
448c38bd
DK
2206 if (cur->file != filename)
2207 continue;
4f51fd86
DK
2208 if (cur->patch_hashes.empty())
2209 cur->patch_hashes.FileSize(size);
448c38bd 2210 cur->patch_hashes.push_back(HashString(*type, hash));
448c38bd 2211 break;
146f7715 2212 }
448c38bd
DK
2213 if (cur != available_patches.end())
2214 continue;
2215 if (Debug == true)
2216 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2217 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
146f7715 2218 break;
146f7715
DK
2219 }
2220 }
2d0a7bb4 2221
37141fe4 2222 for (auto type = types.crbegin(); type != types.crend(); ++type)
4f51fd86
DK
2223 {
2224 std::string tagname = *type;
2225 tagname.append("-Download");
2226 std::string const tmp = Tags.FindS(tagname.c_str());
2227 if (tmp.empty() == true)
2228 continue;
2229
2230 string hash, filename;
2231 unsigned long long size;
2232 std::stringstream ss(tmp);
1136a707 2233 ss.imbue(posix);
4f51fd86
DK
2234
2235 // FIXME: all of pdiff supports only .gz compressed patches
2236 while (ss >> hash >> size >> filename)
2237 {
2238 if (unlikely(hash.empty() == true || filename.empty() == true))
2239 continue;
2240 if (unlikely(APT::String::Endswith(filename, ".gz") == false))
2241 continue;
2242 filename.erase(filename.length() - 3);
2243
2244 // see if we have a record for this file already
2245 std::vector<DiffInfo>::iterator cur = available_patches.begin();
2246 for (; cur != available_patches.end(); ++cur)
2247 {
2248 if (cur->file != filename)
2249 continue;
2250 if (cur->download_hashes.empty())
2251 cur->download_hashes.FileSize(size);
2252 cur->download_hashes.push_back(HashString(*type, hash));
2253 break;
2254 }
2255 if (cur != available_patches.end())
2256 continue;
2257 if (Debug == true)
2258 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
2259 << " wasn't in the list for the first parsed hash! (download)" << std::endl;
2260 break;
2261 }
2262 }
2263
2264
448c38bd
DK
2265 bool foundStart = false;
2266 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
2267 cur != available_patches.end(); ++cur)
2268 {
2269 if (LocalHashes != cur->result_hashes)
2270 continue;
2271
2272 available_patches.erase(available_patches.begin(), cur);
2273 foundStart = true;
2274 break;
e6e89390 2275 }
b3d44315 2276
448c38bd
DK
2277 if (foundStart == false || unlikely(available_patches.empty() == true))
2278 {
2279 if (Debug)
2280 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
2281 << "Couldn't find the start of the patch series." << std::endl;
2282 return false;
2283 }
f6237efd 2284
4a808dea
DK
2285 for (auto const &patch: available_patches)
2286 if (patch.result_hashes.usable() == false ||
2287 patch.patch_hashes.usable() == false ||
2288 patch.download_hashes.usable() == false)
2289 {
2290 if (Debug)
2291 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": provides no usable hashes for " << patch.file
2292 << " so fallback to complete download" << std::endl;
2293 return false;
2294 }
2295
448c38bd
DK
2296 // patching with too many files is rather slow compared to a fast download
2297 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
2298 if (fileLimit != 0 && fileLimit < available_patches.size())
2299 {
2300 if (Debug)
2301 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
2302 << ") so fallback to complete download" << std::endl;
2303 return false;
2304 }
1f4dd8fd 2305
448c38bd 2306 // calculate the size of all patches we have to get
4e6219da 2307 unsigned short const sizeLimitPercent = _config->FindI("Acquire::PDiffs::SizeLimit", 100);
b7ec7a80 2308 if (sizeLimitPercent > 0)
4e6219da 2309 {
4e6219da
DK
2310 unsigned long long downloadSize = std::accumulate(available_patches.begin(),
2311 available_patches.end(), 0llu, [](unsigned long long const T, DiffInfo const &I) {
2312 return T + I.download_hashes.FileSize();
2313 });
2314 if (downloadSize != 0)
2315 {
2316 unsigned long long downloadSizeIdx = 0;
2317 auto const types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
2318 for (auto const &t : types)
2319 {
2320 std::string MetaKey = Target.MetaKey;
2321 if (t != "uncompressed")
2322 MetaKey += '.' + t;
2323 HashStringList const hsl = GetExpectedHashesFor(MetaKey);
2324 if (unlikely(hsl.usable() == false))
2325 continue;
2326 downloadSizeIdx = hsl.FileSize();
2327 break;
2328 }
2329 unsigned long long const sizeLimit = downloadSizeIdx * sizeLimitPercent;
2330 if ((sizeLimit/100) < downloadSize)
2331 {
2332 if (Debug)
2333 std::clog << "Need " << downloadSize << " compressed bytes (Limit is " << (sizeLimit/100) << ", "
2334 << "original is " << downloadSizeIdx << ") so fallback to complete download" << std::endl;
2335 return false;
2336 }
2337 }
448c38bd 2338 }
2737f28a 2339
448c38bd
DK
2340 // we have something, queue the diffs
2341 string::size_type const last_space = Description.rfind(" ");
2342 if(last_space != string::npos)
2343 Description.erase(last_space, Description.size()-last_space);
2344
2345 /* decide if we should download patches one by one or in one go:
2346 The first is good if the server merges patches, but many don't so client
2347 based merging can be attempt in which case the second is better.
2348 "bad things" will happen if patches are merged on the server,
2349 but client side merging is attempt as well */
2350 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
2351 if (pdiff_merge == true)
6bf93605 2352 {
448c38bd
DK
2353 // reprepro adds this flag if it has merged patches on the server
2354 std::string const precedence = Tags.FindS("X-Patch-Precedence");
2355 pdiff_merge = (precedence != "merged");
6bf93605 2356 }
448c38bd 2357
4e3c5633
DK
2358 // clean the plate
2359 {
ef3c549e
DK
2360 std::string const Final = GetExistingFilename(CurrentPackagesFile);
2361 if (unlikely(Final.empty())) // because we wouldn't be called in such a case
2362 return false;
4e3c5633 2363 std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
0e071dfe
DK
2364 std::string const PatchedFile = GetKeepCompressedFileName(PartialFile + "-patched", Target);
2365 if (RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PartialFile) == false ||
2366 RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PatchedFile) == false)
ef3c549e 2367 return false;
ef3c549e 2368 for (auto const &ext : APT::Configuration::getCompressorExtensions())
4e3c5633 2369 {
0e071dfe
DK
2370 if (RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PartialFile + ext) == false ||
2371 RemoveFileForBootstrapLinking(Debug, CurrentPackagesFile, PatchedFile + ext) == false)
ef3c549e 2372 return false;
4e3c5633 2373 }
4e3c5633
DK
2374 std::string const Ext = Final.substr(CurrentPackagesFile.length());
2375 std::string const Partial = PartialFile + Ext;
2376 if (symlink(Final.c_str(), Partial.c_str()) != 0)
2377 {
ef3c549e
DK
2378 if (Debug)
2379 std::clog << "Bootstrap-linking for patching " << CurrentPackagesFile
2380 << " by linking " << Final << " to " << Partial << " failed!" << std::endl;
4e3c5633
DK
2381 return false;
2382 }
2383 }
2384
448c38bd
DK
2385 if (pdiff_merge == false)
2386 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
6bf93605 2387 else
448c38bd 2388 {
3d8232bf 2389 diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
448c38bd
DK
2390 for(size_t i = 0; i < available_patches.size(); ++i)
2391 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
2392 Target,
2393 available_patches[i],
2394 diffs);
2395 }
2396
2397 Complete = false;
2398 Status = StatDone;
2399 Dequeue();
2400 return true;
6bf93605
DK
2401}
2402 /*}}}*/
448c38bd 2403void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
6bf93605 2404{
0340069c 2405 pkgAcqBaseIndex::Failed(Message,Cnf);
448c38bd 2406 Status = StatDone;
1eba782f 2407 ExpectedAdditionalItems = 0;
448c38bd
DK
2408
2409 if(Debug)
2410 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
2411 << "Falling back to normal index file acquire" << std::endl;
2412
2413 new pkgAcqIndex(Owner, TransactionManager, Target);
0118833a 2414}
61aea84d 2415 /*}}}*/
448c38bd
DK
2416void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
2417 pkgAcquire::MethodConfig const * const Cnf)
c88edf1d 2418{
448c38bd
DK
2419 if(Debug)
2420 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
c88edf1d 2421
448c38bd
DK
2422 Item::Done(Message, Hashes, Cnf);
2423
2424 string const FinalFile = GetFinalFilename();
2425 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
2426 DestFile = FinalFile;
2427
2428 if(ParseDiffIndex(DestFile) == false)
c88edf1d 2429 {
448c38bd
DK
2430 Failed("Message: Couldn't parse pdiff index", Cnf);
2431 // queue for final move - this should happen even if we fail
2432 // while parsing (e.g. on sizelimit) and download the complete file.
2433 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2737f28a
MV
2434 return;
2435 }
448c38bd
DK
2436
2437 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2438
2439 Complete = true;
2440 Status = StatDone;
2441 Dequeue();
2442
2443 return;
c88edf1d
AL
2444}
2445 /*}}}*/
3d8232bf
DK
2446pkgAcqDiffIndex::~pkgAcqDiffIndex()
2447{
2448 if (diffs != NULL)
2449 delete diffs;
2450}
448c38bd
DK
2451
2452// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2453// ---------------------------------------------------------------------
2454/* The package diff is added to the queue. one object is constructed
2455 * for each diff and the index
2456 */
2457pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
3d8232bf 2458 pkgAcqMetaClearSig * const TransactionManager,
e8afd168 2459 IndexTarget const &Target,
448c38bd 2460 vector<DiffInfo> const &diffs)
6c55f07a 2461 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
448c38bd 2462 available_patches(diffs)
681d76d0 2463{
d7a51997 2464 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
e8b1db38 2465
448c38bd 2466 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
e8b1db38 2467
448c38bd 2468 Desc.Owner = this;
dcbbb14d
DK
2469 Description = Target.Description;
2470 Desc.ShortDesc = Target.ShortDesc;
631a7dc7 2471
448c38bd 2472 if(available_patches.empty() == true)
631a7dc7 2473 {
448c38bd 2474 // we are done (yeah!), check hashes against the final file
d7a51997 2475 DestFile = GetKeepCompressedFileName(GetFinalFileNameFromURI(Target.URI), Target);
448c38bd 2476 Finish(true);
631a7dc7 2477 }
9d653a6d 2478 else
631a7dc7 2479 {
448c38bd
DK
2480 State = StateFetchDiff;
2481 QueueNextDiff();
631a7dc7 2482 }
448c38bd
DK
2483}
2484 /*}}}*/
2485void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2486{
0340069c 2487 pkgAcqBaseIndex::Failed(Message,Cnf);
448c38bd 2488 Status = StatDone;
631a7dc7 2489
d7a51997 2490 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
448c38bd
DK
2491 if(Debug)
2492 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
d7a51997 2493 << "Falling back to normal index file acquire " << std::endl;
448c38bd 2494 RenameOnError(PDiffError);
36795154
DK
2495 std::string const patchname = GetDiffsPatchFileName(DestFile);
2496 if (RealFileExists(patchname))
4e3c5633
DK
2497 Rename(patchname, patchname + ".FAILED");
2498 std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2499 if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
2500 Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
448c38bd
DK
2501 new pkgAcqIndex(Owner, TransactionManager, Target);
2502 Finish();
2503}
2504 /*}}}*/
2505// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
2506void pkgAcqIndexDiffs::Finish(bool allDone)
2507{
2508 if(Debug)
2509 std::clog << "pkgAcqIndexDiffs::Finish(): "
2510 << allDone << " "
2511 << Desc.URI << std::endl;
2512
2513 // we restore the original name, this is required, otherwise
2514 // the file will be cleaned
2515 if(allDone)
4dbfe436 2516 {
4e3c5633 2517 std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
d7a51997 2518 TransactionManager->TransactionStageCopy(this, DestFile, Final);
448c38bd
DK
2519
2520 // this is for the "real" finish
2521 Complete = true;
e05672e8 2522 Status = StatDone;
448c38bd
DK
2523 Dequeue();
2524 if(Debug)
2525 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
2526 return;
e05672e8 2527 }
d7a51997
DK
2528 else
2529 DestFile.clear();
448c38bd
DK
2530
2531 if(Debug)
2532 std::clog << "Finishing: " << Desc.URI << std::endl;
2533 Complete = false;
2534 Status = StatDone;
2535 Dequeue();
2536 return;
681d76d0 2537}
92fcbfc1 2538 /*}}}*/
448c38bd 2539bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
b3d44315 2540{
448c38bd 2541 // calc sha1 of the just patched file
4e3c5633
DK
2542 std::string const PartialFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2543 if(unlikely(PartialFile.empty()))
715c65de 2544 {
4e3c5633 2545 Failed("Message: The file " + GetPartialFileNameFromURI(Target.URI) + " isn't available", NULL);
448c38bd 2546 return false;
715c65de 2547 }
e05672e8 2548
4e3c5633 2549 FileFd fd(PartialFile, FileFd::ReadOnly, FileFd::Extension);
448c38bd
DK
2550 Hashes LocalHashesCalc;
2551 LocalHashesCalc.AddFD(fd);
2552 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
b3d44315 2553
448c38bd 2554 if(Debug)
4e3c5633 2555 std::clog << "QueueNextDiff: " << PartialFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
b3d44315 2556
dcbbb14d 2557 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
448c38bd 2558 if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
b3d44315 2559 {
4e3c5633 2560 Failed("Local/Expected hashes are not usable for " + PartialFile, NULL);
448c38bd 2561 return false;
b3d44315 2562 }
b3d44315 2563
448c38bd
DK
2564 // final file reached before all patches are applied
2565 if(LocalHashes == TargetFileHashes)
6bf93605 2566 {
448c38bd
DK
2567 Finish(true);
2568 return true;
6bf93605
DK
2569 }
2570
448c38bd
DK
2571 // remove all patches until the next matching patch is found
2572 // this requires the Index file to be ordered
258b9e51
DK
2573 available_patches.erase(available_patches.begin(),
2574 std::find_if(available_patches.begin(), available_patches.end(), [&](DiffInfo const &I) {
2575 return I.result_hashes == LocalHashes;
2576 }));
56bc3358 2577
448c38bd
DK
2578 // error checking and falling back if no patch was found
2579 if(available_patches.empty() == true)
56bc3358 2580 {
4e3c5633 2581 Failed("No patches left to reach target for " + PartialFile, NULL);
f3097647 2582 return false;
56bc3358 2583 }
f3097647 2584
448c38bd 2585 // queue the right diff
dcbbb14d 2586 Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
448c38bd 2587 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
d7a51997 2588 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file), Target);
f3097647 2589
448c38bd
DK
2590 if(Debug)
2591 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
2592
2593 QueueURI(Desc);
f3097647
MV
2594
2595 return true;
2596}
2597 /*}}}*/
448c38bd
DK
2598void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2599 pkgAcquire::MethodConfig const * const Cnf)
27e6c17a 2600{
4e3c5633 2601 if (Debug)
448c38bd 2602 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
27e6c17a 2603
448c38bd 2604 Item::Done(Message, Hashes, Cnf);
27e6c17a 2605
4e3c5633
DK
2606 std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
2607 std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
2608 std::string const PatchFile = GetDiffsPatchFileName(UnpatchedFile);
2609 std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
2610
2611 switch (State)
2612 {
2613 // success in downloading a diff, enter ApplyDiff state
2614 case StateFetchDiff:
2615 Rename(DestFile, PatchFile);
2616 DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
2617 if(Debug)
2618 std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
2619 State = StateApplyDiff;
2620 Local = true;
2621 Desc.URI = "rred:" + UnpatchedFile;
2622 QueueURI(Desc);
2623 SetActiveSubprocess("rred");
2624 return;
2625 // success in download/apply a diff, queue next (if needed)
2626 case StateApplyDiff:
2627 // remove the just applied patch and base file
2628 available_patches.erase(available_patches.begin());
2629 RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
2630 RemoveFile("pkgAcqIndexDiffs::Done", UnpatchedFile);
2631 if(Debug)
2632 std::clog << "Moving patched file in place: " << std::endl
2633 << DestFile << " -> " << PatchedFile << std::endl;
2634 Rename(DestFile, PatchedFile);
2635
2636 // see if there is more to download
2637 if(available_patches.empty() == false)
2638 {
2639 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
2640 Finish();
2641 } else {
2642 DestFile = PatchedFile;
2643 Finish(true);
2644 }
2645 return;
ba6b79bd 2646 }
448c38bd
DK
2647}
2648 /*}}}*/
36795154
DK
2649std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2650{
2651 if(State != StateApplyDiff)
2652 return pkgAcqBaseIndex::Custom600Headers();
2653 std::ostringstream patchhashes;
6e71ec6f
DK
2654 for (auto && hs : available_patches[0].result_hashes)
2655 patchhashes << "\nStart-" << hs.HashType() << "-Hash: " << hs.HashValue();
2656 for (auto && hs : available_patches[0].patch_hashes)
2657 patchhashes << "\nPatch-0-" << hs.HashType() << "-Hash: " << hs.HashValue();
36795154
DK
2658 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2659 return patchhashes.str();
2660}
2661 /*}}}*/
c8a4ce6c 2662pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
448c38bd
DK
2663
2664// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2665pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
3d8232bf 2666 pkgAcqMetaClearSig * const TransactionManager,
e8afd168 2667 IndexTarget const &Target,
448c38bd
DK
2668 DiffInfo const &patch,
2669 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
6c55f07a 2670 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
448c38bd
DK
2671 patch(patch), allPatches(allPatches), State(StateFetchDiff)
2672{
2673 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2674
2675 Desc.Owner = this;
dcbbb14d
DK
2676 Description = Target.Description;
2677 Desc.ShortDesc = Target.ShortDesc;
dcbbb14d 2678 Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
4e3c5633
DK
2679 Desc.Description = Description + " " + patch.file + ".pdiff";
2680 DestFile = GetPartialFileNameFromURI(Desc.URI);
448c38bd
DK
2681
2682 if(Debug)
2683 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
2684
2685 QueueURI(Desc);
2686}
2687 /*}}}*/
2688void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2689{
2690 if(Debug)
2691 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
2737f28a 2692
0340069c 2693 pkgAcqBaseIndex::Failed(Message,Cnf);
448c38bd 2694 Status = StatDone;
b3d44315 2695
448c38bd
DK
2696 // check if we are the first to fail, otherwise we are done here
2697 State = StateDoneDiff;
2698 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2699 I != allPatches->end(); ++I)
2700 if ((*I)->State == StateErrorDiff)
b7a1076f
DK
2701 {
2702 State = StateErrorDiff;
448c38bd 2703 return;
b7a1076f 2704 }
448c38bd
DK
2705
2706 // first failure means we should fallback
2707 State = StateErrorDiff;
2708 if (Debug)
2709 std::clog << "Falling back to normal index file acquire" << std::endl;
448c38bd 2710 RenameOnError(PDiffError);
b7a1076f
DK
2711 if (RealFileExists(DestFile))
2712 Rename(DestFile, DestFile + ".FAILED");
4e3c5633
DK
2713 std::string const UnpatchedFile = GetExistingFilename(GetPartialFileNameFromURI(Target.URI));
2714 if (UnpatchedFile.empty() == false && FileExists(UnpatchedFile))
2715 Rename(UnpatchedFile, UnpatchedFile + ".FAILED");
d7a51997 2716 DestFile.clear();
4e3c5633 2717 new pkgAcqIndex(Owner, TransactionManager, Target);
b3d44315 2718}
92fcbfc1 2719 /*}}}*/
448c38bd
DK
2720void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2721 pkgAcquire::MethodConfig const * const Cnf)
b3d44315 2722{
448c38bd
DK
2723 if(Debug)
2724 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
18593cf7 2725
448c38bd 2726 Item::Done(Message, Hashes, Cnf);
18593cf7 2727
dfcf7f35
DK
2728 if (std::any_of(allPatches->begin(), allPatches->end(),
2729 [](pkgAcqIndexMergeDiffs const * const P) { return P->State == StateErrorDiff; }))
2730 {
2731 if(Debug)
2732 std::clog << "Another patch failed already, no point in processing this one." << std::endl;
b7a1076f 2733 State = StateErrorDiff;
dfcf7f35
DK
2734 return;
2735 }
2736
4e3c5633
DK
2737 std::string const UncompressedUnpatchedFile = GetPartialFileNameFromURI(Target.URI);
2738 std::string const UnpatchedFile = GetExistingFilename(UncompressedUnpatchedFile);
dfcf7f35
DK
2739 if (UnpatchedFile.empty())
2740 {
b7a1076f
DK
2741 _error->Fatal("Unpatched file %s doesn't exist (anymore)!", UncompressedUnpatchedFile.c_str());
2742 State = StateErrorDiff;
dfcf7f35
DK
2743 return;
2744 }
4e3c5633
DK
2745 std::string const PatchFile = GetMergeDiffsPatchFileName(UnpatchedFile, patch.file);
2746 std::string const PatchedFile = GetKeepCompressedFileName(UncompressedUnpatchedFile, Target);
ab53c018 2747
4e3c5633 2748 switch (State)
448c38bd 2749 {
4e3c5633
DK
2750 case StateFetchDiff:
2751 Rename(DestFile, PatchFile);
448c38bd 2752
4e3c5633
DK
2753 // check if this is the last completed diff
2754 State = StateDoneDiff;
2755 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2756 I != allPatches->end(); ++I)
2757 if ((*I)->State != StateDoneDiff)
2758 {
2759 if(Debug)
2760 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
2761 return;
2762 }
2763 // this is the last completed diff, so we are ready to apply now
2764 DestFile = GetKeepCompressedFileName(UncompressedUnpatchedFile + "-patched", Target);
2765 if(Debug)
2766 std::clog << "Sending to rred method: " << UnpatchedFile << std::endl;
2767 State = StateApplyDiff;
2768 Local = true;
2769 Desc.URI = "rred:" + UnpatchedFile;
2770 QueueURI(Desc);
2771 SetActiveSubprocess("rred");
2772 return;
2773 case StateApplyDiff:
2774 // success in download & apply all diffs, finialize and clean up
2775 if(Debug)
2776 std::clog << "Queue patched file in place: " << std::endl
2777 << DestFile << " -> " << PatchedFile << std::endl;
2778
2779 // queue for copy by the transaction manager
2780 TransactionManager->TransactionStageCopy(this, DestFile, GetKeepCompressedFileName(GetFinalFilename(), Target));
2781
2782 // ensure the ed's are gone regardless of list-cleanup
2783 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2784 I != allPatches->end(); ++I)
2785 RemoveFile("pkgAcqIndexMergeDiffs::Done", GetMergeDiffsPatchFileName(UnpatchedFile, (*I)->patch.file));
2786 RemoveFile("pkgAcqIndexMergeDiffs::Done", UnpatchedFile);
2787
2788 // all set and done
2789 Complete = true;
2790 if(Debug)
2791 std::clog << "allDone: " << DestFile << "\n" << std::endl;
2792 return;
2793 case StateDoneDiff: _error->Fatal("Done called for %s which is in an invalid Done state", PatchFile.c_str()); break;
2794 case StateErrorDiff: _error->Fatal("Done called for %s which is in an invalid Error state", PatchFile.c_str()); break;
b3d44315
MV
2795 }
2796}
92fcbfc1 2797 /*}}}*/
36795154
DK
2798std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2799{
2800 if(State != StateApplyDiff)
2801 return pkgAcqBaseIndex::Custom600Headers();
2802 std::ostringstream patchhashes;
2803 unsigned int seen_patches = 0;
6e71ec6f
DK
2804 for (auto && hs : (*allPatches)[0]->patch.result_hashes)
2805 patchhashes << "\nStart-" << hs.HashType() << "-Hash: " << hs.HashValue();
36795154
DK
2806 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2807 I != allPatches->end(); ++I)
2808 {
2809 HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
2810 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
7303e11f 2811 patchhashes << "\nPatch-" << std::to_string(seen_patches) << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
36795154
DK
2812 ++seen_patches;
2813 }
2814 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2815 return patchhashes.str();
2816}
2817 /*}}}*/
c8a4ce6c 2818pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
448c38bd
DK
2819
2820// AcqIndex::AcqIndex - Constructor /*{{{*/
2821pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
3d8232bf 2822 pkgAcqMetaClearSig * const TransactionManager,
e8afd168 2823 IndexTarget const &Target)
d7a51997
DK
2824 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD),
2825 CompressionExtensions(Target.Option(IndexTarget::COMPRESSIONTYPES))
b3d44315 2826{
dcbbb14d 2827 Init(Target.URI, Target.Description, Target.ShortDesc);
ce424cd4 2828
448c38bd
DK
2829 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2830 std::clog << "New pkgIndex with TransactionManager "
2831 << TransactionManager << std::endl;
2832}
2833 /*}}}*/
448c38bd 2834// AcqIndex::Init - defered Constructor /*{{{*/
af81ab90 2835static void NextCompressionExtension(std::string &CurrentCompressionExtension, std::string &CompressionExtensions, bool const preview)
448c38bd 2836{
448c38bd
DK
2837 size_t const nextExt = CompressionExtensions.find(' ');
2838 if (nextExt == std::string::npos)
b3d44315 2839 {
448c38bd 2840 CurrentCompressionExtension = CompressionExtensions;
af81ab90
DK
2841 if (preview == false)
2842 CompressionExtensions.clear();
b3d44315 2843 }
448c38bd
DK
2844 else
2845 {
2846 CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
af81ab90
DK
2847 if (preview == false)
2848 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
1ddb8596 2849 }
af81ab90
DK
2850}
2851void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
2852 string const &ShortDesc)
2853{
2854 Stage = STAGE_DOWNLOAD;
2855
2856 DestFile = GetPartialFileNameFromURI(URI);
2857 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, false);
1ddb8596 2858
fb193b1c
MV
2859 // store file size of the download to ensure the fetcher gives
2860 // accurate progress reporting
2861 FileSize = GetExpectedHashes().FileSize();
2862
448c38bd 2863 if (CurrentCompressionExtension == "uncompressed")
6bf93605 2864 {
448c38bd 2865 Desc.URI = URI;
6bf93605 2866 }
af81ab90
DK
2867 else if (CurrentCompressionExtension == "by-hash")
2868 {
2869 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, true);
b7ec7a80 2870 if(unlikely(CurrentCompressionExtension.empty()))
af81ab90
DK
2871 return;
2872 if (CurrentCompressionExtension != "uncompressed")
2873 {
2874 Desc.URI = URI + '.' + CurrentCompressionExtension;
2875 DestFile = DestFile + '.' + CurrentCompressionExtension;
2876 }
2877
2878 HashStringList const Hashes = GetExpectedHashes();
2879 HashString const * const TargetHash = Hashes.find(NULL);
2880 if (unlikely(TargetHash == nullptr))
2881 return;
2882 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2883 size_t const trailing_slash = Desc.URI.find_last_of("/");
2884 if (unlikely(trailing_slash == std::string::npos))
2885 return;
2886 Desc.URI = Desc.URI.replace(
2887 trailing_slash,
2888 Desc.URI.substr(trailing_slash+1).size()+1,
2889 ByHash);
2890 }
448c38bd
DK
2891 else if (unlikely(CurrentCompressionExtension.empty()))
2892 return;
2893 else
b3d44315 2894 {
448c38bd
DK
2895 Desc.URI = URI + '.' + CurrentCompressionExtension;
2896 DestFile = DestFile + '.' + CurrentCompressionExtension;
b3d44315
MV
2897 }
2898
448c38bd
DK
2899
2900 Desc.Description = URIDesc;
2901 Desc.Owner = this;
2902 Desc.ShortDesc = ShortDesc;
2903
2904 QueueURI(Desc);
2905}
2906 /*}}}*/
448c38bd
DK
2907// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2908// ---------------------------------------------------------------------
2909/* The only header we use is the last-modified header. */
2910string pkgAcqIndex::Custom600Headers() const
b3d44315 2911{
c5fced38 2912
448c38bd 2913 string msg = "\nIndex-File: true";
abd6af5a
DK
2914
2915 if (TransactionManager->LastMetaIndexParser == NULL)
2916 {
2917 std::string const Final = GetFinalFilename();
2918
2919 struct stat Buf;
2920 if (stat(Final.c_str(),&Buf) == 0)
0b45b6e5 2921 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime, false);
abd6af5a 2922 }
1d970e6c 2923
dcbbb14d 2924 if(Target.IsOptional)
448c38bd
DK
2925 msg += "\nFail-Ignore: true";
2926
2927 return msg;
b3d44315 2928}
681d76d0 2929 /*}}}*/
448c38bd
DK
2930// AcqIndex::Failed - getting the indexfile failed /*{{{*/
2931void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
56472095 2932{
0340069c 2933 pkgAcqBaseIndex::Failed(Message,Cnf);
448c38bd
DK
2934
2935 // authorisation matches will not be fixed by other compression types
2936 if (Status != StatAuthError)
2937 {
2938 if (CompressionExtensions.empty() == false)
2939 {
dcbbb14d 2940 Init(Target.URI, Desc.Description, Desc.ShortDesc);
448c38bd
DK
2941 Status = StatIdle;
2942 return;
2943 }
2944 }
2945
dcbbb14d 2946 if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
448c38bd
DK
2947 Status = StatDone;
2948 else
2949 TransactionManager->AbortTransaction();
56472095 2950}
8267fbd9 2951 /*}}}*/
448c38bd
DK
2952// AcqIndex::Done - Finished a fetch /*{{{*/
2953// ---------------------------------------------------------------------
2954/* This goes through a number of states.. On the initial fetch the
2955 method could possibly return an alternate filename which points
2956 to the uncompressed version of the file. If this is so the file
2957 is copied into the partial directory. In all other cases the file
2958 is decompressed with a compressed uri. */
2959void pkgAcqIndex::Done(string const &Message,
2960 HashStringList const &Hashes,
2961 pkgAcquire::MethodConfig const * const Cfg)
8d6c5839 2962{
448c38bd
DK
2963 Item::Done(Message,Hashes,Cfg);
2964
2965 switch(Stage)
2966 {
2967 case STAGE_DOWNLOAD:
0179cfa8 2968 StageDownloadDone(Message);
448c38bd
DK
2969 break;
2970 case STAGE_DECOMPRESS_AND_VERIFY:
0179cfa8 2971 StageDecompressDone();
448c38bd
DK
2972 break;
2973 }
8d6c5839
MV
2974}
2975 /*}}}*/
448c38bd 2976// AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
0179cfa8 2977void pkgAcqIndex::StageDownloadDone(string const &Message)
6bf93605 2978{
0179cfa8 2979 Local = true;
448c38bd 2980 Complete = true;
6bf93605 2981
0179cfa8
DK
2982 std::string const AltFilename = LookupTag(Message,"Alt-Filename");
2983 std::string Filename = LookupTag(Message,"Filename");
2984
2985 // we need to verify the file against the current Release file again
2986 // on if-modfied-since hit to avoid a stale attack against us
2987 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
6bf93605 2988 {
0179cfa8
DK
2989 // copy FinalFile into partial/ so that we check the hash again
2990 string const FinalFile = GetExistingFilename(GetFinalFileNameFromURI(Target.URI));
2991 if (symlink(FinalFile.c_str(), DestFile.c_str()) != 0)
2992 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking final file %s back to %s failed", FinalFile.c_str(), DestFile.c_str());
2993 else
2994 {
2995 EraseFileName = DestFile;
2996 Filename = DestFile;
2997 }
448c38bd 2998 Stage = STAGE_DECOMPRESS_AND_VERIFY;
0179cfa8 2999 Desc.URI = "store:" + Filename;
448c38bd 3000 QueueURI(Desc);
0179cfa8 3001 SetActiveSubprocess(::URI(Desc.URI).Access);
448c38bd 3002 return;
6bf93605 3003 }
0179cfa8
DK
3004 // methods like file:// give us an alternative (uncompressed) file
3005 else if (Target.KeepCompressed == false && AltFilename.empty() == false)
3006 {
0179cfa8 3007 Filename = AltFilename;
e169fa4a 3008 EraseFileName.clear();
0179cfa8 3009 }
448c38bd
DK
3010 // Methods like e.g. "file:" will give us a (compressed) FileName that is
3011 // not the "DestFile" we set, in this case we uncompress from the local file
0179cfa8 3012 else if (Filename != DestFile && RealFileExists(DestFile) == false)
af9e40c9 3013 {
0179cfa8
DK
3014 // symlinking ensures that the filename can be used for compression detection
3015 // that is e.g. needed for by-hash which has no extension over file
3016 if (symlink(Filename.c_str(),DestFile.c_str()) != 0)
3017 _error->WarningE("pkgAcqIndex::StageDownloadDone", "Symlinking file %s to %s failed", Filename.c_str(), DestFile.c_str());
9bd2313a
DK
3018 else
3019 {
0179cfa8
DK
3020 EraseFileName = DestFile;
3021 Filename = DestFile;
9bd2313a 3022 }
af9e40c9 3023 }
448c38bd 3024
0179cfa8
DK
3025 Stage = STAGE_DECOMPRESS_AND_VERIFY;
3026 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
3027 if (Filename != DestFile && flExtension(Filename) == flExtension(DestFile))
3028 Desc.URI = "copy:" + Filename;
af9e40c9 3029 else
0179cfa8
DK
3030 Desc.URI = "store:" + Filename;
3031 if (DestFile == Filename)
9bd2313a
DK
3032 {
3033 if (CurrentCompressionExtension == "uncompressed")
0179cfa8
DK
3034 return StageDecompressDone();
3035 DestFile = "/dev/null";
9bd2313a 3036 }
af9e40c9 3037
e169fa4a 3038 if (EraseFileName.empty() && Filename != AltFilename)
0179cfa8
DK
3039 EraseFileName = Filename;
3040
448c38bd 3041 // queue uri for the next stage
448c38bd 3042 QueueURI(Desc);
0179cfa8 3043 SetActiveSubprocess(::URI(Desc.URI).Access);
a9bb651a
MV
3044}
3045 /*}}}*/
448c38bd 3046// AcqIndex::StageDecompressDone - Final verification /*{{{*/
0179cfa8 3047void pkgAcqIndex::StageDecompressDone()
a9bb651a 3048{
0179cfa8
DK
3049 if (DestFile == "/dev/null")
3050 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
af9e40c9 3051
448c38bd
DK
3052 // Done, queue for rename on transaction finished
3053 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
fe0f7911
DK
3054}
3055 /*}}}*/
c8a4ce6c 3056pkgAcqIndex::~pkgAcqIndex() {}
448c38bd
DK
3057
3058
03e39e59
AL
3059// AcqArchive::AcqArchive - Constructor /*{{{*/
3060// ---------------------------------------------------------------------
17caf1b1
AL
3061/* This just sets up the initial fetch environment and queues the first
3062 possibilitiy */
448c38bd
DK
3063pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
3064 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
30e1eab5 3065 string &StoreFilename) :
6c55f07a 3066 Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
448c38bd 3067 StoreFilename(StoreFilename), Vf(Version.FileList()),
b3d44315 3068 Trusted(false)
03e39e59 3069{
7d8afa39 3070 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
3071
3072 if (Version.Arch() == 0)
bdae53f1 3073 {
d1f1f6a8 3074 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
3075 "This might mean you need to manually fix this package. "
3076 "(due to missing arch)"),
40f8a8ba 3077 Version.ParentPkg().FullName().c_str());
bdae53f1
AL
3078 return;
3079 }
813c8eea 3080
b2e465d6
AL
3081 /* We need to find a filename to determine the extension. We make the
3082 assumption here that all the available sources for this version share
3083 the same extension.. */
3084 // Skip not source sources, they do not have file fields.
69c2ecbd 3085 for (; Vf.end() == false; ++Vf)
b2e465d6 3086 {
b07aeb1a 3087 if (Vf.File().Flagged(pkgCache::Flag::NotSource))
b2e465d6
AL
3088 continue;
3089 break;
3090 }
3091
3092 // Does not really matter here.. we are going to fail out below
3093 if (Vf.end() != true)
3094 {
3095 // If this fails to get a file name we will bomb out below.
3096 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
3097 if (_error->PendingError() == true)
3098 return;
3099
3100 // Generate the final file name as: package_version_arch.foo
3101 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
3102 QuoteString(Version.VerStr(),"_:") + '_' +
3103 QuoteString(Version.Arch(),"_:.") +
3104 "." + flExtension(Parse.FileName());
3105 }
b3d44315
MV
3106
3107 // check if we have one trusted source for the package. if so, switch
6c34ccca
DK
3108 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
3109 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
3110 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
3111 bool seenUntrusted = false;
f7f0d6c7 3112 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
b3d44315
MV
3113 {
3114 pkgIndexFile *Index;
3115 if (Sources->FindIndex(i.File(),Index) == false)
3116 continue;
6c34ccca
DK
3117
3118 if (debugAuth == true)
b3d44315 3119 std::cerr << "Checking index: " << Index->Describe()
6c34ccca
DK
3120 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
3121
3122 if (Index->IsTrusted() == true)
3123 {
b3d44315 3124 Trusted = true;
6c34ccca
DK
3125 if (allowUnauth == false)
3126 break;
b3d44315 3127 }
6c34ccca
DK
3128 else
3129 seenUntrusted = true;
b3d44315
MV
3130 }
3131
a3371852
MV
3132 // "allow-unauthenticated" restores apts old fetching behaviour
3133 // that means that e.g. unauthenticated file:// uris are higher
3134 // priority than authenticated http:// uris
6c34ccca 3135 if (allowUnauth == true && seenUntrusted == true)
a3371852
MV
3136 Trusted = false;
3137
03e39e59 3138 // Select a source
b185acc2 3139 if (QueueNext() == false && _error->PendingError() == false)
d57f6084
DK
3140 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
3141 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
b185acc2
AL
3142}
3143 /*}}}*/
3144// AcqArchive::QueueNext - Queue the next file source /*{{{*/
3145// ---------------------------------------------------------------------
17caf1b1
AL
3146/* This queues the next available file version for download. It checks if
3147 the archive is already available in the cache and stashs the MD5 for
3148 checking later. */
b185acc2 3149bool pkgAcqArchive::QueueNext()
a722b2c5 3150{
f7f0d6c7 3151 for (; Vf.end() == false; ++Vf)
03e39e59 3152 {
448c38bd 3153 pkgCache::PkgFileIterator const PkgF = Vf.File();
03e39e59 3154 // Ignore not source sources
b07aeb1a 3155 if (PkgF.Flagged(pkgCache::Flag::NotSource))
03e39e59
AL
3156 continue;
3157
3158 // Try to cross match against the source list
b2e465d6 3159 pkgIndexFile *Index;
448c38bd 3160 if (Sources->FindIndex(PkgF, Index) == false)
b2e465d6 3161 continue;
b07aeb1a 3162 LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
448c38bd 3163
b3d44315
MV
3164 // only try to get a trusted package from another source if that source
3165 // is also trusted
3166 if(Trusted && !Index->IsTrusted())
3167 continue;
3168
03e39e59
AL
3169 // Grab the text package record
3170 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
3171 if (_error->PendingError() == true)
b185acc2 3172 return false;
b3501edb 3173
b2e465d6 3174 string PkgFile = Parse.FileName();
b3501edb
DK
3175 ExpectedHashes = Parse.Hashes();
3176
03e39e59 3177 if (PkgFile.empty() == true)
b2e465d6
AL
3178 return _error->Error(_("The package index files are corrupted. No Filename: "
3179 "field for package %s."),
3180 Version.ParentPkg().Name());
a6568219 3181
b3d44315
MV
3182 Desc.URI = Index->ArchiveURI(PkgFile);
3183 Desc.Description = Index->ArchiveInfo(Version);
3184 Desc.Owner = this;
40f8a8ba 3185 Desc.ShortDesc = Version.ParentPkg().FullName(true);
b3d44315 3186
17caf1b1 3187 // See if we already have the file. (Legacy filenames)
a6568219
AL
3188 FileSize = Version->Size;
3189 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
3190 struct stat Buf;
3191 if (stat(FinalFile.c_str(),&Buf) == 0)
3192 {
3193 // Make sure the size matches
73da43e9 3194 if ((unsigned long long)Buf.st_size == Version->Size)
a6568219
AL
3195 {
3196 Complete = true;
3197 Local = true;
3198 Status = StatDone;
30e1eab5 3199 StoreFilename = DestFile = FinalFile;
b185acc2 3200 return true;
a6568219
AL
3201 }
3202
6b1ff003
AL
3203 /* Hmm, we have a file and its size does not match, this means it is
3204 an old style mismatched arch */
51818f26 3205 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
a6568219 3206 }
17caf1b1
AL
3207
3208 // Check it again using the new style output filenames
3209 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
3210 if (stat(FinalFile.c_str(),&Buf) == 0)
3211 {
3212 // Make sure the size matches
73da43e9 3213 if ((unsigned long long)Buf.st_size == Version->Size)
17caf1b1
AL
3214 {
3215 Complete = true;
3216 Local = true;
3217 Status = StatDone;
3218 StoreFilename = DestFile = FinalFile;
3219 return true;
3220 }
3221
1e3f4083 3222 /* Hmm, we have a file and its size does not match, this shouldn't
17caf1b1 3223 happen.. */
51818f26 3224 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
17caf1b1
AL
3225 }
3226
3227 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
3228
3229 // Check the destination file
3230 if (stat(DestFile.c_str(),&Buf) == 0)
3231 {
3232 // Hmm, the partial file is too big, erase it
73da43e9 3233 if ((unsigned long long)Buf.st_size > Version->Size)
51818f26 3234 RemoveFile("pkgAcqArchive::QueueNext", DestFile);
6b1ff003
AL
3235 else
3236 PartialSize = Buf.st_size;
3237 }
de31189f
DK
3238
3239 // Disables download of archives - useful if no real installation follows,
3240 // e.g. if we are just interested in proposed installation order
3241 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
3242 {
3243 Complete = true;
3244 Local = true;
3245 Status = StatDone;
3246 StoreFilename = DestFile = FinalFile;
3247 return true;
3248 }
3249
03e39e59 3250 // Create the item
b2e465d6 3251 Local = false;
03e39e59 3252 QueueURI(Desc);
b185acc2 3253
f7f0d6c7 3254 ++Vf;
b185acc2 3255 return true;
03e39e59 3256 }
b185acc2
AL
3257 return false;
3258}
03e39e59
AL
3259 /*}}}*/
3260// AcqArchive::Done - Finished fetching /*{{{*/
3261// ---------------------------------------------------------------------
3262/* */
448c38bd
DK
3263void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
3264 pkgAcquire::MethodConfig const * const Cfg)
03e39e59 3265{
448c38bd 3266 Item::Done(Message, Hashes, Cfg);
a6568219
AL
3267
3268 // Grab the output filename
dd676dc7 3269 std::string const FileName = LookupTag(Message,"Filename");
08ea7806 3270 if (DestFile != FileName && RealFileExists(DestFile) == false)
a6568219 3271 {
30e1eab5 3272 StoreFilename = DestFile = FileName;
a6568219 3273 Local = true;
5684f71f 3274 Complete = true;
a6568219
AL
3275 return;
3276 }
5684f71f 3277
a6568219 3278 // Done, move it into position
295d848b 3279 string const FinalFile = GetFinalFilename();
a6568219 3280 Rename(DestFile,FinalFile);
30e1eab5 3281 StoreFilename = DestFile = FinalFile;
03e39e59
AL
3282 Complete = true;
3283}
3284 /*}}}*/
db890fdb
AL
3285// AcqArchive::Failed - Failure handler /*{{{*/
3286// ---------------------------------------------------------------------
3287/* Here we try other sources */
448c38bd 3288void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
db890fdb 3289{
03aa0847
DK
3290 Item::Failed(Message,Cnf);
3291
448c38bd 3292 /* We don't really want to retry on failed media swaps, this prevents
b2e465d6
AL
3293 that. An interesting observation is that permanent failures are not
3294 recorded. */
448c38bd 3295 if (Cnf->Removable == true &&
b2e465d6
AL
3296 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3297 {
3298 // Vf = Version.FileList();
f7f0d6c7 3299 while (Vf.end() == false) ++Vf;
b2e465d6 3300 StoreFilename = string();
b2e465d6
AL
3301 return;
3302 }
03aa0847
DK
3303
3304 Status = StatIdle;
db890fdb 3305 if (QueueNext() == false)
7d8afa39
AL
3306 {
3307 // This is the retry counter
3308 if (Retries != 0 &&
3309 Cnf->LocalOnly == false &&
3310 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3311 {
3312 Retries--;
3313 Vf = Version.FileList();
3314 if (QueueNext() == true)
3315 return;
3316 }
03aa0847 3317
9dbb421f 3318 StoreFilename = string();
03aa0847 3319 Status = StatError;
7d8afa39 3320 }
db890fdb
AL
3321}
3322 /*}}}*/
448c38bd 3323APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
b3d44315
MV
3324{
3325 return Trusted;
3326}
92fcbfc1 3327 /*}}}*/
448c38bd 3328void pkgAcqArchive::Finished() /*{{{*/
ab559b35
AL
3329{
3330 if (Status == pkgAcquire::Item::StatDone &&
3331 Complete == true)
3332 return;
3333 StoreFilename = string();
3334}
3335 /*}}}*/
448c38bd
DK
3336std::string pkgAcqArchive::DescURI() const /*{{{*/
3337{
3338 return Desc.URI;
3339}
3340 /*}}}*/
3341std::string pkgAcqArchive::ShortDesc() const /*{{{*/
3342{
3343 return Desc.ShortDesc;
3344}
3345 /*}}}*/
c8a4ce6c 3346pkgAcqArchive::~pkgAcqArchive() {}
448c38bd 3347
d56e2917 3348// AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
6fd4b4c0
DK
3349class pkgAcqChangelog::Private
3350{
3351 public:
3352 std::string FinalFile;
3353};
d56e2917
DK
3354pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
3355 std::string const &DestDir, std::string const &DestFilename) :
6fd4b4c0 3356 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
d56e2917
DK
3357{
3358 Desc.URI = URI(Ver);
3359 Init(DestDir, DestFilename);
3360}
3361// some parameters are char* here as they come likely from char* interfaces – which can also return NULL
3362pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
3363 char const * const Component, char const * const SrcName, char const * const SrcVersion,
3364 const string &DestDir, const string &DestFilename) :
6fd4b4c0 3365 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
d56e2917
DK
3366{
3367 Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
3368 Init(DestDir, DestFilename);
3369}
3370pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
3371 std::string const &URI, char const * const SrcName, char const * const SrcVersion,
3372 const string &DestDir, const string &DestFilename) :
6fd4b4c0 3373 pkgAcquire::Item(Owner), d(new pkgAcqChangelog::Private()), SrcName(SrcName), SrcVersion(SrcVersion)
d56e2917
DK
3374{
3375 Desc.URI = URI;
3376 Init(DestDir, DestFilename);
3377}
3378void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
3379{
3380 if (Desc.URI.empty())
3381 {
3382 Status = StatError;
3383 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3384 strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3385 // Let the error message print something sensible rather than "Failed to fetch /"
3386 if (DestFilename.empty())
3387 DestFile = SrcName + ".changelog";
3388 else
3389 DestFile = DestFilename;
3390 Desc.URI = "changelog:/" + DestFile;
3391 return;
3392 }
3393
6fd4b4c0
DK
3394 std::string DestFileName;
3395 if (DestFilename.empty())
3396 DestFileName = flCombine(DestFile, SrcName + ".changelog");
3397 else
3398 DestFileName = flCombine(DestFile, DestFilename);
d1256170 3399
6fd4b4c0
DK
3400 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
3401 std::string const systemTemp = GetTempDir(SandboxUser);
3402 char tmpname[1000];
3403 snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
3404 if (NULL == mkdtemp(tmpname))
3405 {
3406 _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
3407 Status = StatError;
3408 return;
d56e2917 3409 }
6fd4b4c0 3410 TemporaryDirectory = tmpname;
d56e2917 3411
6fd4b4c0
DK
3412 ChangeOwnerAndPermissionOfFile("Item::QueueURI", TemporaryDirectory.c_str(),
3413 SandboxUser.c_str(), "root", 0700);
3414
3415 DestFile = flCombine(TemporaryDirectory, DestFileName);
3416 if (DestDir.empty() == false)
872bd447 3417 {
6fd4b4c0 3418 d->FinalFile = flCombine(DestDir, DestFileName);
872bd447
DK
3419 if (RealFileExists(d->FinalFile))
3420 {
3421 FileFd file1, file2;
3422 if (file1.Open(DestFile, FileFd::WriteOnly | FileFd::Create | FileFd::Exclusive) &&
3423 file2.Open(d->FinalFile, FileFd::ReadOnly) && CopyFile(file2, file1))
3424 {
3425 struct timeval times[2];
3426 times[0].tv_sec = times[1].tv_sec = file2.ModificationTime();
3427 times[0].tv_usec = times[1].tv_usec = 0;
3428 utimes(DestFile.c_str(), times);
3429 }
3430 }
3431 }
d56e2917
DK
3432
3433 Desc.ShortDesc = "Changelog";
3434 strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
3435 Desc.Owner = this;
3436 QueueURI(Desc);
d56e2917
DK
3437}
3438 /*}}}*/
3439std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
3440{
b5aba909
DK
3441 std::string const confOnline = "Acquire::Changelogs::AlwaysOnline";
3442 bool AlwaysOnline = _config->FindB(confOnline, false);
3443 if (AlwaysOnline == false)
3444 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3445 {
3446 pkgCache::PkgFileIterator const PF = VF.File();
3447 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3448 continue;
3449 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3450 if (RF->Origin != 0 && _config->FindB(confOnline + "::Origin::" + RF.Origin(), false))
3451 {
3452 AlwaysOnline = true;
3453 break;
3454 }
3455 }
3456 if (AlwaysOnline == false)
3457 {
3458 pkgCache::PkgIterator const Pkg = Ver.ParentPkg();
3459 if (Pkg->CurrentVer != 0 && Pkg.CurrentVer() == Ver)
3460 {
3461 std::string const basename = std::string("/usr/share/doc/") + Pkg.Name() + "/changelog";
3462 std::string const debianname = basename + ".Debian";
3463 if (FileExists(debianname))
3464 return "copy://" + debianname;
3465 else if (FileExists(debianname + ".gz"))
3466 return "gzip://" + debianname + ".gz";
3467 else if (FileExists(basename))
3468 return "copy://" + basename;
3469 else if (FileExists(basename + ".gz"))
3470 return "gzip://" + basename + ".gz";
3471 }
3472 }
3473
d56e2917
DK
3474 char const * const SrcName = Ver.SourcePkgName();
3475 char const * const SrcVersion = Ver.SourceVerStr();
d56e2917
DK
3476 // find the first source for this version which promises a changelog
3477 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3478 {
3479 pkgCache::PkgFileIterator const PF = VF.File();
3480 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3481 continue;
d56e2917
DK
3482 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3483 std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
3484 if (uri.empty())
3485 continue;
3486 return uri;
3487 }
3488 return "";
3489}
3490std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
3491{
3492 if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
3493 return "";
3494 std::string const serverConfig = "Acquire::Changelogs::URI";
3495 std::string server;
3496#define APT_EMPTY_SERVER \
3497 if (server.empty() == false) \
3498 { \
3499 if (server != "no") \
3500 return server; \
3501 return ""; \
3502 }
3503#define APT_CHECK_SERVER(X, Y) \
3504 if (Rls->X != 0) \
3505 { \
3506 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
3507 server = _config->Find(specialServerConfig); \
3508 APT_EMPTY_SERVER \
3509 }
3510 // this way e.g. Debian-Security can fallback to Debian
3511 APT_CHECK_SERVER(Label, "Override::")
3512 APT_CHECK_SERVER(Origin, "Override::")
3513
3514 if (RealFileExists(Rls.FileName()))
3515 {
3516 _error->PushToStack();
3517 FileFd rf;
3518 /* This can be costly. A caller wanting to get millions of URIs might
3519 want to do this on its own once and use Override settings.
3520 We don't do this here as Origin/Label are not as unique as they
3521 should be so this could produce request order-dependent anomalies */
3522 if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
3523 {
3524 pkgTagFile TagFile(&rf, rf.Size());
3525 pkgTagSection Section;
3526 if (TagFile.Step(Section) == true)
3527 server = Section.FindS("Changelogs");
3528 }
3529 _error->RevertToStack();
3530 APT_EMPTY_SERVER
3531 }
3532
3533 APT_CHECK_SERVER(Label, "")
3534 APT_CHECK_SERVER(Origin, "")
3535#undef APT_CHECK_SERVER
3536#undef APT_EMPTY_SERVER
3537 return "";
3538}
3539std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
3540 char const * const Component, char const * const SrcName,
3541 char const * const SrcVersion)
3542{
3543 return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
3544}
3545std::string pkgAcqChangelog::URI(std::string const &Template,
3546 char const * const Component, char const * const SrcName,
3547 char const * const SrcVersion)
3548{
430481e7 3549 if (Template.find("@CHANGEPATH@") == std::string::npos)
d56e2917
DK
3550 return "";
3551
3552 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3553 std::string Src = SrcName;
3554 std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
3555 path.append("/").append(Src).append("/");
3556 path.append(Src).append("_").append(StripEpoch(SrcVersion));
3557 // we omit component for releases without one (= flat-style repositories)
3558 if (Component != NULL && strlen(Component) != 0)
3559 path = std::string(Component) + "/" + path;
3560
430481e7 3561 return SubstVar(Template, "@CHANGEPATH@", path);
d56e2917
DK
3562}
3563 /*}}}*/
3564// AcqChangelog::Failed - Failure handler /*{{{*/
3565void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3566{
3567 Item::Failed(Message,Cnf);
3568
3569 std::string errText;
3570 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3571 strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3572
3573 // Error is probably something techy like 404 Not Found
3574 if (ErrorText.empty())
3575 ErrorText = errText;
3576 else
3577 ErrorText = errText + " (" + ErrorText + ")";
d56e2917
DK
3578}
3579 /*}}}*/
3580// AcqChangelog::Done - Item downloaded OK /*{{{*/
3581void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
3582 pkgAcquire::MethodConfig const * const Cnf)
3583{
3584 Item::Done(Message,CalcHashes,Cnf);
6fd4b4c0 3585 if (d->FinalFile.empty() == false)
872bd447
DK
3586 {
3587 if (RemoveFile("pkgAcqChangelog::Done", d->FinalFile) == false ||
3588 Rename(DestFile, d->FinalFile) == false)
3589 Status = StatError;
3590 }
d56e2917
DK
3591
3592 Complete = true;
3593}
3594 /*}}}*/
3595pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3596{
3597 if (TemporaryDirectory.empty() == false)
3598 {
51818f26 3599 RemoveFile("~pkgAcqChangelog", DestFile);
d56e2917
DK
3600 rmdir(TemporaryDirectory.c_str());
3601 }
6fd4b4c0 3602 delete d;
d56e2917
DK
3603}
3604 /*}}}*/
3605
36375005 3606// AcqFile::pkgAcqFile - Constructor /*{{{*/
448c38bd
DK
3607pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
3608 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
77278c2b 3609 const string &DestDir, const string &DestFilename,
448c38bd 3610 bool const IsIndexFile) :
6c55f07a 3611 Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
36375005 3612{
08cfc005 3613 Retries = _config->FindI("Acquire::Retries",0);
448c38bd 3614
46e00f9d
MV
3615 if(!DestFilename.empty())
3616 DestFile = DestFilename;
3617 else if(!DestDir.empty())
3618 DestFile = DestDir + "/" + flNotDir(URI);
3619 else
3620 DestFile = flNotDir(URI);
3621
36375005
AL
3622 // Create the item
3623 Desc.URI = URI;
3624 Desc.Description = Dsc;
3625 Desc.Owner = this;
3626
3627 // Set the short description to the archive component
3628 Desc.ShortDesc = ShortDesc;
448c38bd 3629
36375005
AL
3630 // Get the transfer sizes
3631 FileSize = Size;
3632 struct stat Buf;
3633 if (stat(DestFile.c_str(),&Buf) == 0)
3634 {
3635 // Hmm, the partial file is too big, erase it
ed9665ae 3636 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
51818f26 3637 RemoveFile("pkgAcqFile", DestFile);
36375005
AL
3638 else
3639 PartialSize = Buf.st_size;
3640 }
092ae175 3641
36375005
AL
3642 QueueURI(Desc);
3643}
3644 /*}}}*/
3645// AcqFile::Done - Item downloaded OK /*{{{*/
448c38bd
DK
3646void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
3647 pkgAcquire::MethodConfig const * const Cnf)
36375005 3648{
448c38bd 3649 Item::Done(Message,CalcHashes,Cnf);
495e5cb2 3650
dd676dc7 3651 std::string const FileName = LookupTag(Message,"Filename");
36375005 3652 Complete = true;
448c38bd 3653
36375005
AL
3654 // The files timestamp matches
3655 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
3656 return;
448c38bd 3657
36375005 3658 // We have to copy it into place
08ea7806 3659 if (RealFileExists(DestFile.c_str()) == false)
36375005
AL
3660 {
3661 Local = true;
459681d3
AL
3662 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
3663 Cnf->Removable == true)
917ae805
AL
3664 {
3665 Desc.URI = "copy:" + FileName;
3666 QueueURI(Desc);
3667 return;
3668 }
448c38bd 3669
83ab33fc
AL
3670 // Erase the file if it is a symlink so we can overwrite it
3671 struct stat St;
3672 if (lstat(DestFile.c_str(),&St) == 0)
3673 {
3674 if (S_ISLNK(St.st_mode) != 0)
51818f26 3675 RemoveFile("pkgAcqFile::Done", DestFile);
83ab33fc 3676 }
448c38bd 3677
83ab33fc 3678 // Symlink the file
917ae805
AL
3679 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
3680 {
03aa0847
DK
3681 _error->PushToStack();
3682 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
3683 std::stringstream msg;
95278287 3684 _error->DumpErrors(msg, GlobalError::DEBUG, false);
03aa0847
DK
3685 _error->RevertToStack();
3686 ErrorText = msg.str();
917ae805
AL
3687 Status = StatError;
3688 Complete = false;
448c38bd 3689 }
36375005
AL
3690 }
3691}
3692 /*}}}*/
08cfc005
AL
3693// AcqFile::Failed - Failure handler /*{{{*/
3694// ---------------------------------------------------------------------
3695/* Here we try other sources */
448c38bd 3696void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
08cfc005 3697{
03aa0847
DK
3698 Item::Failed(Message,Cnf);
3699
08cfc005
AL
3700 // This is the retry counter
3701 if (Retries != 0 &&
3702 Cnf->LocalOnly == false &&
3703 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3704 {
03aa0847 3705 --Retries;
08cfc005 3706 QueueURI(Desc);
03aa0847 3707 Status = StatIdle;
08cfc005
AL
3708 return;
3709 }
03aa0847 3710
08cfc005
AL
3711}
3712 /*}}}*/
448c38bd 3713string pkgAcqFile::Custom600Headers() const /*{{{*/
77278c2b
MV
3714{
3715 if (IsIndexFile)
3716 return "\nIndex-File: true";
61a07c57 3717 return "";
77278c2b
MV
3718}
3719 /*}}}*/
c8a4ce6c 3720pkgAcqFile::~pkgAcqFile() {}