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