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