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