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