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