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