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