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