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