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