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