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