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