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