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