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