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