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