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