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