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