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