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