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