]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
merge indexRecords into metaIndex
[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
812 struct stat Buf;
813 if (stat(FinalFile.c_str(),&Buf) == 0)
814 Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
815
816 return Header;
817 }
818 /*}}}*/
819 // AcqMetaBase::QueueForSignatureVerify /*{{{*/
820 void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature)
821 {
822 AuthPass = true;
823 I->Desc.URI = "gpgv:" + Signature;
824 I->DestFile = File;
825 QueueURI(I->Desc);
826 I->SetActiveSubprocess("gpgv");
827 }
828 /*}}}*/
829 // AcqMetaBase::CheckDownloadDone /*{{{*/
830 bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const
831 {
832 // We have just finished downloading a Release file (it is not
833 // verified yet)
834
835 string const FileName = LookupTag(Message,"Filename");
836 if (FileName.empty() == true)
837 {
838 I->Status = StatError;
839 I->ErrorText = "Method gave a blank filename";
840 return false;
841 }
842
843 if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
844 {
845 I->Local = true;
846 I->Desc.URI = "copy:" + FileName;
847 I->QueueURI(I->Desc);
848 return false;
849 }
850
851 // make sure to verify against the right file on I-M-S hit
852 bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
853 if (IMSHit == false && Hashes.usable())
854 {
855 // detect IMS-Hits servers haven't detected by Hash comparison
856 std::string const FinalFile = I->GetFinalFilename();
857 if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
858 {
859 IMSHit = true;
860 unlink(I->DestFile.c_str());
861 }
862 }
863
864 if(IMSHit == true)
865 {
866 // for simplicity, the transaction manager is always InRelease
867 // even if it doesn't exist.
868 if (TransactionManager != NULL)
869 TransactionManager->IMSHit = true;
870 I->PartialFile = I->DestFile = I->GetFinalFilename();
871 }
872
873 // set Item to complete as the remaining work is all local (verify etc)
874 I->Complete = true;
875
876 return true;
877 }
878 /*}}}*/
879 bool pkgAcqMetaBase::CheckAuthDone(string const &Message) /*{{{*/
880 {
881 // At this point, the gpgv method has succeeded, so there is a
882 // valid signature from a key in the trusted keyring. We
883 // perform additional verification of its contents, and use them
884 // to verify the indexes we are about to download
885
886 if (TransactionManager->IMSHit == false)
887 {
888 // open the last (In)Release if we have it
889 std::string const FinalFile = GetFinalFilename();
890 std::string FinalRelease;
891 std::string FinalInRelease;
892 if (APT::String::Endswith(FinalFile, "InRelease"))
893 {
894 FinalInRelease = FinalFile;
895 FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
896 }
897 else
898 {
899 FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
900 FinalRelease = FinalFile;
901 }
902 if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
903 {
904 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
905 if (TransactionManager->LastMetaIndexParser != NULL)
906 {
907 _error->PushToStack();
908 if (RealFileExists(FinalInRelease))
909 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
910 else
911 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
912 // its unlikely to happen, but if what we have is bad ignore it
913 if (_error->PendingError())
914 {
915 delete TransactionManager->LastMetaIndexParser;
916 TransactionManager->LastMetaIndexParser = NULL;
917 }
918 _error->RevertToStack();
919 }
920 }
921 }
922
923 if (TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText) == false)
924 {
925 Status = StatAuthError;
926 return false;
927 }
928
929 if (!VerifyVendor(Message))
930 {
931 Status = StatAuthError;
932 return false;
933 }
934
935 if (_config->FindB("Debug::pkgAcquire::Auth", false))
936 std::cerr << "Signature verification succeeded: "
937 << DestFile << std::endl;
938
939 // Download further indexes with verification
940 QueueIndexes(true);
941
942 return true;
943 }
944 /*}}}*/
945 void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/
946 {
947 // at this point the real Items are loaded in the fetcher
948 ExpectedAdditionalItems = 0;
949
950 for (std::vector <IndexTarget>::const_iterator Target = IndexTargets.begin();
951 Target != IndexTargets.end();
952 ++Target)
953 {
954 bool trypdiff = _config->FindB("Acquire::PDiffs", true);
955 if (verify == true)
956 {
957 if (TransactionManager->MetaIndexParser->Exists(Target->MetaKey) == false)
958 {
959 // optional targets that we do not have in the Release file are skipped
960 if (Target->IsOptional)
961 continue;
962
963 Status = StatAuthError;
964 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target->MetaKey.c_str());
965 return;
966 }
967
968 if (RealFileExists(GetFinalFileNameFromURI(Target->URI)))
969 {
970 if (TransactionManager->LastMetaIndexParser != NULL)
971 {
972 HashStringList const newFile = GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, Target->MetaKey);
973 HashStringList const oldFile = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target->MetaKey);
974 if (newFile == oldFile)
975 {
976 // we have the file already, no point in trying to acquire it again
977 new NoActionItem(Owner, *Target);
978 continue;
979 }
980 }
981 }
982 else
983 trypdiff = false; // no file to patch
984
985 // check if we have patches available
986 trypdiff &= TransactionManager->MetaIndexParser->Exists(Target->MetaKey + ".diff/Index");
987 }
988 // if we have no file to patch, no point in trying
989 trypdiff &= RealFileExists(GetFinalFileNameFromURI(Target->URI));
990
991 // no point in patching from local sources
992 if (trypdiff)
993 {
994 std::string const proto = Target->URI.substr(0, strlen("file:/"));
995 if (proto == "file:/" || proto == "copy:/" || proto == "cdrom:")
996 trypdiff = false;
997 }
998
999 // Queue the Index file (Packages, Sources, Translation-$foo, …)
1000 if (trypdiff)
1001 new pkgAcqDiffIndex(Owner, TransactionManager, *Target);
1002 else
1003 new pkgAcqIndex(Owner, TransactionManager, *Target);
1004 }
1005 }
1006 /*}}}*/
1007 bool pkgAcqMetaBase::VerifyVendor(string const &Message) /*{{{*/
1008 {
1009 string::size_type pos;
1010
1011 // check for missing sigs (that where not fatal because otherwise we had
1012 // bombed earlier)
1013 string missingkeys;
1014 string msg = _("There is no public key available for the "
1015 "following key IDs:\n");
1016 pos = Message.find("NO_PUBKEY ");
1017 if (pos != std::string::npos)
1018 {
1019 string::size_type start = pos+strlen("NO_PUBKEY ");
1020 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1021 missingkeys += (Fingerprint);
1022 }
1023 if(!missingkeys.empty())
1024 _error->Warning("%s", (msg + missingkeys).c_str());
1025
1026 string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
1027
1028 if (Transformed == "../project/experimental")
1029 {
1030 Transformed = "experimental";
1031 }
1032
1033 pos = Transformed.rfind('/');
1034 if (pos != string::npos)
1035 {
1036 Transformed = Transformed.substr(0, pos);
1037 }
1038
1039 if (Transformed == ".")
1040 {
1041 Transformed = "";
1042 }
1043
1044 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1045 TransactionManager->MetaIndexParser->GetValidUntil() > 0) {
1046 time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
1047 if (invalid_since > 0)
1048 {
1049 std::string errmsg;
1050 strprintf(errmsg,
1051 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1052 // the time since then the file is invalid - formatted in the same way as in
1053 // the download progress display (e.g. 7d 3h 42min 1s)
1054 _("Release file for %s is expired (invalid since %s). "
1055 "Updates for this repository will not be applied."),
1056 Target.URI.c_str(), TimeToStr(invalid_since).c_str());
1057 if (ErrorText.empty())
1058 ErrorText = errmsg;
1059 return _error->Error("%s", errmsg.c_str());
1060 }
1061 }
1062
1063 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1064 as a prevention of downgrading us to older (still valid) files */
1065 if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
1066 TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
1067 {
1068 TransactionManager->IMSHit = true;
1069 unlink(DestFile.c_str());
1070 PartialFile = DestFile = GetFinalFilename();
1071 // load the 'old' file in the 'new' one instead of flipping pointers as
1072 // the new one isn't owned by us, while the old one is so cleanup would be confused.
1073 TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
1074 delete TransactionManager->LastMetaIndexParser;
1075 TransactionManager->LastMetaIndexParser = NULL;
1076 }
1077
1078 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1079 {
1080 std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
1081 std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
1082 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1083 }
1084
1085 if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
1086 {
1087 // This might become fatal one day
1088 // Status = StatAuthError;
1089 // ErrorText = "Conflicting distribution; expected "
1090 // + MetaIndexParser->GetExpectedDist() + " but got "
1091 // + MetaIndexParser->GetCodename();
1092 // return false;
1093 if (!Transformed.empty())
1094 {
1095 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1096 Desc.Description.c_str(),
1097 Transformed.c_str(),
1098 TransactionManager->MetaIndexParser->GetCodename().c_str());
1099 }
1100 }
1101
1102 return true;
1103 }
1104 /*}}}*/
1105 pkgAcqMetaBase::~pkgAcqMetaBase()
1106 {
1107 }
1108
1109 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
1110 IndexTarget const &ClearsignedTarget,
1111 IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
1112 std::vector<IndexTarget> const &IndexTargets,
1113 metaIndex * const MetaIndexParser) :
1114 pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget, IndexTargets),
1115 d(NULL), ClearsignedTarget(ClearsignedTarget),
1116 DetachedDataTarget(DetachedDataTarget),
1117 MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
1118 {
1119 // index targets + (worst case:) Release/Release.gpg
1120 ExpectedAdditionalItems = IndexTargets.size() + 2;
1121 TransactionManager->Add(this);
1122 }
1123 /*}}}*/
1124 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1125 {
1126 if (LastMetaIndexParser != NULL)
1127 delete LastMetaIndexParser;
1128 }
1129 /*}}}*/
1130 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1131 string pkgAcqMetaClearSig::Custom600Headers() const
1132 {
1133 string Header = pkgAcqMetaBase::Custom600Headers();
1134 Header += "\nFail-Ignore: true";
1135 return Header;
1136 }
1137 /*}}}*/
1138 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
1139 void pkgAcqMetaClearSig::Done(std::string const &Message,
1140 HashStringList const &Hashes,
1141 pkgAcquire::MethodConfig const * const Cnf)
1142 {
1143 Item::Done(Message, Hashes, Cnf);
1144
1145 // if we expect a ClearTextSignature (InRelease), ensure that
1146 // this is what we get and if not fail to queue a
1147 // Release/Release.gpg, see #346386
1148 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
1149 {
1150 pkgAcquire::Item::Failed(Message, Cnf);
1151 RenameOnError(NotClearsigned);
1152 TransactionManager->AbortTransaction();
1153 return;
1154 }
1155
1156 if(AuthPass == false)
1157 {
1158 if(CheckDownloadDone(this, Message, Hashes) == true)
1159 QueueForSignatureVerify(this, DestFile, DestFile);
1160 return;
1161 }
1162 else if(CheckAuthDone(Message) == true)
1163 {
1164 if (TransactionManager->IMSHit == false)
1165 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1166 else if (RealFileExists(GetFinalFilename()) == false)
1167 {
1168 // We got an InRelease file IMSHit, but we haven't one, which means
1169 // we had a valid Release/Release.gpg combo stepping in, which we have
1170 // to 'acquire' now to ensure list cleanup isn't removing them
1171 new NoActionItem(Owner, DetachedDataTarget);
1172 new NoActionItem(Owner, DetachedSigTarget);
1173 }
1174 }
1175 }
1176 /*}}}*/
1177 void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
1178 {
1179 Item::Failed(Message, Cnf);
1180
1181 // we failed, we will not get additional items from this method
1182 ExpectedAdditionalItems = 0;
1183
1184 if (AuthPass == false)
1185 {
1186 // Queue the 'old' InRelease file for removal if we try Release.gpg
1187 // as otherwise the file will stay around and gives a false-auth
1188 // impression (CVE-2012-0214)
1189 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1190 Status = StatDone;
1191
1192 new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget, IndexTargets);
1193 }
1194 else
1195 {
1196 if(CheckStopAuthentication(this, Message))
1197 return;
1198
1199 _error->Warning(_("The data from '%s' is not signed. Packages "
1200 "from that repository can not be authenticated."),
1201 ClearsignedTarget.Description.c_str());
1202
1203 // No Release file was present, or verification failed, so fall
1204 // back to queueing Packages files without verification
1205 // only allow going further if the users explicitely wants it
1206 if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1207 {
1208 Status = StatDone;
1209
1210 /* InRelease files become Release files, otherwise
1211 * they would be considered as trusted later on */
1212 string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1213 string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
1214 string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
1215 string const FinalInRelease = GetFinalFilename();
1216 Rename(DestFile, PartialRelease);
1217 TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
1218
1219 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1220 {
1221 // open the last Release if we have it
1222 if (TransactionManager->IMSHit == false)
1223 {
1224 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
1225 if (TransactionManager->LastMetaIndexParser != NULL)
1226 {
1227 _error->PushToStack();
1228 if (RealFileExists(FinalInRelease))
1229 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
1230 else
1231 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
1232 // its unlikely to happen, but if what we have is bad ignore it
1233 if (_error->PendingError())
1234 {
1235 delete TransactionManager->LastMetaIndexParser;
1236 TransactionManager->LastMetaIndexParser = NULL;
1237 }
1238 _error->RevertToStack();
1239 }
1240 }
1241 }
1242
1243 // we parse the indexes here because at this point the user wanted
1244 // a repository that may potentially harm him
1245 if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
1246 /* expired Release files are still a problem you need extra force for */;
1247 else
1248 QueueIndexes(true);
1249 }
1250 }
1251 }
1252 /*}}}*/
1253
1254 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
1255 pkgAcqMetaClearSig * const TransactionManager,
1256 IndexTarget const &DataTarget,
1257 IndexTarget const &DetachedSigTarget,
1258 vector<IndexTarget> const &IndexTargets) :
1259 pkgAcqMetaBase(Owner, TransactionManager, IndexTargets, DataTarget), d(NULL),
1260 DetachedSigTarget(DetachedSigTarget)
1261 {
1262 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1263 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1264 << this->TransactionManager << std::endl;
1265
1266 DestFile = GetPartialFileNameFromURI(DataTarget.URI);
1267
1268 // Create the item
1269 Desc.Description = DataTarget.Description;
1270 Desc.Owner = this;
1271 Desc.ShortDesc = DataTarget.ShortDesc;
1272 Desc.URI = DataTarget.URI;
1273
1274 // we expect more item
1275 ExpectedAdditionalItems = IndexTargets.size();
1276 QueueURI(Desc);
1277 }
1278 /*}}}*/
1279 void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
1280 HashStringList const &Hashes,
1281 pkgAcquire::MethodConfig const * const Cfg)
1282 {
1283 Item::Done(Message,Hashes,Cfg);
1284
1285 if(CheckDownloadDone(this, Message, Hashes))
1286 {
1287 // we have a Release file, now download the Signature, all further
1288 // verify/queue for additional downloads will be done in the
1289 // pkgAcqMetaSig::Done() code
1290 new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
1291 }
1292 }
1293 /*}}}*/
1294 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1295 void pkgAcqMetaIndex::Failed(string const &Message,
1296 pkgAcquire::MethodConfig const * const Cnf)
1297 {
1298 pkgAcquire::Item::Failed(Message, Cnf);
1299 Status = StatDone;
1300
1301 _error->Warning(_("The repository '%s' does not have a Release file. "
1302 "This is deprecated, please contact the owner of the "
1303 "repository."), Target.Description.c_str());
1304
1305 // No Release file was present so fall
1306 // back to queueing Packages files without verification
1307 // only allow going further if the users explicitely wants it
1308 if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1309 {
1310 // ensure old Release files are removed
1311 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1312
1313 // queue without any kind of hashsum support
1314 QueueIndexes(false);
1315 }
1316 }
1317 /*}}}*/
1318 void pkgAcqMetaIndex::Finished() /*{{{*/
1319 {
1320 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1321 std::clog << "Finished: " << DestFile <<std::endl;
1322 if(TransactionManager != NULL &&
1323 TransactionManager->TransactionHasError() == false)
1324 TransactionManager->CommitTransaction();
1325 }
1326 /*}}}*/
1327 std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
1328 {
1329 return Target.URI;
1330 }
1331 /*}}}*/
1332 pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
1333
1334 // AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1335 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
1336 pkgAcqMetaClearSig * const TransactionManager,
1337 IndexTarget const &Target,
1338 pkgAcqMetaIndex * const MetaIndex) :
1339 pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
1340 {
1341 DestFile = GetPartialFileNameFromURI(Target.URI);
1342
1343 // remove any partial downloaded sig-file in partial/.
1344 // it may confuse proxies and is too small to warrant a
1345 // partial download anyway
1346 unlink(DestFile.c_str());
1347
1348 // set the TransactionManager
1349 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1350 std::clog << "New pkgAcqMetaSig with TransactionManager "
1351 << TransactionManager << std::endl;
1352
1353 // Create the item
1354 Desc.Description = Target.Description;
1355 Desc.Owner = this;
1356 Desc.ShortDesc = Target.ShortDesc;
1357 Desc.URI = Target.URI;
1358
1359 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1360 // so we skip the download step and go instantly to verification
1361 if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
1362 {
1363 Complete = true;
1364 Status = StatDone;
1365 PartialFile = DestFile = GetFinalFilename();
1366 MetaIndexFileSignature = DestFile;
1367 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1368 }
1369 else
1370 QueueURI(Desc);
1371 }
1372 /*}}}*/
1373 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1374 {
1375 }
1376 /*}}}*/
1377 // AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1378 void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
1379 pkgAcquire::MethodConfig const * const Cfg)
1380 {
1381 if (MetaIndexFileSignature.empty() == false)
1382 {
1383 DestFile = MetaIndexFileSignature;
1384 MetaIndexFileSignature.clear();
1385 }
1386 Item::Done(Message, Hashes, Cfg);
1387
1388 if(MetaIndex->AuthPass == false)
1389 {
1390 if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
1391 {
1392 // destfile will be modified to point to MetaIndexFile for the
1393 // gpgv method, so we need to save it here
1394 MetaIndexFileSignature = DestFile;
1395 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1396 }
1397 return;
1398 }
1399 else if(MetaIndex->CheckAuthDone(Message) == true)
1400 {
1401 if (TransactionManager->IMSHit == false)
1402 {
1403 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1404 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1405 }
1406 }
1407 }
1408 /*}}}*/
1409 void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1410 {
1411 Item::Failed(Message,Cnf);
1412
1413 // check if we need to fail at this point
1414 if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
1415 return;
1416
1417 string const FinalRelease = MetaIndex->GetFinalFilename();
1418 string const FinalReleasegpg = GetFinalFilename();
1419 string const FinalInRelease = TransactionManager->GetFinalFilename();
1420
1421 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1422 {
1423 std::string downgrade_msg;
1424 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1425 MetaIndex->Target.Description.c_str());
1426 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1427 {
1428 // meh, the users wants to take risks (we still mark the packages
1429 // from this repository as unauthenticated)
1430 _error->Warning("%s", downgrade_msg.c_str());
1431 _error->Warning(_("This is normally not allowed, but the option "
1432 "Acquire::AllowDowngradeToInsecureRepositories was "
1433 "given to override it."));
1434 Status = StatDone;
1435 } else {
1436 _error->Error("%s", downgrade_msg.c_str());
1437 if (TransactionManager->IMSHit == false)
1438 Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
1439 Item::Failed("Message: " + downgrade_msg, Cnf);
1440 TransactionManager->AbortTransaction();
1441 return;
1442 }
1443 }
1444 else
1445 _error->Warning(_("The data from '%s' is not signed. Packages "
1446 "from that repository can not be authenticated."),
1447 MetaIndex->Target.Description.c_str());
1448
1449 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1450 TransactionManager->TransactionStageRemoval(this, DestFile);
1451
1452 // only allow going further if the users explicitely wants it
1453 if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1454 {
1455 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1456 {
1457 // open the last Release if we have it
1458 if (TransactionManager->IMSHit == false)
1459 {
1460 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
1461 if (TransactionManager->LastMetaIndexParser != NULL)
1462 {
1463 _error->PushToStack();
1464 if (RealFileExists(FinalInRelease))
1465 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
1466 else
1467 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
1468 // its unlikely to happen, but if what we have is bad ignore it
1469 if (_error->PendingError())
1470 {
1471 delete TransactionManager->LastMetaIndexParser;
1472 TransactionManager->LastMetaIndexParser = NULL;
1473 }
1474 _error->RevertToStack();
1475 }
1476 }
1477 }
1478
1479 // we parse the indexes here because at this point the user wanted
1480 // a repository that may potentially harm him
1481 if (TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText) == false || MetaIndex->VerifyVendor(Message) == false)
1482 /* expired Release files are still a problem you need extra force for */;
1483 else
1484 MetaIndex->QueueIndexes(true);
1485
1486 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1487 }
1488
1489 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1490 if (Cnf->LocalOnly == true ||
1491 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1492 {
1493 // Ignore this
1494 Status = StatDone;
1495 }
1496 }
1497 /*}}}*/
1498
1499
1500 // AcqBaseIndex - Constructor /*{{{*/
1501 pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
1502 pkgAcqMetaClearSig * const TransactionManager,
1503 IndexTarget const &Target)
1504 : pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
1505 {
1506 }
1507 /*}}}*/
1508 pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
1509
1510 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1511 // ---------------------------------------------------------------------
1512 /* Get the DiffIndex file first and see if there are patches available
1513 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1514 * patches. If anything goes wrong in that process, it will fall back to
1515 * the original packages file
1516 */
1517 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
1518 pkgAcqMetaClearSig * const TransactionManager,
1519 IndexTarget const &Target)
1520 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), diffs(NULL)
1521 {
1522 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1523
1524 Desc.Owner = this;
1525 Desc.Description = Target.Description + ".diff/Index";
1526 Desc.ShortDesc = Target.ShortDesc;
1527 Desc.URI = Target.URI + ".diff/Index";
1528
1529 DestFile = GetPartialFileNameFromURI(Desc.URI);
1530
1531 if(Debug)
1532 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
1533
1534 QueueURI(Desc);
1535 }
1536 /*}}}*/
1537 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1538 // ---------------------------------------------------------------------
1539 /* The only header we use is the last-modified header. */
1540 string pkgAcqDiffIndex::Custom600Headers() const
1541 {
1542 string const Final = GetFinalFilename();
1543
1544 if(Debug)
1545 std::clog << "Custom600Header-IMS: " << Final << std::endl;
1546
1547 struct stat Buf;
1548 if (stat(Final.c_str(),&Buf) != 0)
1549 return "\nIndex-File: true";
1550
1551 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1552 }
1553 /*}}}*/
1554 void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
1555 {
1556 // list cleanup needs to know that this file as well as the already
1557 // present index is ours, so we create an empty diff to save it for us
1558 new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
1559 }
1560 /*}}}*/
1561 bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
1562 {
1563 // failing here is fine: our caller will take care of trying to
1564 // get the complete file if patching fails
1565 if(Debug)
1566 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
1567 << std::endl;
1568
1569 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
1570 pkgTagFile TF(&Fd);
1571 if (_error->PendingError() == true)
1572 return false;
1573
1574 pkgTagSection Tags;
1575 if(unlikely(TF.Step(Tags) == false))
1576 return false;
1577
1578 HashStringList ServerHashes;
1579 unsigned long long ServerSize = 0;
1580
1581 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1582 {
1583 std::string tagname = *type;
1584 tagname.append("-Current");
1585 std::string const tmp = Tags.FindS(tagname.c_str());
1586 if (tmp.empty() == true)
1587 continue;
1588
1589 string hash;
1590 unsigned long long size;
1591 std::stringstream ss(tmp);
1592 ss >> hash >> size;
1593 if (unlikely(hash.empty() == true))
1594 continue;
1595 if (unlikely(ServerSize != 0 && ServerSize != size))
1596 continue;
1597 ServerHashes.push_back(HashString(*type, hash));
1598 ServerSize = size;
1599 }
1600
1601 if (ServerHashes.usable() == false)
1602 {
1603 if (Debug == true)
1604 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
1605 return false;
1606 }
1607
1608 std::string const CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
1609 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
1610 if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
1611 {
1612 if (Debug == true)
1613 {
1614 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
1615 printHashSumComparision(CurrentPackagesFile, ServerHashes, TargetFileHashes);
1616 }
1617 return false;
1618 }
1619
1620 HashStringList LocalHashes;
1621 // try avoiding calculating the hash here as this is costly
1622 if (TransactionManager->LastMetaIndexParser != NULL)
1623 LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
1624 if (LocalHashes.usable() == false)
1625 {
1626 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
1627 Hashes LocalHashesCalc(ServerHashes);
1628 LocalHashesCalc.AddFD(fd);
1629 LocalHashes = LocalHashesCalc.GetHashStringList();
1630 }
1631
1632 if (ServerHashes == LocalHashes)
1633 {
1634 // we have the same sha1 as the server so we are done here
1635 if(Debug)
1636 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
1637 QueueOnIMSHit();
1638 return true;
1639 }
1640
1641 if(Debug)
1642 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
1643 << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
1644
1645 // parse all of (provided) history
1646 vector<DiffInfo> available_patches;
1647 bool firstAcceptedHashes = true;
1648 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1649 {
1650 if (LocalHashes.find(*type) == NULL)
1651 continue;
1652
1653 std::string tagname = *type;
1654 tagname.append("-History");
1655 std::string const tmp = Tags.FindS(tagname.c_str());
1656 if (tmp.empty() == true)
1657 continue;
1658
1659 string hash, filename;
1660 unsigned long long size;
1661 std::stringstream ss(tmp);
1662
1663 while (ss >> hash >> size >> filename)
1664 {
1665 if (unlikely(hash.empty() == true || filename.empty() == true))
1666 continue;
1667
1668 // see if we have a record for this file already
1669 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1670 for (; cur != available_patches.end(); ++cur)
1671 {
1672 if (cur->file != filename)
1673 continue;
1674 cur->result_hashes.push_back(HashString(*type, hash));
1675 break;
1676 }
1677 if (cur != available_patches.end())
1678 continue;
1679 if (firstAcceptedHashes == true)
1680 {
1681 DiffInfo next;
1682 next.file = filename;
1683 next.result_hashes.push_back(HashString(*type, hash));
1684 next.result_hashes.FileSize(size);
1685 available_patches.push_back(next);
1686 }
1687 else
1688 {
1689 if (Debug == true)
1690 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1691 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
1692 break;
1693 }
1694 }
1695 firstAcceptedHashes = false;
1696 }
1697
1698 if (unlikely(available_patches.empty() == true))
1699 {
1700 if (Debug)
1701 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1702 << "Couldn't find any patches for the patch series." << std::endl;
1703 return false;
1704 }
1705
1706 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1707 {
1708 if (LocalHashes.find(*type) == NULL)
1709 continue;
1710
1711 std::string tagname = *type;
1712 tagname.append("-Patches");
1713 std::string const tmp = Tags.FindS(tagname.c_str());
1714 if (tmp.empty() == true)
1715 continue;
1716
1717 string hash, filename;
1718 unsigned long long size;
1719 std::stringstream ss(tmp);
1720
1721 while (ss >> hash >> size >> filename)
1722 {
1723 if (unlikely(hash.empty() == true || filename.empty() == true))
1724 continue;
1725
1726 // see if we have a record for this file already
1727 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1728 for (; cur != available_patches.end(); ++cur)
1729 {
1730 if (cur->file != filename)
1731 continue;
1732 if (cur->patch_hashes.empty())
1733 cur->patch_hashes.FileSize(size);
1734 cur->patch_hashes.push_back(HashString(*type, hash));
1735 break;
1736 }
1737 if (cur != available_patches.end())
1738 continue;
1739 if (Debug == true)
1740 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1741 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
1742 break;
1743 }
1744 }
1745
1746 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1747 {
1748 std::string tagname = *type;
1749 tagname.append("-Download");
1750 std::string const tmp = Tags.FindS(tagname.c_str());
1751 if (tmp.empty() == true)
1752 continue;
1753
1754 string hash, filename;
1755 unsigned long long size;
1756 std::stringstream ss(tmp);
1757
1758 // FIXME: all of pdiff supports only .gz compressed patches
1759 while (ss >> hash >> size >> filename)
1760 {
1761 if (unlikely(hash.empty() == true || filename.empty() == true))
1762 continue;
1763 if (unlikely(APT::String::Endswith(filename, ".gz") == false))
1764 continue;
1765 filename.erase(filename.length() - 3);
1766
1767 // see if we have a record for this file already
1768 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1769 for (; cur != available_patches.end(); ++cur)
1770 {
1771 if (cur->file != filename)
1772 continue;
1773 if (cur->download_hashes.empty())
1774 cur->download_hashes.FileSize(size);
1775 cur->download_hashes.push_back(HashString(*type, hash));
1776 break;
1777 }
1778 if (cur != available_patches.end())
1779 continue;
1780 if (Debug == true)
1781 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1782 << " wasn't in the list for the first parsed hash! (download)" << std::endl;
1783 break;
1784 }
1785 }
1786
1787
1788 bool foundStart = false;
1789 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
1790 cur != available_patches.end(); ++cur)
1791 {
1792 if (LocalHashes != cur->result_hashes)
1793 continue;
1794
1795 available_patches.erase(available_patches.begin(), cur);
1796 foundStart = true;
1797 break;
1798 }
1799
1800 if (foundStart == false || unlikely(available_patches.empty() == true))
1801 {
1802 if (Debug)
1803 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1804 << "Couldn't find the start of the patch series." << std::endl;
1805 return false;
1806 }
1807
1808 // patching with too many files is rather slow compared to a fast download
1809 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
1810 if (fileLimit != 0 && fileLimit < available_patches.size())
1811 {
1812 if (Debug)
1813 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
1814 << ") so fallback to complete download" << std::endl;
1815 return false;
1816 }
1817
1818 // calculate the size of all patches we have to get
1819 // note that all sizes are uncompressed, while we download compressed files
1820 unsigned long long patchesSize = 0;
1821 for (std::vector<DiffInfo>::const_iterator cur = available_patches.begin();
1822 cur != available_patches.end(); ++cur)
1823 patchesSize += cur->patch_hashes.FileSize();
1824 unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
1825 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
1826 {
1827 if (Debug)
1828 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
1829 << ") so fallback to complete download" << std::endl;
1830 return false;
1831 }
1832
1833 // we have something, queue the diffs
1834 string::size_type const last_space = Description.rfind(" ");
1835 if(last_space != string::npos)
1836 Description.erase(last_space, Description.size()-last_space);
1837
1838 /* decide if we should download patches one by one or in one go:
1839 The first is good if the server merges patches, but many don't so client
1840 based merging can be attempt in which case the second is better.
1841 "bad things" will happen if patches are merged on the server,
1842 but client side merging is attempt as well */
1843 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
1844 if (pdiff_merge == true)
1845 {
1846 // reprepro adds this flag if it has merged patches on the server
1847 std::string const precedence = Tags.FindS("X-Patch-Precedence");
1848 pdiff_merge = (precedence != "merged");
1849 }
1850
1851 if (pdiff_merge == false)
1852 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
1853 else
1854 {
1855 diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
1856 for(size_t i = 0; i < available_patches.size(); ++i)
1857 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
1858 Target,
1859 available_patches[i],
1860 diffs);
1861 }
1862
1863 Complete = false;
1864 Status = StatDone;
1865 Dequeue();
1866 return true;
1867 }
1868 /*}}}*/
1869 void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1870 {
1871 Item::Failed(Message,Cnf);
1872 Status = StatDone;
1873
1874 if(Debug)
1875 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
1876 << "Falling back to normal index file acquire" << std::endl;
1877
1878 new pkgAcqIndex(Owner, TransactionManager, Target);
1879 }
1880 /*}}}*/
1881 void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
1882 pkgAcquire::MethodConfig const * const Cnf)
1883 {
1884 if(Debug)
1885 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
1886
1887 Item::Done(Message, Hashes, Cnf);
1888
1889 string const FinalFile = GetFinalFilename();
1890 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
1891 DestFile = FinalFile;
1892
1893 if(ParseDiffIndex(DestFile) == false)
1894 {
1895 Failed("Message: Couldn't parse pdiff index", Cnf);
1896 // queue for final move - this should happen even if we fail
1897 // while parsing (e.g. on sizelimit) and download the complete file.
1898 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1899 return;
1900 }
1901
1902 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1903
1904 Complete = true;
1905 Status = StatDone;
1906 Dequeue();
1907
1908 return;
1909 }
1910 /*}}}*/
1911 pkgAcqDiffIndex::~pkgAcqDiffIndex()
1912 {
1913 if (diffs != NULL)
1914 delete diffs;
1915 }
1916
1917 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
1918 // ---------------------------------------------------------------------
1919 /* The package diff is added to the queue. one object is constructed
1920 * for each diff and the index
1921 */
1922 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
1923 pkgAcqMetaClearSig * const TransactionManager,
1924 IndexTarget const &Target,
1925 vector<DiffInfo> const &diffs)
1926 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
1927 available_patches(diffs)
1928 {
1929 DestFile = GetPartialFileNameFromURI(Target.URI);
1930
1931 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1932
1933 Desc.Owner = this;
1934 Description = Target.Description;
1935 Desc.ShortDesc = Target.ShortDesc;
1936
1937 if(available_patches.empty() == true)
1938 {
1939 // we are done (yeah!), check hashes against the final file
1940 DestFile = GetFinalFileNameFromURI(Target.URI);
1941 Finish(true);
1942 }
1943 else
1944 {
1945 // patching needs to be bootstrapped with the 'old' version
1946 std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
1947 if (RealFileExists(PartialFile) == false)
1948 {
1949 if (symlink(GetFinalFilename().c_str(), PartialFile.c_str()) != 0)
1950 {
1951 Failed("Link creation of " + PartialFile + " to " + GetFinalFilename() + " failed", NULL);
1952 return;
1953 }
1954 }
1955
1956 // get the next diff
1957 State = StateFetchDiff;
1958 QueueNextDiff();
1959 }
1960 }
1961 /*}}}*/
1962 void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1963 {
1964 Item::Failed(Message,Cnf);
1965 Status = StatDone;
1966
1967 if(Debug)
1968 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
1969 << "Falling back to normal index file acquire" << std::endl;
1970 DestFile = GetPartialFileNameFromURI(Target.URI);
1971 RenameOnError(PDiffError);
1972 std::string const patchname = GetDiffsPatchFileName(DestFile);
1973 if (RealFileExists(patchname))
1974 rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
1975 new pkgAcqIndex(Owner, TransactionManager, Target);
1976 Finish();
1977 }
1978 /*}}}*/
1979 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
1980 void pkgAcqIndexDiffs::Finish(bool allDone)
1981 {
1982 if(Debug)
1983 std::clog << "pkgAcqIndexDiffs::Finish(): "
1984 << allDone << " "
1985 << Desc.URI << std::endl;
1986
1987 // we restore the original name, this is required, otherwise
1988 // the file will be cleaned
1989 if(allDone)
1990 {
1991 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1992
1993 // this is for the "real" finish
1994 Complete = true;
1995 Status = StatDone;
1996 Dequeue();
1997 if(Debug)
1998 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
1999 return;
2000 }
2001
2002 if(Debug)
2003 std::clog << "Finishing: " << Desc.URI << std::endl;
2004 Complete = false;
2005 Status = StatDone;
2006 Dequeue();
2007 return;
2008 }
2009 /*}}}*/
2010 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
2011 {
2012 // calc sha1 of the just patched file
2013 std::string const FinalFile = GetPartialFileNameFromURI(Target.URI);
2014
2015 if(!FileExists(FinalFile))
2016 {
2017 Failed("Message: No FinalFile " + FinalFile + " available", NULL);
2018 return false;
2019 }
2020
2021 FileFd fd(FinalFile, FileFd::ReadOnly);
2022 Hashes LocalHashesCalc;
2023 LocalHashesCalc.AddFD(fd);
2024 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
2025
2026 if(Debug)
2027 std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
2028
2029 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
2030 if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
2031 {
2032 Failed("Local/Expected hashes are not usable", NULL);
2033 return false;
2034 }
2035
2036
2037 // final file reached before all patches are applied
2038 if(LocalHashes == TargetFileHashes)
2039 {
2040 Finish(true);
2041 return true;
2042 }
2043
2044 // remove all patches until the next matching patch is found
2045 // this requires the Index file to be ordered
2046 for(vector<DiffInfo>::iterator I = available_patches.begin();
2047 available_patches.empty() == false &&
2048 I != available_patches.end() &&
2049 I->result_hashes != LocalHashes;
2050 ++I)
2051 {
2052 available_patches.erase(I);
2053 }
2054
2055 // error checking and falling back if no patch was found
2056 if(available_patches.empty() == true)
2057 {
2058 Failed("No patches left to reach target", NULL);
2059 return false;
2060 }
2061
2062 // queue the right diff
2063 Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
2064 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
2065 DestFile = GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file);
2066
2067 if(Debug)
2068 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
2069
2070 QueueURI(Desc);
2071
2072 return true;
2073 }
2074 /*}}}*/
2075 void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2076 pkgAcquire::MethodConfig const * const Cnf)
2077 {
2078 if(Debug)
2079 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
2080
2081 Item::Done(Message, Hashes, Cnf);
2082
2083 std::string const FinalFile = GetPartialFileNameFromURI(Target.URI);
2084 std::string const PatchFile = GetDiffsPatchFileName(FinalFile);
2085
2086 // success in downloading a diff, enter ApplyDiff state
2087 if(State == StateFetchDiff)
2088 {
2089 Rename(DestFile, PatchFile);
2090
2091 if(Debug)
2092 std::clog << "Sending to rred method: " << FinalFile << std::endl;
2093
2094 State = StateApplyDiff;
2095 Local = true;
2096 Desc.URI = "rred:" + FinalFile;
2097 QueueURI(Desc);
2098 SetActiveSubprocess("rred");
2099 return;
2100 }
2101
2102 // success in download/apply a diff, queue next (if needed)
2103 if(State == StateApplyDiff)
2104 {
2105 // remove the just applied patch
2106 available_patches.erase(available_patches.begin());
2107 unlink(PatchFile.c_str());
2108
2109 // move into place
2110 if(Debug)
2111 {
2112 std::clog << "Moving patched file in place: " << std::endl
2113 << DestFile << " -> " << FinalFile << std::endl;
2114 }
2115 Rename(DestFile,FinalFile);
2116 chmod(FinalFile.c_str(),0644);
2117
2118 // see if there is more to download
2119 if(available_patches.empty() == false) {
2120 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
2121 available_patches);
2122 return Finish();
2123 } else
2124 // update
2125 DestFile = FinalFile;
2126 return Finish(true);
2127 }
2128 }
2129 /*}}}*/
2130 std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2131 {
2132 if(State != StateApplyDiff)
2133 return pkgAcqBaseIndex::Custom600Headers();
2134 std::ostringstream patchhashes;
2135 HashStringList const ExpectedHashes = available_patches[0].patch_hashes;
2136 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2137 patchhashes << "\nPatch-0-" << hs->HashType() << "-Hash: " << hs->HashValue();
2138 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2139 return patchhashes.str();
2140 }
2141 /*}}}*/
2142 pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
2143
2144 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2145 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
2146 pkgAcqMetaClearSig * const TransactionManager,
2147 IndexTarget const &Target,
2148 DiffInfo const &patch,
2149 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
2150 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2151 patch(patch), allPatches(allPatches), State(StateFetchDiff)
2152 {
2153 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2154
2155 Desc.Owner = this;
2156 Description = Target.Description;
2157 Desc.ShortDesc = Target.ShortDesc;
2158
2159 Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
2160 Desc.Description = Description + " " + patch.file + string(".pdiff");
2161
2162 DestFile = GetPartialFileNameFromURI(Target.URI + ".diff/" + patch.file);
2163
2164 if(Debug)
2165 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
2166
2167 QueueURI(Desc);
2168 }
2169 /*}}}*/
2170 void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2171 {
2172 if(Debug)
2173 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
2174
2175 Item::Failed(Message,Cnf);
2176 Status = StatDone;
2177
2178 // check if we are the first to fail, otherwise we are done here
2179 State = StateDoneDiff;
2180 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2181 I != allPatches->end(); ++I)
2182 if ((*I)->State == StateErrorDiff)
2183 return;
2184
2185 // first failure means we should fallback
2186 State = StateErrorDiff;
2187 if (Debug)
2188 std::clog << "Falling back to normal index file acquire" << std::endl;
2189 DestFile = GetPartialFileNameFromURI(Target.URI);
2190 RenameOnError(PDiffError);
2191 std::string const patchname = GetMergeDiffsPatchFileName(DestFile, patch.file);
2192 if (RealFileExists(patchname))
2193 rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
2194 new pkgAcqIndex(Owner, TransactionManager, Target);
2195 }
2196 /*}}}*/
2197 void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2198 pkgAcquire::MethodConfig const * const Cnf)
2199 {
2200 if(Debug)
2201 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
2202
2203 Item::Done(Message, Hashes, Cnf);
2204
2205 string const FinalFile = GetPartialFileNameFromURI(Target.URI);
2206 if (State == StateFetchDiff)
2207 {
2208 Rename(DestFile, GetMergeDiffsPatchFileName(FinalFile, patch.file));
2209
2210 // check if this is the last completed diff
2211 State = StateDoneDiff;
2212 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2213 I != allPatches->end(); ++I)
2214 if ((*I)->State != StateDoneDiff)
2215 {
2216 if(Debug)
2217 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
2218 return;
2219 }
2220
2221 // this is the last completed diff, so we are ready to apply now
2222 State = StateApplyDiff;
2223
2224 // patching needs to be bootstrapped with the 'old' version
2225 if (symlink(GetFinalFilename().c_str(), FinalFile.c_str()) != 0)
2226 {
2227 Failed("Link creation of " + FinalFile + " to " + GetFinalFilename() + " failed", NULL);
2228 return;
2229 }
2230
2231 if(Debug)
2232 std::clog << "Sending to rred method: " << FinalFile << std::endl;
2233
2234 Local = true;
2235 Desc.URI = "rred:" + FinalFile;
2236 QueueURI(Desc);
2237 SetActiveSubprocess("rred");
2238 return;
2239 }
2240 // success in download/apply all diffs, clean up
2241 else if (State == StateApplyDiff)
2242 {
2243 // move the result into place
2244 std::string const Final = GetFinalFilename();
2245 if(Debug)
2246 std::clog << "Queue patched file in place: " << std::endl
2247 << DestFile << " -> " << Final << std::endl;
2248
2249 // queue for copy by the transaction manager
2250 TransactionManager->TransactionStageCopy(this, DestFile, Final);
2251
2252 // ensure the ed's are gone regardless of list-cleanup
2253 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2254 I != allPatches->end(); ++I)
2255 {
2256 std::string const PartialFile = GetPartialFileNameFromURI(Target.URI);
2257 std::string const patch = GetMergeDiffsPatchFileName(PartialFile, (*I)->patch.file);
2258 unlink(patch.c_str());
2259 }
2260 unlink(FinalFile.c_str());
2261
2262 // all set and done
2263 Complete = true;
2264 if(Debug)
2265 std::clog << "allDone: " << DestFile << "\n" << std::endl;
2266 }
2267 }
2268 /*}}}*/
2269 std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2270 {
2271 if(State != StateApplyDiff)
2272 return pkgAcqBaseIndex::Custom600Headers();
2273 std::ostringstream patchhashes;
2274 unsigned int seen_patches = 0;
2275 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2276 I != allPatches->end(); ++I)
2277 {
2278 HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
2279 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2280 patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
2281 ++seen_patches;
2282 }
2283 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2284 return patchhashes.str();
2285 }
2286 /*}}}*/
2287 pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
2288
2289 // AcqIndex::AcqIndex - Constructor /*{{{*/
2290 pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
2291 pkgAcqMetaClearSig * const TransactionManager,
2292 IndexTarget const &Target)
2293 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD)
2294 {
2295 // autoselect the compression method
2296 AutoSelectCompression();
2297 Init(Target.URI, Target.Description, Target.ShortDesc);
2298
2299 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2300 std::clog << "New pkgIndex with TransactionManager "
2301 << TransactionManager << std::endl;
2302 }
2303 /*}}}*/
2304 // AcqIndex::AutoSelectCompression - Select compression /*{{{*/
2305 void pkgAcqIndex::AutoSelectCompression()
2306 {
2307 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
2308 CompressionExtensions = "";
2309 if (TransactionManager->MetaIndexParser != NULL && TransactionManager->MetaIndexParser->Exists(Target.MetaKey))
2310 {
2311 for (std::vector<std::string>::const_iterator t = types.begin();
2312 t != types.end(); ++t)
2313 {
2314 std::string CompressedMetaKey = string(Target.MetaKey).append(".").append(*t);
2315 if (*t == "uncompressed" ||
2316 TransactionManager->MetaIndexParser->Exists(CompressedMetaKey) == true)
2317 CompressionExtensions.append(*t).append(" ");
2318 }
2319 }
2320 else
2321 {
2322 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
2323 CompressionExtensions.append(*t).append(" ");
2324 }
2325 if (CompressionExtensions.empty() == false)
2326 CompressionExtensions.erase(CompressionExtensions.end()-1);
2327 }
2328 /*}}}*/
2329 // AcqIndex::Init - defered Constructor /*{{{*/
2330 void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
2331 string const &ShortDesc)
2332 {
2333 Stage = STAGE_DOWNLOAD;
2334
2335 DestFile = GetPartialFileNameFromURI(URI);
2336
2337 size_t const nextExt = CompressionExtensions.find(' ');
2338 if (nextExt == std::string::npos)
2339 {
2340 CurrentCompressionExtension = CompressionExtensions;
2341 CompressionExtensions.clear();
2342 }
2343 else
2344 {
2345 CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
2346 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
2347 }
2348
2349 if (CurrentCompressionExtension == "uncompressed")
2350 {
2351 Desc.URI = URI;
2352 }
2353 else if (unlikely(CurrentCompressionExtension.empty()))
2354 return;
2355 else
2356 {
2357 Desc.URI = URI + '.' + CurrentCompressionExtension;
2358 DestFile = DestFile + '.' + CurrentCompressionExtension;
2359 }
2360
2361 if(TransactionManager->MetaIndexParser != NULL)
2362 InitByHashIfNeeded();
2363
2364 Desc.Description = URIDesc;
2365 Desc.Owner = this;
2366 Desc.ShortDesc = ShortDesc;
2367
2368 QueueURI(Desc);
2369 }
2370 /*}}}*/
2371 // AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
2372 void pkgAcqIndex::InitByHashIfNeeded()
2373 {
2374 // TODO:
2375 // - (maybe?) add support for by-hash into the sources.list as flag
2376 // - make apt-ftparchive generate the hashes (and expire?)
2377 std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
2378 if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
2379 _config->FindB(HostKnob, false) == true ||
2380 TransactionManager->MetaIndexParser->GetSupportsAcquireByHash())
2381 {
2382 HashStringList const Hashes = GetExpectedHashes();
2383 if(Hashes.usable())
2384 {
2385 // FIXME: should we really use the best hash here? or a fixed one?
2386 HashString const * const TargetHash = Hashes.find("");
2387 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2388 size_t const trailing_slash = Desc.URI.find_last_of("/");
2389 Desc.URI = Desc.URI.replace(
2390 trailing_slash,
2391 Desc.URI.substr(trailing_slash+1).size()+1,
2392 ByHash);
2393 } else {
2394 _error->Warning(
2395 "Fetching ByHash requested but can not find record for %s",
2396 GetMetaKey().c_str());
2397 }
2398 }
2399 }
2400 /*}}}*/
2401 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2402 // ---------------------------------------------------------------------
2403 /* The only header we use is the last-modified header. */
2404 string pkgAcqIndex::Custom600Headers() const
2405 {
2406 string Final = GetFinalFilename();
2407
2408 string msg = "\nIndex-File: true";
2409 struct stat Buf;
2410 if (stat(Final.c_str(),&Buf) == 0)
2411 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2412
2413 if(Target.IsOptional)
2414 msg += "\nFail-Ignore: true";
2415
2416 return msg;
2417 }
2418 /*}}}*/
2419 // AcqIndex::Failed - getting the indexfile failed /*{{{*/
2420 void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2421 {
2422 Item::Failed(Message,Cnf);
2423
2424 // authorisation matches will not be fixed by other compression types
2425 if (Status != StatAuthError)
2426 {
2427 if (CompressionExtensions.empty() == false)
2428 {
2429 Init(Target.URI, Desc.Description, Desc.ShortDesc);
2430 Status = StatIdle;
2431 return;
2432 }
2433 }
2434
2435 if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
2436 Status = StatDone;
2437 else
2438 TransactionManager->AbortTransaction();
2439 }
2440 /*}}}*/
2441 // AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
2442 void pkgAcqIndex::ReverifyAfterIMS()
2443 {
2444 // update destfile to *not* include the compression extension when doing
2445 // a reverify (as its uncompressed on disk already)
2446 DestFile = GetCompressedFileName(Target.URI, GetPartialFileNameFromURI(Target.URI), CurrentCompressionExtension);
2447
2448 // copy FinalFile into partial/ so that we check the hash again
2449 string FinalFile = GetFinalFilename();
2450 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2451 Desc.URI = "copy:" + FinalFile;
2452 QueueURI(Desc);
2453 }
2454 /*}}}*/
2455 // AcqIndex::Done - Finished a fetch /*{{{*/
2456 // ---------------------------------------------------------------------
2457 /* This goes through a number of states.. On the initial fetch the
2458 method could possibly return an alternate filename which points
2459 to the uncompressed version of the file. If this is so the file
2460 is copied into the partial directory. In all other cases the file
2461 is decompressed with a compressed uri. */
2462 void pkgAcqIndex::Done(string const &Message,
2463 HashStringList const &Hashes,
2464 pkgAcquire::MethodConfig const * const Cfg)
2465 {
2466 Item::Done(Message,Hashes,Cfg);
2467
2468 switch(Stage)
2469 {
2470 case STAGE_DOWNLOAD:
2471 StageDownloadDone(Message, Hashes, Cfg);
2472 break;
2473 case STAGE_DECOMPRESS_AND_VERIFY:
2474 StageDecompressDone(Message, Hashes, Cfg);
2475 break;
2476 }
2477 }
2478 /*}}}*/
2479 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2480 void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const &,
2481 pkgAcquire::MethodConfig const * const)
2482 {
2483 Complete = true;
2484
2485 // Handle the unzipd case
2486 string FileName = LookupTag(Message,"Alt-Filename");
2487 if (FileName.empty() == false)
2488 {
2489 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2490 Local = true;
2491 DestFile += ".decomp";
2492 Desc.URI = "copy:" + FileName;
2493 QueueURI(Desc);
2494 SetActiveSubprocess("copy");
2495 return;
2496 }
2497
2498 FileName = LookupTag(Message,"Filename");
2499 if (FileName.empty() == true)
2500 {
2501 Status = StatError;
2502 ErrorText = "Method gave a blank filename";
2503 }
2504
2505 // Methods like e.g. "file:" will give us a (compressed) FileName that is
2506 // not the "DestFile" we set, in this case we uncompress from the local file
2507 if (FileName != DestFile && RealFileExists(DestFile) == false)
2508 Local = true;
2509 else
2510 EraseFileName = FileName;
2511
2512 // we need to verify the file against the current Release file again
2513 // on if-modfied-since hit to avoid a stale attack against us
2514 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2515 {
2516 // The files timestamp matches, reverify by copy into partial/
2517 EraseFileName = "";
2518 ReverifyAfterIMS();
2519 return;
2520 }
2521
2522 // If we have compressed indexes enabled, queue for hash verification
2523 if (_config->FindB("Acquire::GzipIndexes",false))
2524 {
2525 DestFile = GetPartialFileNameFromURI(Target.URI + '.' + CurrentCompressionExtension);
2526 EraseFileName = "";
2527 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2528 Desc.URI = "copy:" + FileName;
2529 QueueURI(Desc);
2530 SetActiveSubprocess("copy");
2531 return;
2532 }
2533
2534 // get the binary name for your used compression type
2535 string decompProg;
2536 if(CurrentCompressionExtension == "uncompressed")
2537 decompProg = "copy";
2538 else
2539 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
2540 if(decompProg.empty() == true)
2541 {
2542 _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
2543 return;
2544 }
2545
2546 // queue uri for the next stage
2547 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2548 DestFile += ".decomp";
2549 Desc.URI = decompProg + ":" + FileName;
2550 QueueURI(Desc);
2551 SetActiveSubprocess(decompProg);
2552 }
2553 /*}}}*/
2554 // AcqIndex::StageDecompressDone - Final verification /*{{{*/
2555 void pkgAcqIndex::StageDecompressDone(string const &,
2556 HashStringList const &,
2557 pkgAcquire::MethodConfig const * const)
2558 {
2559 // Done, queue for rename on transaction finished
2560 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
2561 return;
2562 }
2563 /*}}}*/
2564 pkgAcqIndex::~pkgAcqIndex() {}
2565
2566
2567 // AcqArchive::AcqArchive - Constructor /*{{{*/
2568 // ---------------------------------------------------------------------
2569 /* This just sets up the initial fetch environment and queues the first
2570 possibilitiy */
2571 pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
2572 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
2573 string &StoreFilename) :
2574 Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
2575 StoreFilename(StoreFilename), Vf(Version.FileList()),
2576 Trusted(false)
2577 {
2578 Retries = _config->FindI("Acquire::Retries",0);
2579
2580 if (Version.Arch() == 0)
2581 {
2582 _error->Error(_("I wasn't able to locate a file for the %s package. "
2583 "This might mean you need to manually fix this package. "
2584 "(due to missing arch)"),
2585 Version.ParentPkg().FullName().c_str());
2586 return;
2587 }
2588
2589 /* We need to find a filename to determine the extension. We make the
2590 assumption here that all the available sources for this version share
2591 the same extension.. */
2592 // Skip not source sources, they do not have file fields.
2593 for (; Vf.end() == false; ++Vf)
2594 {
2595 if (Vf.File().Flagged(pkgCache::Flag::NotSource))
2596 continue;
2597 break;
2598 }
2599
2600 // Does not really matter here.. we are going to fail out below
2601 if (Vf.end() != true)
2602 {
2603 // If this fails to get a file name we will bomb out below.
2604 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2605 if (_error->PendingError() == true)
2606 return;
2607
2608 // Generate the final file name as: package_version_arch.foo
2609 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2610 QuoteString(Version.VerStr(),"_:") + '_' +
2611 QuoteString(Version.Arch(),"_:.") +
2612 "." + flExtension(Parse.FileName());
2613 }
2614
2615 // check if we have one trusted source for the package. if so, switch
2616 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2617 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2618 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2619 bool seenUntrusted = false;
2620 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2621 {
2622 pkgIndexFile *Index;
2623 if (Sources->FindIndex(i.File(),Index) == false)
2624 continue;
2625
2626 if (debugAuth == true)
2627 std::cerr << "Checking index: " << Index->Describe()
2628 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2629
2630 if (Index->IsTrusted() == true)
2631 {
2632 Trusted = true;
2633 if (allowUnauth == false)
2634 break;
2635 }
2636 else
2637 seenUntrusted = true;
2638 }
2639
2640 // "allow-unauthenticated" restores apts old fetching behaviour
2641 // that means that e.g. unauthenticated file:// uris are higher
2642 // priority than authenticated http:// uris
2643 if (allowUnauth == true && seenUntrusted == true)
2644 Trusted = false;
2645
2646 // Select a source
2647 if (QueueNext() == false && _error->PendingError() == false)
2648 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2649 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2650 }
2651 /*}}}*/
2652 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2653 // ---------------------------------------------------------------------
2654 /* This queues the next available file version for download. It checks if
2655 the archive is already available in the cache and stashs the MD5 for
2656 checking later. */
2657 bool pkgAcqArchive::QueueNext()
2658 {
2659 for (; Vf.end() == false; ++Vf)
2660 {
2661 pkgCache::PkgFileIterator const PkgF = Vf.File();
2662 // Ignore not source sources
2663 if (PkgF.Flagged(pkgCache::Flag::NotSource))
2664 continue;
2665
2666 // Try to cross match against the source list
2667 pkgIndexFile *Index;
2668 if (Sources->FindIndex(PkgF, Index) == false)
2669 continue;
2670 LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
2671
2672 // only try to get a trusted package from another source if that source
2673 // is also trusted
2674 if(Trusted && !Index->IsTrusted())
2675 continue;
2676
2677 // Grab the text package record
2678 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2679 if (_error->PendingError() == true)
2680 return false;
2681
2682 string PkgFile = Parse.FileName();
2683 ExpectedHashes = Parse.Hashes();
2684
2685 if (PkgFile.empty() == true)
2686 return _error->Error(_("The package index files are corrupted. No Filename: "
2687 "field for package %s."),
2688 Version.ParentPkg().Name());
2689
2690 Desc.URI = Index->ArchiveURI(PkgFile);
2691 Desc.Description = Index->ArchiveInfo(Version);
2692 Desc.Owner = this;
2693 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2694
2695 // See if we already have the file. (Legacy filenames)
2696 FileSize = Version->Size;
2697 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2698 struct stat Buf;
2699 if (stat(FinalFile.c_str(),&Buf) == 0)
2700 {
2701 // Make sure the size matches
2702 if ((unsigned long long)Buf.st_size == Version->Size)
2703 {
2704 Complete = true;
2705 Local = true;
2706 Status = StatDone;
2707 StoreFilename = DestFile = FinalFile;
2708 return true;
2709 }
2710
2711 /* Hmm, we have a file and its size does not match, this means it is
2712 an old style mismatched arch */
2713 unlink(FinalFile.c_str());
2714 }
2715
2716 // Check it again using the new style output filenames
2717 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2718 if (stat(FinalFile.c_str(),&Buf) == 0)
2719 {
2720 // Make sure the size matches
2721 if ((unsigned long long)Buf.st_size == Version->Size)
2722 {
2723 Complete = true;
2724 Local = true;
2725 Status = StatDone;
2726 StoreFilename = DestFile = FinalFile;
2727 return true;
2728 }
2729
2730 /* Hmm, we have a file and its size does not match, this shouldn't
2731 happen.. */
2732 unlink(FinalFile.c_str());
2733 }
2734
2735 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2736
2737 // Check the destination file
2738 if (stat(DestFile.c_str(),&Buf) == 0)
2739 {
2740 // Hmm, the partial file is too big, erase it
2741 if ((unsigned long long)Buf.st_size > Version->Size)
2742 unlink(DestFile.c_str());
2743 else
2744 PartialSize = Buf.st_size;
2745 }
2746
2747 // Disables download of archives - useful if no real installation follows,
2748 // e.g. if we are just interested in proposed installation order
2749 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2750 {
2751 Complete = true;
2752 Local = true;
2753 Status = StatDone;
2754 StoreFilename = DestFile = FinalFile;
2755 return true;
2756 }
2757
2758 // Create the item
2759 Local = false;
2760 QueueURI(Desc);
2761
2762 ++Vf;
2763 return true;
2764 }
2765 return false;
2766 }
2767 /*}}}*/
2768 // AcqArchive::Done - Finished fetching /*{{{*/
2769 // ---------------------------------------------------------------------
2770 /* */
2771 void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
2772 pkgAcquire::MethodConfig const * const Cfg)
2773 {
2774 Item::Done(Message, Hashes, Cfg);
2775
2776 // Grab the output filename
2777 string FileName = LookupTag(Message,"Filename");
2778 if (FileName.empty() == true)
2779 {
2780 Status = StatError;
2781 ErrorText = "Method gave a blank filename";
2782 return;
2783 }
2784
2785 // Reference filename
2786 if (DestFile != FileName && RealFileExists(DestFile) == false)
2787 {
2788 StoreFilename = DestFile = FileName;
2789 Local = true;
2790 Complete = true;
2791 return;
2792 }
2793
2794 // Done, move it into position
2795 string const FinalFile = GetFinalFilename();
2796 Rename(DestFile,FinalFile);
2797 StoreFilename = DestFile = FinalFile;
2798 Complete = true;
2799 }
2800 /*}}}*/
2801 // AcqArchive::Failed - Failure handler /*{{{*/
2802 // ---------------------------------------------------------------------
2803 /* Here we try other sources */
2804 void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2805 {
2806 Item::Failed(Message,Cnf);
2807
2808 /* We don't really want to retry on failed media swaps, this prevents
2809 that. An interesting observation is that permanent failures are not
2810 recorded. */
2811 if (Cnf->Removable == true &&
2812 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2813 {
2814 // Vf = Version.FileList();
2815 while (Vf.end() == false) ++Vf;
2816 StoreFilename = string();
2817 return;
2818 }
2819
2820 Status = StatIdle;
2821 if (QueueNext() == false)
2822 {
2823 // This is the retry counter
2824 if (Retries != 0 &&
2825 Cnf->LocalOnly == false &&
2826 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2827 {
2828 Retries--;
2829 Vf = Version.FileList();
2830 if (QueueNext() == true)
2831 return;
2832 }
2833
2834 StoreFilename = string();
2835 Status = StatError;
2836 }
2837 }
2838 /*}}}*/
2839 APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
2840 {
2841 return Trusted;
2842 }
2843 /*}}}*/
2844 void pkgAcqArchive::Finished() /*{{{*/
2845 {
2846 if (Status == pkgAcquire::Item::StatDone &&
2847 Complete == true)
2848 return;
2849 StoreFilename = string();
2850 }
2851 /*}}}*/
2852 std::string pkgAcqArchive::DescURI() const /*{{{*/
2853 {
2854 return Desc.URI;
2855 }
2856 /*}}}*/
2857 std::string pkgAcqArchive::ShortDesc() const /*{{{*/
2858 {
2859 return Desc.ShortDesc;
2860 }
2861 /*}}}*/
2862 pkgAcqArchive::~pkgAcqArchive() {}
2863
2864 // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
2865 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
2866 std::string const &DestDir, std::string const &DestFilename) :
2867 pkgAcquire::Item(Owner), d(NULL), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
2868 {
2869 Desc.URI = URI(Ver);
2870 Init(DestDir, DestFilename);
2871 }
2872 // some parameters are char* here as they come likely from char* interfaces – which can also return NULL
2873 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
2874 char const * const Component, char const * const SrcName, char const * const SrcVersion,
2875 const string &DestDir, const string &DestFilename) :
2876 pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion)
2877 {
2878 Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
2879 Init(DestDir, DestFilename);
2880 }
2881 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
2882 std::string const &URI, char const * const SrcName, char const * const SrcVersion,
2883 const string &DestDir, const string &DestFilename) :
2884 pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion)
2885 {
2886 Desc.URI = URI;
2887 Init(DestDir, DestFilename);
2888 }
2889 void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
2890 {
2891 if (Desc.URI.empty())
2892 {
2893 Status = StatError;
2894 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
2895 strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
2896 // Let the error message print something sensible rather than "Failed to fetch /"
2897 if (DestFilename.empty())
2898 DestFile = SrcName + ".changelog";
2899 else
2900 DestFile = DestFilename;
2901 Desc.URI = "changelog:/" + DestFile;
2902 return;
2903 }
2904
2905 if (DestDir.empty())
2906 {
2907 std::string const systemTemp = GetTempDir();
2908 char tmpname[100];
2909 snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
2910 if (NULL == mkdtemp(tmpname))
2911 {
2912 _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
2913 Status = StatError;
2914 return;
2915 }
2916 DestFile = TemporaryDirectory = tmpname;
2917 }
2918 else
2919 DestFile = DestDir;
2920
2921 if (DestFilename.empty())
2922 DestFile = flCombine(DestFile, SrcName + ".changelog");
2923 else
2924 DestFile = flCombine(DestFile, DestFilename);
2925
2926 Desc.ShortDesc = "Changelog";
2927 strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
2928 Desc.Owner = this;
2929 QueueURI(Desc);
2930 }
2931 /*}}}*/
2932 std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
2933 {
2934 char const * const SrcName = Ver.SourcePkgName();
2935 char const * const SrcVersion = Ver.SourceVerStr();
2936 pkgCache::PkgFileIterator PkgFile;
2937 // find the first source for this version which promises a changelog
2938 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
2939 {
2940 pkgCache::PkgFileIterator const PF = VF.File();
2941 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
2942 continue;
2943 PkgFile = PF;
2944 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
2945 std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
2946 if (uri.empty())
2947 continue;
2948 return uri;
2949 }
2950 return "";
2951 }
2952 std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
2953 {
2954 if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
2955 return "";
2956 std::string const serverConfig = "Acquire::Changelogs::URI";
2957 std::string server;
2958 #define APT_EMPTY_SERVER \
2959 if (server.empty() == false) \
2960 { \
2961 if (server != "no") \
2962 return server; \
2963 return ""; \
2964 }
2965 #define APT_CHECK_SERVER(X, Y) \
2966 if (Rls->X != 0) \
2967 { \
2968 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
2969 server = _config->Find(specialServerConfig); \
2970 APT_EMPTY_SERVER \
2971 }
2972 // this way e.g. Debian-Security can fallback to Debian
2973 APT_CHECK_SERVER(Label, "Override::")
2974 APT_CHECK_SERVER(Origin, "Override::")
2975
2976 if (RealFileExists(Rls.FileName()))
2977 {
2978 _error->PushToStack();
2979 FileFd rf;
2980 /* This can be costly. A caller wanting to get millions of URIs might
2981 want to do this on its own once and use Override settings.
2982 We don't do this here as Origin/Label are not as unique as they
2983 should be so this could produce request order-dependent anomalies */
2984 if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
2985 {
2986 pkgTagFile TagFile(&rf, rf.Size());
2987 pkgTagSection Section;
2988 if (TagFile.Step(Section) == true)
2989 server = Section.FindS("Changelogs");
2990 }
2991 _error->RevertToStack();
2992 APT_EMPTY_SERVER
2993 }
2994
2995 APT_CHECK_SERVER(Label, "")
2996 APT_CHECK_SERVER(Origin, "")
2997 #undef APT_CHECK_SERVER
2998 #undef APT_EMPTY_SERVER
2999 return "";
3000 }
3001 std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
3002 char const * const Component, char const * const SrcName,
3003 char const * const SrcVersion)
3004 {
3005 return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
3006 }
3007 std::string pkgAcqChangelog::URI(std::string const &Template,
3008 char const * const Component, char const * const SrcName,
3009 char const * const SrcVersion)
3010 {
3011 if (Template.find("CHANGEPATH") == std::string::npos)
3012 return "";
3013
3014 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3015 std::string Src = SrcName;
3016 std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
3017 path.append("/").append(Src).append("/");
3018 path.append(Src).append("_").append(StripEpoch(SrcVersion));
3019 // we omit component for releases without one (= flat-style repositories)
3020 if (Component != NULL && strlen(Component) != 0)
3021 path = std::string(Component) + "/" + path;
3022
3023 return SubstVar(Template, "CHANGEPATH", path);
3024 }
3025 /*}}}*/
3026 // AcqChangelog::Failed - Failure handler /*{{{*/
3027 void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3028 {
3029 Item::Failed(Message,Cnf);
3030
3031 std::string errText;
3032 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3033 strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3034
3035 // Error is probably something techy like 404 Not Found
3036 if (ErrorText.empty())
3037 ErrorText = errText;
3038 else
3039 ErrorText = errText + " (" + ErrorText + ")";
3040 return;
3041 }
3042 /*}}}*/
3043 // AcqChangelog::Done - Item downloaded OK /*{{{*/
3044 void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
3045 pkgAcquire::MethodConfig const * const Cnf)
3046 {
3047 Item::Done(Message,CalcHashes,Cnf);
3048
3049 Complete = true;
3050 }
3051 /*}}}*/
3052 pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3053 {
3054 if (TemporaryDirectory.empty() == false)
3055 {
3056 unlink(DestFile.c_str());
3057 rmdir(TemporaryDirectory.c_str());
3058 }
3059 }
3060 /*}}}*/
3061
3062 // AcqFile::pkgAcqFile - Constructor /*{{{*/
3063 pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
3064 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
3065 const string &DestDir, const string &DestFilename,
3066 bool const IsIndexFile) :
3067 Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
3068 {
3069 Retries = _config->FindI("Acquire::Retries",0);
3070
3071 if(!DestFilename.empty())
3072 DestFile = DestFilename;
3073 else if(!DestDir.empty())
3074 DestFile = DestDir + "/" + flNotDir(URI);
3075 else
3076 DestFile = flNotDir(URI);
3077
3078 // Create the item
3079 Desc.URI = URI;
3080 Desc.Description = Dsc;
3081 Desc.Owner = this;
3082
3083 // Set the short description to the archive component
3084 Desc.ShortDesc = ShortDesc;
3085
3086 // Get the transfer sizes
3087 FileSize = Size;
3088 struct stat Buf;
3089 if (stat(DestFile.c_str(),&Buf) == 0)
3090 {
3091 // Hmm, the partial file is too big, erase it
3092 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
3093 unlink(DestFile.c_str());
3094 else
3095 PartialSize = Buf.st_size;
3096 }
3097
3098 QueueURI(Desc);
3099 }
3100 /*}}}*/
3101 // AcqFile::Done - Item downloaded OK /*{{{*/
3102 void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
3103 pkgAcquire::MethodConfig const * const Cnf)
3104 {
3105 Item::Done(Message,CalcHashes,Cnf);
3106
3107 string FileName = LookupTag(Message,"Filename");
3108 if (FileName.empty() == true)
3109 {
3110 Status = StatError;
3111 ErrorText = "Method gave a blank filename";
3112 return;
3113 }
3114
3115 Complete = true;
3116
3117 // The files timestamp matches
3118 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
3119 return;
3120
3121 // We have to copy it into place
3122 if (RealFileExists(DestFile.c_str()) == false)
3123 {
3124 Local = true;
3125 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
3126 Cnf->Removable == true)
3127 {
3128 Desc.URI = "copy:" + FileName;
3129 QueueURI(Desc);
3130 return;
3131 }
3132
3133 // Erase the file if it is a symlink so we can overwrite it
3134 struct stat St;
3135 if (lstat(DestFile.c_str(),&St) == 0)
3136 {
3137 if (S_ISLNK(St.st_mode) != 0)
3138 unlink(DestFile.c_str());
3139 }
3140
3141 // Symlink the file
3142 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
3143 {
3144 _error->PushToStack();
3145 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
3146 std::stringstream msg;
3147 _error->DumpErrors(msg);
3148 _error->RevertToStack();
3149 ErrorText = msg.str();
3150 Status = StatError;
3151 Complete = false;
3152 }
3153 }
3154 }
3155 /*}}}*/
3156 // AcqFile::Failed - Failure handler /*{{{*/
3157 // ---------------------------------------------------------------------
3158 /* Here we try other sources */
3159 void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3160 {
3161 Item::Failed(Message,Cnf);
3162
3163 // This is the retry counter
3164 if (Retries != 0 &&
3165 Cnf->LocalOnly == false &&
3166 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3167 {
3168 --Retries;
3169 QueueURI(Desc);
3170 Status = StatIdle;
3171 return;
3172 }
3173
3174 }
3175 /*}}}*/
3176 string pkgAcqFile::Custom600Headers() const /*{{{*/
3177 {
3178 if (IsIndexFile)
3179 return "\nIndex-File: true";
3180 return "";
3181 }
3182 /*}}}*/
3183 pkgAcqFile::~pkgAcqFile() {}