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