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