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