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