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