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