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