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