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