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