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