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