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