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