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