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