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