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