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