]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
support hashes for compressed pdiff files
[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 {
928 if (verify == true && TransactionManager->MetaIndexParser->Exists((*Target)->MetaKey) == false)
2ac3eeb6 929 {
448c38bd
DK
930 // optional target that we do not have in the Release file are skipped
931 if ((*Target)->IsOptional())
f6d4ab9a 932 continue;
47d2bc78 933
448c38bd
DK
934 Status = StatAuthError;
935 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
936 return;
2237bd01 937 }
448c38bd
DK
938
939 /* Queue the Index file (Packages, Sources, Translation-$foo
940 (either diff or full packages files, depending
941 on the users option) - we also check if the PDiff Index file is listed
942 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
943 instead, but passing the required info to it is to much hassle */
944 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
945 TransactionManager->MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
946 new pkgAcqDiffIndex(Owner, TransactionManager, *Target);
947 else
948 new pkgAcqIndex(Owner, TransactionManager, *Target);
2237bd01 949 }
448c38bd
DK
950}
951 /*}}}*/
952bool pkgAcqMetaBase::VerifyVendor(string const &Message) /*{{{*/
953{
954 string::size_type pos;
f6d4ab9a 955
448c38bd
DK
956 // check for missing sigs (that where not fatal because otherwise we had
957 // bombed earlier)
958 string missingkeys;
959 string msg = _("There is no public key available for the "
960 "following key IDs:\n");
961 pos = Message.find("NO_PUBKEY ");
962 if (pos != std::string::npos)
f6d4ab9a 963 {
448c38bd
DK
964 string::size_type start = pos+strlen("NO_PUBKEY ");
965 string Fingerprint = Message.substr(start, Message.find("\n")-start);
966 missingkeys += (Fingerprint);
f6d4ab9a 967 }
448c38bd
DK
968 if(!missingkeys.empty())
969 _error->Warning("%s", (msg + missingkeys).c_str());
f6d4ab9a 970
448c38bd
DK
971 string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
972
973 if (Transformed == "../project/experimental")
f6d4ab9a 974 {
448c38bd 975 Transformed = "experimental";
f6d4ab9a
DK
976 }
977
448c38bd
DK
978 pos = Transformed.rfind('/');
979 if (pos != string::npos)
f6d4ab9a 980 {
448c38bd 981 Transformed = Transformed.substr(0, pos);
f6d4ab9a
DK
982 }
983
448c38bd 984 if (Transformed == ".")
f6d4ab9a 985 {
448c38bd 986 Transformed = "";
f6d4ab9a
DK
987 }
988
448c38bd
DK
989 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
990 TransactionManager->MetaIndexParser->GetValidUntil() > 0) {
991 time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
992 if (invalid_since > 0)
993 {
994 std::string errmsg;
995 strprintf(errmsg,
996 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
997 // the time since then the file is invalid - formated in the same way as in
998 // the download progress display (e.g. 7d 3h 42min 1s)
999 _("Release file for %s is expired (invalid since %s). "
1000 "Updates for this repository will not be applied."),
1001 DataTarget.URI.c_str(), TimeToStr(invalid_since).c_str());
1002 if (ErrorText.empty())
1003 ErrorText = errmsg;
1004 return _error->Error("%s", errmsg.c_str());
1005 }
1006 }
f6d4ab9a 1007
448c38bd
DK
1008 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1009 as a prevention of downgrading us to older (still valid) files */
1010 if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
1011 TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
f6d4ab9a 1012 {
448c38bd
DK
1013 TransactionManager->IMSHit = true;
1014 unlink(DestFile.c_str());
1015 PartialFile = DestFile = GetFinalFilename();
1016 delete TransactionManager->MetaIndexParser;
1017 TransactionManager->MetaIndexParser = TransactionManager->LastMetaIndexParser;
1018 TransactionManager->LastMetaIndexParser = NULL;
f6d4ab9a
DK
1019 }
1020
448c38bd 1021 if (_config->FindB("Debug::pkgAcquire::Auth", false))
f6d4ab9a 1022 {
448c38bd
DK
1023 std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetDist() << std::endl;
1024 std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
1025 std::cerr << "Transformed Dist: " << Transformed << std::endl;
f6d4ab9a 1026 }
448c38bd
DK
1027
1028 if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
f6d4ab9a 1029 {
448c38bd
DK
1030 // This might become fatal one day
1031// Status = StatAuthError;
1032// ErrorText = "Conflicting distribution; expected "
1033// + MetaIndexParser->GetExpectedDist() + " but got "
1034// + MetaIndexParser->GetDist();
1035// return false;
1036 if (!Transformed.empty())
1037 {
1038 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1039 Desc.Description.c_str(),
1040 Transformed.c_str(),
1041 TransactionManager->MetaIndexParser->GetDist().c_str());
1042 }
f6d4ab9a
DK
1043 }
1044
f6d4ab9a 1045 return true;
2237bd01 1046}
92fcbfc1 1047 /*}}}*/
2237bd01 1048
448c38bd
DK
1049pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
1050 IndexTarget const &ClearsignedTarget,
1051 IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
1052 const vector<IndexTarget*>* const IndexTargets,
1053 indexRecords * const MetaIndexParser) :
1054 pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget, IndexTargets, MetaIndexParser),
1055 ClearsignedTarget(ClearsignedTarget),
1056 DetachedDataTarget(DetachedDataTarget), DetachedSigTarget(DetachedSigTarget)
1057{
1058 // index targets + (worst case:) Release/Release.gpg
1059 ExpectedAdditionalItems = IndexTargets->size() + 2;
1060 TransactionManager->Add(this);
2237bd01 1061}
92fcbfc1 1062 /*}}}*/
448c38bd 1063pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
146f7715 1064{
146f7715
DK
1065}
1066 /*}}}*/
448c38bd
DK
1067// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1068string pkgAcqMetaClearSig::Custom600Headers() const
2237bd01 1069{
448c38bd
DK
1070 string Header = pkgAcqMetaBase::Custom600Headers();
1071 Header += "\nFail-Ignore: true";
1072 return Header;
1073}
1074 /*}}}*/
1075// pkgAcqMetaClearSig::Done - We got a file /*{{{*/
1076class APT_HIDDEN DummyItem : public pkgAcquire::Item
1077{
1078 IndexTarget const * const Target;
1079 public:
1080 virtual std::string DescURI() const {return Target->URI;};
1081 virtual HashStringList GetExpectedHashes() const {return HashStringList();};
2237bd01 1082
448c38bd
DK
1083 DummyItem(pkgAcquire * const Owner, IndexTarget const * const Target) :
1084 pkgAcquire::Item(Owner), Target(Target)
8d266656 1085 {
448c38bd
DK
1086 Status = StatDone;
1087 DestFile = GetFinalFileNameFromURI(Target->URI);
8d266656 1088 }
448c38bd
DK
1089};
1090void pkgAcqMetaClearSig::Done(std::string const &Message,
1091 HashStringList const &Hashes,
1092 pkgAcquire::MethodConfig const * const Cnf)
1093{
1094 Item::Done(Message, Hashes, Cnf);
8d266656 1095
448c38bd
DK
1096 // if we expect a ClearTextSignature (InRelease), ensure that
1097 // this is what we get and if not fail to queue a
1098 // Release/Release.gpg, see #346386
1099 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
146f7715 1100 {
448c38bd
DK
1101 pkgAcquire::Item::Failed(Message, Cnf);
1102 RenameOnError(NotClearsigned);
1103 TransactionManager->AbortTransaction();
146f7715
DK
1104 return;
1105 }
22b2ef9d 1106
448c38bd
DK
1107 if(AuthPass == false)
1108 {
1109 if(CheckDownloadDone(this, Message, Hashes) == true)
1110 QueueForSignatureVerify(this, DestFile, DestFile);
1111 return;
1112 }
1113 else if(CheckAuthDone(Message) == true)
1114 {
1115 if (TransactionManager->IMSHit == false)
1116 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1117 else if (RealFileExists(GetFinalFilename()) == false)
1118 {
1119 // We got an InRelease file IMSHit, but we haven't one, which means
1120 // we had a valid Release/Release.gpg combo stepping in, which we have
1121 // to 'acquire' now to ensure list cleanup isn't removing them
1122 new DummyItem(Owner, &DetachedDataTarget);
1123 new DummyItem(Owner, &DetachedSigTarget);
1124 }
1125 }
2237bd01 1126}
92fcbfc1 1127 /*}}}*/
448c38bd 1128void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
2237bd01 1129{
448c38bd 1130 Item::Failed(Message, Cnf);
2237bd01 1131
448c38bd
DK
1132 // we failed, we will not get additional items from this method
1133 ExpectedAdditionalItems = 0;
2237bd01 1134
448c38bd 1135 if (AuthPass == false)
2ac3eeb6 1136 {
448c38bd
DK
1137 // Queue the 'old' InRelease file for removal if we try Release.gpg
1138 // as otherwise the file will stay around and gives a false-auth
1139 // impression (CVE-2012-0214)
1140 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1141 Status = StatDone;
1142
1143 new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget, IndexTargets, TransactionManager->MetaIndexParser);
2ac3eeb6
MV
1144 }
1145 else
1146 {
448c38bd
DK
1147 if(CheckStopAuthentication(this, Message))
1148 return;
1149
1150 _error->Warning(_("The data from '%s' is not signed. Packages "
1151 "from that repository can not be authenticated."),
1152 ClearsignedTarget.Description.c_str());
1153
1154 // No Release file was present, or verification failed, so fall
1155 // back to queueing Packages files without verification
1156 // only allow going further if the users explicitely wants it
1157 if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
146f7715 1158 {
448c38bd
DK
1159 Status = StatDone;
1160
1161 /* InRelease files become Release files, otherwise
1162 * they would be considered as trusted later on */
1163 string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1164 string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
1165 string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
1166 string const FinalInRelease = GetFinalFilename();
1167 Rename(DestFile, PartialRelease);
1168 TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
1169
1170 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
146f7715 1171 {
448c38bd
DK
1172 // open the last Release if we have it
1173 if (TransactionManager->IMSHit == false)
1174 {
1175 TransactionManager->LastMetaIndexParser = new indexRecords;
1176 _error->PushToStack();
1177 if (RealFileExists(FinalInRelease))
1178 TransactionManager->LastMetaIndexParser->Load(FinalInRelease);
1179 else
1180 TransactionManager->LastMetaIndexParser->Load(FinalRelease);
1181 // its unlikely to happen, but if what we have is bad ignore it
1182 if (_error->PendingError())
1183 {
1184 delete TransactionManager->LastMetaIndexParser;
1185 TransactionManager->LastMetaIndexParser = NULL;
1186 }
1187 _error->RevertToStack();
1188 }
146f7715 1189 }
146f7715 1190
448c38bd
DK
1191 // we parse the indexes here because at this point the user wanted
1192 // a repository that may potentially harm him
1193 if (TransactionManager->MetaIndexParser->Load(PartialRelease) == false || VerifyVendor(Message) == false)
1194 /* expired Release files are still a problem you need extra force for */;
1195 else
1196 QueueIndexes(true);
1197 }
2237bd01
MV
1198 }
1199}
92fcbfc1 1200 /*}}}*/
03aa0847 1201
448c38bd
DK
1202pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
1203 pkgAcqMetaBase * const TransactionManager,
1204 IndexTarget const &DataTarget,
1205 IndexTarget const &DetachedSigTarget,
1206 vector<IndexTarget*> const * const IndexTargets,
1207 indexRecords * const MetaIndexParser) :
1208 pkgAcqMetaBase(Owner, TransactionManager, IndexTargets, DataTarget, MetaIndexParser),
1209 DetachedSigTarget(DetachedSigTarget)
ac5b205a 1210{
448c38bd
DK
1211 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1212 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1213 << this->TransactionManager << std::endl;
2d4722e2 1214
448c38bd 1215 DestFile = GetPartialFileNameFromURI(DataTarget.URI);
fa3a96a1 1216
448c38bd
DK
1217 // Create the item
1218 Desc.Description = DataTarget.Description;
1219 Desc.Owner = this;
1220 Desc.ShortDesc = DataTarget.ShortDesc;
1221 Desc.URI = DataTarget.URI;
ac5b205a 1222
448c38bd
DK
1223 // we expect more item
1224 ExpectedAdditionalItems = IndexTargets->size();
1225 QueueURI(Desc);
ac5b205a 1226}
92fcbfc1 1227 /*}}}*/
448c38bd
DK
1228void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
1229 HashStringList const &Hashes,
1230 pkgAcquire::MethodConfig const * const Cfg)
ac5b205a 1231{
448c38bd 1232 Item::Done(Message,Hashes,Cfg);
03bfbc96 1233
448c38bd 1234 if(CheckDownloadDone(this, Message, Hashes))
03bfbc96 1235 {
448c38bd
DK
1236 // we have a Release file, now download the Signature, all further
1237 // verify/queue for additional downloads will be done in the
1238 // pkgAcqMetaSig::Done() code
1239 new pkgAcqMetaSig(Owner, TransactionManager, &DetachedSigTarget, this);
03bfbc96 1240 }
448c38bd
DK
1241}
1242 /*}}}*/
1243// pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1244void pkgAcqMetaIndex::Failed(string const &Message,
1245 pkgAcquire::MethodConfig const * const Cnf)
1246{
1247 pkgAcquire::Item::Failed(Message, Cnf);
1248 Status = StatDone;
94dc9d7d 1249
448c38bd
DK
1250 _error->Warning(_("The repository '%s' does not have a Release file. "
1251 "This is deprecated, please contact the owner of the "
1252 "repository."), DataTarget.Description.c_str());
f6d4ab9a 1253
448c38bd
DK
1254 // No Release file was present so fall
1255 // back to queueing Packages files without verification
1256 // only allow going further if the users explicitely wants it
1257 if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
f6d4ab9a 1258 {
448c38bd
DK
1259 // ensure old Release files are removed
1260 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1261 delete TransactionManager->MetaIndexParser;
1262 TransactionManager->MetaIndexParser = NULL;
03bfbc96 1263
448c38bd
DK
1264 // queue without any kind of hashsum support
1265 QueueIndexes(false);
59a704f0 1266 }
448c38bd
DK
1267}
1268 /*}}}*/
1269void pkgAcqMetaIndex::Finished() /*{{{*/
1270{
1271 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1272 std::clog << "Finished: " << DestFile <<std::endl;
1273 if(TransactionManager != NULL &&
1274 TransactionManager->TransactionHasError() == false)
1275 TransactionManager->CommitTransaction();
1276}
1277 /*}}}*/
1278std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
1279{
1280 return DataTarget.URI;
1281}
1282 /*}}}*/
94dc9d7d 1283
448c38bd
DK
1284// AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1285pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
1286 pkgAcqMetaBase * const TransactionManager,
1287 IndexTarget const * const Target,
1288 pkgAcqMetaIndex * const MetaIndex) :
1289 pkgAcqTransactionItem(Owner, TransactionManager, Target), MetaIndex(MetaIndex)
1290{
1291 DestFile = GetPartialFileNameFromURI(Target->URI);
6cb30d01 1292
448c38bd
DK
1293 // remove any partial downloaded sig-file in partial/.
1294 // it may confuse proxies and is too small to warrant a
1295 // partial download anyway
1296 unlink(DestFile.c_str());
ac5b205a 1297
448c38bd
DK
1298 // set the TransactionManager
1299 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1300 std::clog << "New pkgAcqMetaSig with TransactionManager "
1301 << TransactionManager << std::endl;
f6d4ab9a 1302
448c38bd
DK
1303 // Create the item
1304 Desc.Description = Target->Description;
1305 Desc.Owner = this;
1306 Desc.ShortDesc = Target->ShortDesc;
1307 Desc.URI = Target->URI;
ac5b205a 1308
448c38bd
DK
1309 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1310 // so we skip the download step and go instantly to verification
1311 if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
1312 {
1313 Complete = true;
1314 Status = StatDone;
1315 PartialFile = DestFile = GetFinalFilename();
1316 MetaIndexFileSignature = DestFile;
1317 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1318 }
1319 else
1320 QueueURI(Desc);
ac5b205a 1321}
92fcbfc1 1322 /*}}}*/
448c38bd 1323pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
ac5b205a 1324{
448c38bd
DK
1325}
1326 /*}}}*/
1327// AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1328void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
1329 pkgAcquire::MethodConfig const * const Cfg)
1330{
1331 if (MetaIndexFileSignature.empty() == false)
4a0a786f 1332 {
448c38bd
DK
1333 DestFile = MetaIndexFileSignature;
1334 MetaIndexFileSignature.clear();
1335 }
1336 Item::Done(Message, Hashes, Cfg);
f6d4ab9a 1337
448c38bd
DK
1338 if(MetaIndex->AuthPass == false)
1339 {
1340 if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
f6d4ab9a 1341 {
448c38bd
DK
1342 // destfile will be modified to point to MetaIndexFile for the
1343 // gpgv method, so we need to save it here
1344 MetaIndexFileSignature = DestFile;
1345 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1346 }
1347 return;
1348 }
1349 else if(MetaIndex->CheckAuthDone(Message) == true)
1350 {
1351 if (TransactionManager->IMSHit == false)
1352 {
1353 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1354 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
f6d4ab9a 1355 }
448c38bd
DK
1356 }
1357}
1358 /*}}}*/
1359void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1360{
1361 Item::Failed(Message,Cnf);
4a0a786f 1362
448c38bd
DK
1363 // check if we need to fail at this point
1364 if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
1365 return;
4a0a786f 1366
448c38bd
DK
1367 string const FinalRelease = MetaIndex->GetFinalFilename();
1368 string const FinalReleasegpg = GetFinalFilename();
1369 string const FinalInRelease = TransactionManager->GetFinalFilename();
4a0a786f 1370
448c38bd
DK
1371 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1372 {
1373 std::string downgrade_msg;
1374 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1375 MetaIndex->DataTarget.Description.c_str());
1376 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1377 {
1378 // meh, the users wants to take risks (we still mark the packages
1379 // from this repository as unauthenticated)
1380 _error->Warning("%s", downgrade_msg.c_str());
1381 _error->Warning(_("This is normally not allowed, but the option "
1382 "Acquire::AllowDowngradeToInsecureRepositories was "
1383 "given to override it."));
1384 Status = StatDone;
1385 } else {
1386 _error->Error("%s", downgrade_msg.c_str());
1387 if (TransactionManager->IMSHit == false)
1388 Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
1389 Item::Failed("Message: " + downgrade_msg, Cnf);
1390 TransactionManager->AbortTransaction();
1391 return;
1392 }
1393 }
1394 else
1395 _error->Warning(_("The data from '%s' is not signed. Packages "
1396 "from that repository can not be authenticated."),
1397 MetaIndex->DataTarget.Description.c_str());
4a0a786f 1398
448c38bd
DK
1399 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1400 TransactionManager->TransactionStageRemoval(this, DestFile);
4a0a786f 1401
448c38bd
DK
1402 // only allow going further if the users explicitely wants it
1403 if(AllowInsecureRepositories(TransactionManager->MetaIndexParser, TransactionManager, this) == true)
4a0a786f 1404 {
448c38bd 1405 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
59a704f0 1406 {
448c38bd
DK
1407 // open the last Release if we have it
1408 if (TransactionManager->IMSHit == false)
1409 {
1410 TransactionManager->LastMetaIndexParser = new indexRecords;
1411 _error->PushToStack();
1412 if (RealFileExists(FinalInRelease))
1413 TransactionManager->LastMetaIndexParser->Load(FinalInRelease);
1414 else
1415 TransactionManager->LastMetaIndexParser->Load(FinalRelease);
1416 // its unlikely to happen, but if what we have is bad ignore it
1417 if (_error->PendingError())
1418 {
1419 delete TransactionManager->LastMetaIndexParser;
1420 TransactionManager->LastMetaIndexParser = NULL;
1421 }
1422 _error->RevertToStack();
1423 }
59a704f0 1424 }
4a0a786f 1425
448c38bd
DK
1426 // we parse the indexes here because at this point the user wanted
1427 // a repository that may potentially harm him
1428 if (TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile) == false || MetaIndex->VerifyVendor(Message) == false)
1429 /* expired Release files are still a problem you need extra force for */;
1430 else
1431 MetaIndex->QueueIndexes(true);
1432
1433 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1434 }
1435
1436 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1437 if (Cnf->LocalOnly == true ||
1438 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1439 {
1440 // Ignore this
1441 Status = StatDone;
ac5b205a 1442 }
ac5b205a 1443}
92fcbfc1 1444 /*}}}*/
448c38bd
DK
1445
1446
1447// AcqBaseIndex - Constructor /*{{{*/
1448pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
1449 pkgAcqMetaBase * const TransactionManager,
1450 IndexTarget const * const Target)
1451: pkgAcqTransactionItem(Owner, TransactionManager, Target)
1452{
1453}
1454 /*}}}*/
1455
1456// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1457// ---------------------------------------------------------------------
1458/* Get the DiffIndex file first and see if there are patches available
1459 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1460 * patches. If anything goes wrong in that process, it will fall back to
1461 * the original packages file
1462 */
1463pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
1464 pkgAcqMetaBase * const TransactionManager,
1465 IndexTarget const * const Target)
1466 : pkgAcqBaseIndex(Owner, TransactionManager, Target)
47d2bc78 1467{
47d2bc78
DK
1468 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1469
47d2bc78 1470 Desc.Owner = this;
448c38bd 1471 Desc.Description = Target->Description + ".diff/Index";
c2184314 1472 Desc.ShortDesc = Target->ShortDesc;
448c38bd 1473 Desc.URI = Target->URI + ".diff/Index";
47d2bc78 1474
448c38bd
DK
1475 DestFile = GetPartialFileNameFromURI(Desc.URI);
1476
1477 if(Debug)
1478 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
5684f71f 1479
448c38bd
DK
1480 // look for the current package file
1481 CurrentPackagesFile = GetFinalFileNameFromURI(Target->URI);
1482
1483 // FIXME: this file:/ check is a hack to prevent fetching
1484 // from local sources. this is really silly, and
1485 // should be fixed cleanly as soon as possible
1486 if(!FileExists(CurrentPackagesFile) ||
1487 Desc.URI.substr(0,strlen("file:/")) == "file:/")
1488 {
1489 // we don't have a pkg file or we don't want to queue
1490 Failed("No index file, local or canceld by user", NULL);
1491 return;
1492 }
47d2bc78
DK
1493
1494 if(Debug)
448c38bd
DK
1495 std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): "
1496 << CurrentPackagesFile << std::endl;
47d2bc78
DK
1497
1498 QueueURI(Desc);
448c38bd 1499
47d2bc78
DK
1500}
1501 /*}}}*/
448c38bd
DK
1502// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1503// ---------------------------------------------------------------------
1504/* The only header we use is the last-modified header. */
1505string pkgAcqDiffIndex::Custom600Headers() const
47d2bc78 1506{
448c38bd 1507 string const Final = GetFinalFilename();
47d2bc78 1508
448c38bd
DK
1509 if(Debug)
1510 std::clog << "Custom600Header-IMS: " << Final << std::endl;
47d2bc78 1511
448c38bd
DK
1512 struct stat Buf;
1513 if (stat(Final.c_str(),&Buf) != 0)
1514 return "\nIndex-File: true";
1515
1516 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1517}
1518 /*}}}*/
1519void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
1520{
1521 // list cleanup needs to know that this file as well as the already
1522 // present index is ours, so we create an empty diff to save it for us
1523 new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
47d2bc78
DK
1524}
1525 /*}}}*/
448c38bd 1526bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
47d2bc78 1527{
448c38bd
DK
1528 // failing here is fine: our caller will take care of trying to
1529 // get the complete file if patching fails
47d2bc78 1530 if(Debug)
448c38bd
DK
1531 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
1532 << std::endl;
f6d4ab9a 1533
448c38bd
DK
1534 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
1535 pkgTagFile TF(&Fd);
1536 if (_error->PendingError() == true)
1537 return false;
47d2bc78 1538
448c38bd
DK
1539 pkgTagSection Tags;
1540 if(unlikely(TF.Step(Tags) == false))
1541 return false;
47d2bc78 1542
448c38bd
DK
1543 HashStringList ServerHashes;
1544 unsigned long long ServerSize = 0;
47d2bc78 1545
448c38bd
DK
1546 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1547 {
1548 std::string tagname = *type;
1549 tagname.append("-Current");
1550 std::string const tmp = Tags.FindS(tagname.c_str());
1551 if (tmp.empty() == true)
1552 continue;
146f7715 1553
448c38bd
DK
1554 string hash;
1555 unsigned long long size;
1556 std::stringstream ss(tmp);
1557 ss >> hash >> size;
1558 if (unlikely(hash.empty() == true))
1559 continue;
1560 if (unlikely(ServerSize != 0 && ServerSize != size))
1561 continue;
1562 ServerHashes.push_back(HashString(*type, hash));
1563 ServerSize = size;
1564 }
47d2bc78 1565
448c38bd
DK
1566 if (ServerHashes.usable() == false)
1567 {
1568 if (Debug == true)
1569 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
1570 return false;
47d2bc78 1571 }
448c38bd
DK
1572
1573 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target->MetaKey);
1574 if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
47d2bc78 1575 {
448c38bd 1576 if (Debug == true)
47d2bc78 1577 {
448c38bd
DK
1578 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
1579 printHashSumComparision(CurrentPackagesFile, ServerHashes, TargetFileHashes);
47d2bc78 1580 }
448c38bd
DK
1581 return false;
1582 }
47d2bc78 1583
448c38bd
DK
1584 if (ServerHashes.VerifyFile(CurrentPackagesFile) == true)
1585 {
1586 // we have the same sha1 as the server so we are done here
47d2bc78 1587 if(Debug)
448c38bd
DK
1588 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
1589 QueueOnIMSHit();
1590 return true;
1591 }
47d2bc78 1592
448c38bd
DK
1593 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
1594 Hashes LocalHashesCalc;
1595 LocalHashesCalc.AddFD(fd);
1596 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
47d2bc78 1597
448c38bd
DK
1598 if(Debug)
1599 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
1600 << fd.Name() << " " << fd.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
34d6ece7 1601
448c38bd
DK
1602 // parse all of (provided) history
1603 vector<DiffInfo> available_patches;
1604 bool firstAcceptedHashes = true;
1605 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
651bddad 1606 {
448c38bd
DK
1607 if (LocalHashes.find(*type) == NULL)
1608 continue;
21638c3a 1609
448c38bd
DK
1610 std::string tagname = *type;
1611 tagname.append("-History");
1612 std::string const tmp = Tags.FindS(tagname.c_str());
1613 if (tmp.empty() == true)
1614 continue;
a64bf0eb 1615
448c38bd
DK
1616 string hash, filename;
1617 unsigned long long size;
1618 std::stringstream ss(tmp);
56472095 1619
448c38bd 1620 while (ss >> hash >> size >> filename)
651bddad 1621 {
448c38bd
DK
1622 if (unlikely(hash.empty() == true || filename.empty() == true))
1623 continue;
1624
1625 // see if we have a record for this file already
1626 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1627 for (; cur != available_patches.end(); ++cur)
1628 {
4f51fd86 1629 if (cur->file != filename)
448c38bd
DK
1630 continue;
1631 cur->result_hashes.push_back(HashString(*type, hash));
1632 break;
1633 }
1634 if (cur != available_patches.end())
1635 continue;
1636 if (firstAcceptedHashes == true)
1637 {
1638 DiffInfo next;
1639 next.file = filename;
1640 next.result_hashes.push_back(HashString(*type, hash));
4f51fd86 1641 next.result_hashes.FileSize(size);
448c38bd
DK
1642 available_patches.push_back(next);
1643 }
1644 else
1645 {
1646 if (Debug == true)
1647 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1648 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
1649 break;
1650 }
651bddad 1651 }
448c38bd 1652 firstAcceptedHashes = false;
5d885723 1653 }
448c38bd
DK
1654
1655 if (unlikely(available_patches.empty() == true))
5d885723 1656 {
448c38bd
DK
1657 if (Debug)
1658 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1659 << "Couldn't find any patches for the patch series." << std::endl;
1660 return false;
5d885723 1661 }
8267fe24 1662
448c38bd 1663 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
b11f9599 1664 {
448c38bd
DK
1665 if (LocalHashes.find(*type) == NULL)
1666 continue;
97b65b10 1667
448c38bd
DK
1668 std::string tagname = *type;
1669 tagname.append("-Patches");
1670 std::string const tmp = Tags.FindS(tagname.c_str());
1671 if (tmp.empty() == true)
1672 continue;
18593cf7 1673
448c38bd
DK
1674 string hash, filename;
1675 unsigned long long size;
1676 std::stringstream ss(tmp);
03aa0847 1677
448c38bd 1678 while (ss >> hash >> size >> filename)
58702f85 1679 {
448c38bd
DK
1680 if (unlikely(hash.empty() == true || filename.empty() == true))
1681 continue;
146f7715 1682
448c38bd
DK
1683 // see if we have a record for this file already
1684 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1685 for (; cur != available_patches.end(); ++cur)
146f7715 1686 {
448c38bd
DK
1687 if (cur->file != filename)
1688 continue;
4f51fd86
DK
1689 if (cur->patch_hashes.empty())
1690 cur->patch_hashes.FileSize(size);
448c38bd 1691 cur->patch_hashes.push_back(HashString(*type, hash));
448c38bd 1692 break;
146f7715 1693 }
448c38bd
DK
1694 if (cur != available_patches.end())
1695 continue;
1696 if (Debug == true)
1697 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1698 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
146f7715 1699 break;
146f7715
DK
1700 }
1701 }
2d0a7bb4 1702
4f51fd86
DK
1703 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1704 {
1705 std::string tagname = *type;
1706 tagname.append("-Download");
1707 std::string const tmp = Tags.FindS(tagname.c_str());
1708 if (tmp.empty() == true)
1709 continue;
1710
1711 string hash, filename;
1712 unsigned long long size;
1713 std::stringstream ss(tmp);
1714
1715 // FIXME: all of pdiff supports only .gz compressed patches
1716 while (ss >> hash >> size >> filename)
1717 {
1718 if (unlikely(hash.empty() == true || filename.empty() == true))
1719 continue;
1720 if (unlikely(APT::String::Endswith(filename, ".gz") == false))
1721 continue;
1722 filename.erase(filename.length() - 3);
1723
1724 // see if we have a record for this file already
1725 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1726 for (; cur != available_patches.end(); ++cur)
1727 {
1728 if (cur->file != filename)
1729 continue;
1730 if (cur->download_hashes.empty())
1731 cur->download_hashes.FileSize(size);
1732 cur->download_hashes.push_back(HashString(*type, hash));
1733 break;
1734 }
1735 if (cur != available_patches.end())
1736 continue;
1737 if (Debug == true)
1738 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1739 << " wasn't in the list for the first parsed hash! (download)" << std::endl;
1740 break;
1741 }
1742 }
1743
1744
448c38bd
DK
1745 bool foundStart = false;
1746 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
1747 cur != available_patches.end(); ++cur)
1748 {
1749 if (LocalHashes != cur->result_hashes)
1750 continue;
1751
1752 available_patches.erase(available_patches.begin(), cur);
1753 foundStart = true;
1754 break;
e6e89390 1755 }
b3d44315 1756
448c38bd
DK
1757 if (foundStart == false || unlikely(available_patches.empty() == true))
1758 {
1759 if (Debug)
1760 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1761 << "Couldn't find the start of the patch series." << std::endl;
1762 return false;
1763 }
f6237efd 1764
448c38bd
DK
1765 // patching with too many files is rather slow compared to a fast download
1766 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
1767 if (fileLimit != 0 && fileLimit < available_patches.size())
1768 {
1769 if (Debug)
1770 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
1771 << ") so fallback to complete download" << std::endl;
1772 return false;
1773 }
1f4dd8fd 1774
448c38bd
DK
1775 // calculate the size of all patches we have to get
1776 // note that all sizes are uncompressed, while we download compressed files
1777 unsigned long long patchesSize = 0;
1778 for (std::vector<DiffInfo>::const_iterator cur = available_patches.begin();
1779 cur != available_patches.end(); ++cur)
4f51fd86 1780 patchesSize += cur->patch_hashes.FileSize();
448c38bd
DK
1781 unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
1782 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
1783 {
1784 if (Debug)
1785 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
1786 << ") so fallback to complete download" << std::endl;
1787 return false;
1788 }
2737f28a 1789
448c38bd
DK
1790 // we have something, queue the diffs
1791 string::size_type const last_space = Description.rfind(" ");
1792 if(last_space != string::npos)
1793 Description.erase(last_space, Description.size()-last_space);
1794
1795 /* decide if we should download patches one by one or in one go:
1796 The first is good if the server merges patches, but many don't so client
1797 based merging can be attempt in which case the second is better.
1798 "bad things" will happen if patches are merged on the server,
1799 but client side merging is attempt as well */
1800 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
1801 if (pdiff_merge == true)
6bf93605 1802 {
448c38bd
DK
1803 // reprepro adds this flag if it has merged patches on the server
1804 std::string const precedence = Tags.FindS("X-Patch-Precedence");
1805 pdiff_merge = (precedence != "merged");
6bf93605 1806 }
448c38bd
DK
1807
1808 if (pdiff_merge == false)
1809 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
6bf93605 1810 else
448c38bd
DK
1811 {
1812 std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
1813 for(size_t i = 0; i < available_patches.size(); ++i)
1814 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
1815 Target,
1816 available_patches[i],
1817 diffs);
1818 }
1819
1820 Complete = false;
1821 Status = StatDone;
1822 Dequeue();
1823 return true;
6bf93605
DK
1824}
1825 /*}}}*/
448c38bd 1826void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
6bf93605 1827{
448c38bd
DK
1828 Item::Failed(Message,Cnf);
1829 Status = StatDone;
1830
1831 if(Debug)
1832 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
1833 << "Falling back to normal index file acquire" << std::endl;
1834
1835 new pkgAcqIndex(Owner, TransactionManager, Target);
0118833a 1836}
61aea84d 1837 /*}}}*/
448c38bd
DK
1838void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
1839 pkgAcquire::MethodConfig const * const Cnf)
c88edf1d 1840{
448c38bd
DK
1841 if(Debug)
1842 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
c88edf1d 1843
448c38bd
DK
1844 Item::Done(Message, Hashes, Cnf);
1845
1846 string const FinalFile = GetFinalFilename();
1847 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
1848 DestFile = FinalFile;
1849
1850 if(ParseDiffIndex(DestFile) == false)
c88edf1d 1851 {
448c38bd
DK
1852 Failed("Message: Couldn't parse pdiff index", Cnf);
1853 // queue for final move - this should happen even if we fail
1854 // while parsing (e.g. on sizelimit) and download the complete file.
1855 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2737f28a
MV
1856 return;
1857 }
448c38bd
DK
1858
1859 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1860
1861 Complete = true;
1862 Status = StatDone;
1863 Dequeue();
1864
1865 return;
c88edf1d
AL
1866}
1867 /*}}}*/
448c38bd
DK
1868
1869// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
1870// ---------------------------------------------------------------------
1871/* The package diff is added to the queue. one object is constructed
1872 * for each diff and the index
1873 */
1874pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
1875 pkgAcqMetaBase * const TransactionManager,
1876 IndexTarget const * const Target,
1877 vector<DiffInfo> const &diffs)
1878 : pkgAcqBaseIndex(Owner, TransactionManager, Target),
1879 available_patches(diffs)
681d76d0 1880{
448c38bd 1881 DestFile = GetPartialFileNameFromURI(Target->URI);
e8b1db38 1882
448c38bd 1883 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
e8b1db38 1884
448c38bd
DK
1885 Desc.Owner = this;
1886 Description = Target->Description;
1887 Desc.ShortDesc = Target->ShortDesc;
631a7dc7 1888
448c38bd 1889 if(available_patches.empty() == true)
631a7dc7 1890 {
448c38bd
DK
1891 // we are done (yeah!), check hashes against the final file
1892 DestFile = GetFinalFileNameFromURI(Target->URI);
1893 Finish(true);
631a7dc7 1894 }
9d653a6d 1895 else
631a7dc7 1896 {
448c38bd
DK
1897 // patching needs to be bootstrapped with the 'old' version
1898 std::string const PartialFile = GetPartialFileNameFromURI(Target->URI);
1899 if (RealFileExists(PartialFile) == false)
6bf93605 1900 {
448c38bd 1901 if (symlink(GetFinalFilename().c_str(), PartialFile.c_str()) != 0)
6bf93605 1902 {
448c38bd
DK
1903 Failed("Link creation of " + PartialFile + " to " + GetFinalFilename() + " failed", NULL);
1904 return;
6bf93605
DK
1905 }
1906 }
1907
448c38bd
DK
1908 // get the next diff
1909 State = StateFetchDiff;
1910 QueueNextDiff();
631a7dc7 1911 }
448c38bd
DK
1912}
1913 /*}}}*/
1914void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1915{
1916 Item::Failed(Message,Cnf);
1917 Status = StatDone;
631a7dc7 1918
448c38bd
DK
1919 if(Debug)
1920 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
1921 << "Falling back to normal index file acquire" << std::endl;
1922 DestFile = GetPartialFileNameFromURI(Target->URI);
1923 RenameOnError(PDiffError);
36795154
DK
1924 std::string const patchname = GetDiffsPatchFileName(DestFile);
1925 if (RealFileExists(patchname))
1926 rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
448c38bd
DK
1927 new pkgAcqIndex(Owner, TransactionManager, Target);
1928 Finish();
1929}
1930 /*}}}*/
1931// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
1932void pkgAcqIndexDiffs::Finish(bool allDone)
1933{
1934 if(Debug)
1935 std::clog << "pkgAcqIndexDiffs::Finish(): "
1936 << allDone << " "
1937 << Desc.URI << std::endl;
1938
1939 // we restore the original name, this is required, otherwise
1940 // the file will be cleaned
1941 if(allDone)
4dbfe436 1942 {
448c38bd
DK
1943 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1944
1945 // this is for the "real" finish
1946 Complete = true;
e05672e8 1947 Status = StatDone;
448c38bd
DK
1948 Dequeue();
1949 if(Debug)
1950 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
1951 return;
e05672e8 1952 }
448c38bd
DK
1953
1954 if(Debug)
1955 std::clog << "Finishing: " << Desc.URI << std::endl;
1956 Complete = false;
1957 Status = StatDone;
1958 Dequeue();
1959 return;
681d76d0 1960}
92fcbfc1 1961 /*}}}*/
448c38bd 1962bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
b3d44315 1963{
448c38bd
DK
1964 // calc sha1 of the just patched file
1965 std::string const FinalFile = GetPartialFileNameFromURI(Target->URI);
1966
1967 if(!FileExists(FinalFile))
715c65de 1968 {
448c38bd
DK
1969 Failed("Message: No FinalFile " + FinalFile + " available", NULL);
1970 return false;
715c65de 1971 }
e05672e8 1972
448c38bd
DK
1973 FileFd fd(FinalFile, FileFd::ReadOnly);
1974 Hashes LocalHashesCalc;
1975 LocalHashesCalc.AddFD(fd);
1976 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
b3d44315 1977
448c38bd
DK
1978 if(Debug)
1979 std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
b3d44315 1980
448c38bd
DK
1981 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target->MetaKey);
1982 if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
b3d44315 1983 {
448c38bd
DK
1984 Failed("Local/Expected hashes are not usable", NULL);
1985 return false;
b3d44315 1986 }
b3d44315 1987
448c38bd
DK
1988
1989 // final file reached before all patches are applied
1990 if(LocalHashes == TargetFileHashes)
6bf93605 1991 {
448c38bd
DK
1992 Finish(true);
1993 return true;
6bf93605
DK
1994 }
1995
448c38bd
DK
1996 // remove all patches until the next matching patch is found
1997 // this requires the Index file to be ordered
1998 for(vector<DiffInfo>::iterator I = available_patches.begin();
1999 available_patches.empty() == false &&
2000 I != available_patches.end() &&
2001 I->result_hashes != LocalHashes;
2002 ++I)
f3097647 2003 {
448c38bd 2004 available_patches.erase(I);
b3d44315 2005 }
56bc3358 2006
448c38bd
DK
2007 // error checking and falling back if no patch was found
2008 if(available_patches.empty() == true)
56bc3358 2009 {
448c38bd 2010 Failed("No patches left to reach target", NULL);
f3097647 2011 return false;
56bc3358 2012 }
f3097647 2013
448c38bd
DK
2014 // queue the right diff
2015 Desc.URI = Target->URI + ".diff/" + available_patches[0].file + ".gz";
2016 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
2017 DestFile = GetPartialFileNameFromURI(Target->URI + ".diff/" + available_patches[0].file);
f3097647 2018
448c38bd
DK
2019 if(Debug)
2020 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
2021
2022 QueueURI(Desc);
f3097647
MV
2023
2024 return true;
2025}
2026 /*}}}*/
448c38bd
DK
2027void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2028 pkgAcquire::MethodConfig const * const Cnf)
27e6c17a 2029{
448c38bd
DK
2030 if(Debug)
2031 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
27e6c17a 2032
448c38bd 2033 Item::Done(Message, Hashes, Cnf);
27e6c17a 2034
448c38bd 2035 std::string const FinalFile = GetPartialFileNameFromURI(Target->URI);
36795154 2036 std::string const PatchFile = GetDiffsPatchFileName(FinalFile);
b3d44315 2037
448c38bd
DK
2038 // success in downloading a diff, enter ApplyDiff state
2039 if(State == StateFetchDiff)
b3d44315 2040 {
36795154 2041 Rename(DestFile, PatchFile);
448c38bd
DK
2042
2043 if(Debug)
2044 std::clog << "Sending to rred method: " << FinalFile << std::endl;
2045
2046 State = StateApplyDiff;
2047 Local = true;
2048 Desc.URI = "rred:" + FinalFile;
2049 QueueURI(Desc);
2050 SetActiveSubprocess("rred");
2051 return;
36795154 2052 }
448c38bd
DK
2053
2054 // success in download/apply a diff, queue next (if needed)
2055 if(State == StateApplyDiff)
8eafc759 2056 {
448c38bd
DK
2057 // remove the just applied patch
2058 available_patches.erase(available_patches.begin());
36795154 2059 unlink(PatchFile.c_str());
448c38bd
DK
2060
2061 // move into place
36795154 2062 if(Debug)
8eafc759 2063 {
448c38bd
DK
2064 std::clog << "Moving patched file in place: " << std::endl
2065 << DestFile << " -> " << FinalFile << std::endl;
8eafc759 2066 }
448c38bd
DK
2067 Rename(DestFile,FinalFile);
2068 chmod(FinalFile.c_str(),0644);
8eafc759 2069
448c38bd
DK
2070 // see if there is more to download
2071 if(available_patches.empty() == false) {
2072 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
2073 available_patches);
2074 return Finish();
2075 } else
2076 // update
2077 DestFile = FinalFile;
2078 return Finish(true);
ba6b79bd 2079 }
448c38bd
DK
2080}
2081 /*}}}*/
36795154
DK
2082std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2083{
2084 if(State != StateApplyDiff)
2085 return pkgAcqBaseIndex::Custom600Headers();
2086 std::ostringstream patchhashes;
2087 HashStringList const ExpectedHashes = available_patches[0].patch_hashes;
2088 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2089 patchhashes << "\nPatch-0-" << hs->HashType() << "-Hash: " << hs->HashValue();
2090 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2091 return patchhashes.str();
2092}
2093 /*}}}*/
448c38bd
DK
2094
2095// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2096pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
2097 pkgAcqMetaBase * const TransactionManager,
2098 IndexTarget const * const Target,
2099 DiffInfo const &patch,
2100 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
2101 : pkgAcqBaseIndex(Owner, TransactionManager, Target),
2102 patch(patch), allPatches(allPatches), State(StateFetchDiff)
2103{
2104 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2105
2106 Desc.Owner = this;
2107 Description = Target->Description;
2108 Desc.ShortDesc = Target->ShortDesc;
2109
2110 Desc.URI = Target->URI + ".diff/" + patch.file + ".gz";
2111 Desc.Description = Description + " " + patch.file + string(".pdiff");
2112
2113 DestFile = GetPartialFileNameFromURI(Target->URI + ".diff/" + patch.file);
2114
2115 if(Debug)
2116 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
2117
2118 QueueURI(Desc);
2119}
2120 /*}}}*/
2121void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2122{
2123 if(Debug)
2124 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
2737f28a 2125
448c38bd
DK
2126 Item::Failed(Message,Cnf);
2127 Status = StatDone;
b3d44315 2128
448c38bd
DK
2129 // check if we are the first to fail, otherwise we are done here
2130 State = StateDoneDiff;
2131 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2132 I != allPatches->end(); ++I)
2133 if ((*I)->State == StateErrorDiff)
2134 return;
2135
2136 // first failure means we should fallback
2137 State = StateErrorDiff;
2138 if (Debug)
2139 std::clog << "Falling back to normal index file acquire" << std::endl;
2140 DestFile = GetPartialFileNameFromURI(Target->URI);
2141 RenameOnError(PDiffError);
36795154
DK
2142 std::string const patchname = GetMergeDiffsPatchFileName(DestFile, patch.file);
2143 if (RealFileExists(patchname))
2144 rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
448c38bd 2145 new pkgAcqIndex(Owner, TransactionManager, Target);
b3d44315 2146}
92fcbfc1 2147 /*}}}*/
448c38bd
DK
2148void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2149 pkgAcquire::MethodConfig const * const Cnf)
b3d44315 2150{
448c38bd
DK
2151 if(Debug)
2152 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
18593cf7 2153
448c38bd 2154 Item::Done(Message, Hashes, Cnf);
18593cf7 2155
448c38bd 2156 string const FinalFile = GetPartialFileNameFromURI(Target->URI);
448c38bd
DK
2157 if (State == StateFetchDiff)
2158 {
36795154 2159 Rename(DestFile, GetMergeDiffsPatchFileName(FinalFile, patch.file));
448c38bd
DK
2160
2161 // check if this is the last completed diff
2162 State = StateDoneDiff;
2163 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2164 I != allPatches->end(); ++I)
2165 if ((*I)->State != StateDoneDiff)
2166 {
2167 if(Debug)
2168 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
2169 return;
2170 }
2171
2172 // this is the last completed diff, so we are ready to apply now
2173 State = StateApplyDiff;
ab53c018 2174
448c38bd
DK
2175 // patching needs to be bootstrapped with the 'old' version
2176 if (symlink(GetFinalFilename().c_str(), FinalFile.c_str()) != 0)
2177 {
2178 Failed("Link creation of " + FinalFile + " to " + GetFinalFilename() + " failed", NULL);
2179 return;
18593cf7 2180 }
448c38bd
DK
2181
2182 if(Debug)
2183 std::clog << "Sending to rred method: " << FinalFile << std::endl;
2184
2185 Local = true;
2186 Desc.URI = "rred:" + FinalFile;
2187 QueueURI(Desc);
2188 SetActiveSubprocess("rred");
2189 return;
2190 }
2191 // success in download/apply all diffs, clean up
2192 else if (State == StateApplyDiff)
2193 {
2194 // move the result into place
2195 std::string const Final = GetFinalFilename();
2196 if(Debug)
2197 std::clog << "Queue patched file in place: " << std::endl
2198 << DestFile << " -> " << Final << std::endl;
2199
2200 // queue for copy by the transaction manager
2201 TransactionManager->TransactionStageCopy(this, DestFile, Final);
2202
2203 // ensure the ed's are gone regardless of list-cleanup
2204 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2205 I != allPatches->end(); ++I)
ab53c018 2206 {
448c38bd 2207 std::string const PartialFile = GetPartialFileNameFromURI(Target->URI);
36795154 2208 std::string const patch = GetMergeDiffsPatchFileName(PartialFile, (*I)->patch.file);
448c38bd 2209 unlink(patch.c_str());
b3d44315 2210 }
448c38bd 2211 unlink(FinalFile.c_str());
e1430400 2212
448c38bd
DK
2213 // all set and done
2214 Complete = true;
2215 if(Debug)
2216 std::clog << "allDone: " << DestFile << "\n" << std::endl;
b3d44315
MV
2217 }
2218}
92fcbfc1 2219 /*}}}*/
36795154
DK
2220std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2221{
2222 if(State != StateApplyDiff)
2223 return pkgAcqBaseIndex::Custom600Headers();
2224 std::ostringstream patchhashes;
2225 unsigned int seen_patches = 0;
2226 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2227 I != allPatches->end(); ++I)
2228 {
2229 HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
2230 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2231 patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
2232 ++seen_patches;
2233 }
2234 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2235 return patchhashes.str();
2236}
2237 /*}}}*/
448c38bd
DK
2238
2239// AcqIndex::AcqIndex - Constructor /*{{{*/
2240pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
2241 pkgAcqMetaBase * const TransactionManager,
2242 IndexTarget const * const Target)
2243 : pkgAcqBaseIndex(Owner, TransactionManager, Target)
b3d44315 2244{
448c38bd
DK
2245 // autoselect the compression method
2246 AutoSelectCompression();
2247 Init(Target->URI, Target->Description, Target->ShortDesc);
ce424cd4 2248
448c38bd
DK
2249 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2250 std::clog << "New pkgIndex with TransactionManager "
2251 << TransactionManager << std::endl;
2252}
2253 /*}}}*/
2254// AcqIndex::AutoSelectCompression - Select compression /*{{{*/
2255void pkgAcqIndex::AutoSelectCompression()
2256{
2257 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
2258 CompressionExtensions = "";
2259 if (TransactionManager->MetaIndexParser != NULL && TransactionManager->MetaIndexParser->Exists(Target->MetaKey))
ce424cd4 2260 {
448c38bd
DK
2261 for (std::vector<std::string>::const_iterator t = types.begin();
2262 t != types.end(); ++t)
2263 {
2264 std::string CompressedMetaKey = string(Target->MetaKey).append(".").append(*t);
2265 if (*t == "uncompressed" ||
2266 TransactionManager->MetaIndexParser->Exists(CompressedMetaKey) == true)
2267 CompressionExtensions.append(*t).append(" ");
2268 }
ce424cd4 2269 }
448c38bd 2270 else
b3d44315 2271 {
448c38bd
DK
2272 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
2273 CompressionExtensions.append(*t).append(" ");
b3d44315 2274 }
448c38bd
DK
2275 if (CompressionExtensions.empty() == false)
2276 CompressionExtensions.erase(CompressionExtensions.end()-1);
2277}
2278 /*}}}*/
2279// AcqIndex::Init - defered Constructor /*{{{*/
2280void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
2281 string const &ShortDesc)
2282{
2283 Stage = STAGE_DOWNLOAD;
b3d44315 2284
448c38bd 2285 DestFile = GetPartialFileNameFromURI(URI);
b3d44315 2286
448c38bd
DK
2287 size_t const nextExt = CompressionExtensions.find(' ');
2288 if (nextExt == std::string::npos)
b3d44315 2289 {
448c38bd
DK
2290 CurrentCompressionExtension = CompressionExtensions;
2291 CompressionExtensions.clear();
b3d44315 2292 }
448c38bd
DK
2293 else
2294 {
2295 CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
2296 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
1ddb8596
DK
2297 }
2298
448c38bd 2299 if (CurrentCompressionExtension == "uncompressed")
6bf93605 2300 {
448c38bd 2301 Desc.URI = URI;
6bf93605 2302 }
448c38bd
DK
2303 else if (unlikely(CurrentCompressionExtension.empty()))
2304 return;
2305 else
b3d44315 2306 {
448c38bd
DK
2307 Desc.URI = URI + '.' + CurrentCompressionExtension;
2308 DestFile = DestFile + '.' + CurrentCompressionExtension;
b3d44315
MV
2309 }
2310
448c38bd
DK
2311 if(TransactionManager->MetaIndexParser != NULL)
2312 InitByHashIfNeeded();
2313
2314 Desc.Description = URIDesc;
2315 Desc.Owner = this;
2316 Desc.ShortDesc = ShortDesc;
2317
2318 QueueURI(Desc);
2319}
2320 /*}}}*/
2321// AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
2322void pkgAcqIndex::InitByHashIfNeeded()
2323{
2324 // TODO:
2325 // - (maybe?) add support for by-hash into the sources.list as flag
2326 // - make apt-ftparchive generate the hashes (and expire?)
2327 std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
2328 if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
2329 _config->FindB(HostKnob, false) == true ||
2330 TransactionManager->MetaIndexParser->GetSupportsAcquireByHash())
b3d44315 2331 {
448c38bd
DK
2332 HashStringList const Hashes = GetExpectedHashes();
2333 if(Hashes.usable())
b3d44315 2334 {
448c38bd
DK
2335 // FIXME: should we really use the best hash here? or a fixed one?
2336 HashString const * const TargetHash = Hashes.find("");
2337 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2338 size_t const trailing_slash = Desc.URI.find_last_of("/");
2339 Desc.URI = Desc.URI.replace(
2340 trailing_slash,
2341 Desc.URI.substr(trailing_slash+1).size()+1,
2342 ByHash);
2343 } else {
2344 _error->Warning(
2345 "Fetching ByHash requested but can not find record for %s",
2346 GetMetaKey().c_str());
b3d44315
MV
2347 }
2348 }
b3d44315 2349}
92fcbfc1 2350 /*}}}*/
448c38bd
DK
2351// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2352// ---------------------------------------------------------------------
2353/* The only header we use is the last-modified header. */
2354string pkgAcqIndex::Custom600Headers() const
b3d44315 2355{
448c38bd 2356 string Final = GetFinalFilename();
c5fced38 2357
448c38bd
DK
2358 string msg = "\nIndex-File: true";
2359 struct stat Buf;
2360 if (stat(Final.c_str(),&Buf) == 0)
2361 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1d970e6c 2362
448c38bd
DK
2363 if(Target->IsOptional())
2364 msg += "\nFail-Ignore: true";
2365
2366 return msg;
b3d44315 2367}
681d76d0 2368 /*}}}*/
448c38bd
DK
2369// AcqIndex::Failed - getting the indexfile failed /*{{{*/
2370void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
56472095 2371{
448c38bd
DK
2372 Item::Failed(Message,Cnf);
2373
2374 // authorisation matches will not be fixed by other compression types
2375 if (Status != StatAuthError)
2376 {
2377 if (CompressionExtensions.empty() == false)
2378 {
2379 Init(Target->URI, Desc.Description, Desc.ShortDesc);
2380 Status = StatIdle;
2381 return;
2382 }
2383 }
2384
2385 if(Target->IsOptional() && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
2386 Status = StatDone;
2387 else
2388 TransactionManager->AbortTransaction();
56472095 2389}
8267fbd9 2390 /*}}}*/
448c38bd
DK
2391// AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
2392void pkgAcqIndex::ReverifyAfterIMS()
fe0f7911 2393{
448c38bd
DK
2394 // update destfile to *not* include the compression extension when doing
2395 // a reverify (as its uncompressed on disk already)
2396 DestFile = GetCompressedFileName(Target->URI, GetPartialFileNameFromURI(Target->URI), CurrentCompressionExtension);
2397
2398 // copy FinalFile into partial/ so that we check the hash again
2399 string FinalFile = GetFinalFilename();
2400 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2401 Desc.URI = "copy:" + FinalFile;
2402 QueueURI(Desc);
fe0f7911
DK
2403}
2404 /*}}}*/
448c38bd
DK
2405// AcqIndex::ValidateFile - Validate the content of the downloaded file /*{{{*/
2406bool pkgAcqIndex::ValidateFile(const std::string &FileName)
ffcccd62 2407{
448c38bd
DK
2408 // FIXME: this can go away once we only ever download stuff that
2409 // has a valid hash and we never do GET based probing
2410 // FIXME2: this also leaks debian-isms into the code and should go therefore
2411
2412 /* Always validate the index file for correctness (all indexes must
2413 * have a Package field) (LP: #346386) (Closes: #627642)
2414 */
2415 FileFd fd(FileName, FileFd::ReadOnly, FileFd::Extension);
2416 // Only test for correctness if the content of the file is not empty
2417 // (empty is ok)
2418 if (fd.Size() > 0)
2419 {
2420 pkgTagSection sec;
2421 pkgTagFile tag(&fd);
2422
2423 // all our current indexes have a field 'Package' in each section
2424 if (_error->PendingError() == true ||
2425 tag.Step(sec) == false ||
2426 sec.Exists("Package") == false)
2427 return false;
2428 }
2429 return true;
ffcccd62
DK
2430}
2431 /*}}}*/
448c38bd
DK
2432// AcqIndex::Done - Finished a fetch /*{{{*/
2433// ---------------------------------------------------------------------
2434/* This goes through a number of states.. On the initial fetch the
2435 method could possibly return an alternate filename which points
2436 to the uncompressed version of the file. If this is so the file
2437 is copied into the partial directory. In all other cases the file
2438 is decompressed with a compressed uri. */
2439void pkgAcqIndex::Done(string const &Message,
2440 HashStringList const &Hashes,
2441 pkgAcquire::MethodConfig const * const Cfg)
8d6c5839 2442{
448c38bd
DK
2443 Item::Done(Message,Hashes,Cfg);
2444
2445 switch(Stage)
2446 {
2447 case STAGE_DOWNLOAD:
2448 StageDownloadDone(Message, Hashes, Cfg);
2449 break;
2450 case STAGE_DECOMPRESS_AND_VERIFY:
2451 StageDecompressDone(Message, Hashes, Cfg);
2452 break;
2453 }
8d6c5839
MV
2454}
2455 /*}}}*/
448c38bd
DK
2456// AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2457void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const &,
2458 pkgAcquire::MethodConfig const * const)
6bf93605 2459{
448c38bd 2460 Complete = true;
6bf93605 2461
448c38bd
DK
2462 // Handle the unzipd case
2463 string FileName = LookupTag(Message,"Alt-Filename");
2464 if (FileName.empty() == false)
6bf93605 2465 {
448c38bd
DK
2466 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2467 Local = true;
2468 DestFile += ".decomp";
2469 Desc.URI = "copy:" + FileName;
2470 QueueURI(Desc);
2471 SetActiveSubprocess("copy");
2472 return;
6bf93605 2473 }
03aa0847 2474
448c38bd
DK
2475 FileName = LookupTag(Message,"Filename");
2476 if (FileName.empty() == true)
e84d3803 2477 {
448c38bd
DK
2478 Status = StatError;
2479 ErrorText = "Method gave a blank filename";
e84d3803 2480 }
f3097647 2481
448c38bd
DK
2482 // Methods like e.g. "file:" will give us a (compressed) FileName that is
2483 // not the "DestFile" we set, in this case we uncompress from the local file
2484 if (FileName != DestFile)
2485 Local = true;
2486 else
2487 EraseFileName = FileName;
2488
2489 // we need to verify the file against the current Release file again
2490 // on if-modfied-since hit to avoid a stale attack against us
2491 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
f3097647 2492 {
448c38bd
DK
2493 // The files timestamp matches, reverify by copy into partial/
2494 EraseFileName = "";
2495 ReverifyAfterIMS();
f3097647
MV
2496 return;
2497 }
448c38bd
DK
2498
2499 // If we have compressed indexes enabled, queue for hash verification
2500 if (_config->FindB("Acquire::GzipIndexes",false))
6bf93605 2501 {
448c38bd
DK
2502 DestFile = GetPartialFileNameFromURI(Target->URI + '.' + CurrentCompressionExtension);
2503 EraseFileName = "";
2504 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2505 Desc.URI = "copy:" + FileName;
2506 QueueURI(Desc);
2507 SetActiveSubprocess("copy");
2508 return;
2509 }
2510
2511 // get the binary name for your used compression type
2512 string decompProg;
2513 if(CurrentCompressionExtension == "uncompressed")
2514 decompProg = "copy";
2515 else
2516 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
2517 if(decompProg.empty() == true)
2518 {
2519 _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
2520 return;
6bf93605 2521 }
448c38bd
DK
2522
2523 // queue uri for the next stage
2524 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2525 DestFile += ".decomp";
2526 Desc.URI = decompProg + ":" + FileName;
2527 QueueURI(Desc);
2528 SetActiveSubprocess(decompProg);
a9bb651a
MV
2529}
2530 /*}}}*/
448c38bd
DK
2531// AcqIndex::StageDecompressDone - Final verification /*{{{*/
2532void pkgAcqIndex::StageDecompressDone(string const &Message,
2533 HashStringList const &,
2534 pkgAcquire::MethodConfig const * const Cfg)
a9bb651a 2535{
448c38bd 2536 if(!ValidateFile(DestFile))
fe0f7911 2537 {
448c38bd
DK
2538 RenameOnError(InvalidFormat);
2539 Failed(Message, Cfg);
2540 return;
fe0f7911 2541 }
673c9469 2542
448c38bd
DK
2543 // Done, queue for rename on transaction finished
2544 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
4dbfe436 2545
448c38bd 2546 return;
fe0f7911
DK
2547}
2548 /*}}}*/
448c38bd
DK
2549
2550
03e39e59
AL
2551// AcqArchive::AcqArchive - Constructor /*{{{*/
2552// ---------------------------------------------------------------------
17caf1b1
AL
2553/* This just sets up the initial fetch environment and queues the first
2554 possibilitiy */
448c38bd
DK
2555pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
2556 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
30e1eab5 2557 string &StoreFilename) :
448c38bd
DK
2558 Item(Owner), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
2559 StoreFilename(StoreFilename), Vf(Version.FileList()),
b3d44315 2560 Trusted(false)
03e39e59 2561{
7d8afa39 2562 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
2563
2564 if (Version.Arch() == 0)
bdae53f1 2565 {
d1f1f6a8 2566 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
2567 "This might mean you need to manually fix this package. "
2568 "(due to missing arch)"),
40f8a8ba 2569 Version.ParentPkg().FullName().c_str());
bdae53f1
AL
2570 return;
2571 }
813c8eea 2572
b2e465d6
AL
2573 /* We need to find a filename to determine the extension. We make the
2574 assumption here that all the available sources for this version share
2575 the same extension.. */
2576 // Skip not source sources, they do not have file fields.
69c2ecbd 2577 for (; Vf.end() == false; ++Vf)
b2e465d6
AL
2578 {
2579 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2580 continue;
2581 break;
2582 }
2583
2584 // Does not really matter here.. we are going to fail out below
2585 if (Vf.end() != true)
2586 {
2587 // If this fails to get a file name we will bomb out below.
2588 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2589 if (_error->PendingError() == true)
2590 return;
2591
2592 // Generate the final file name as: package_version_arch.foo
2593 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2594 QuoteString(Version.VerStr(),"_:") + '_' +
2595 QuoteString(Version.Arch(),"_:.") +
2596 "." + flExtension(Parse.FileName());
2597 }
b3d44315
MV
2598
2599 // check if we have one trusted source for the package. if so, switch
6c34ccca
DK
2600 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2601 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2602 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2603 bool seenUntrusted = false;
f7f0d6c7 2604 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
b3d44315
MV
2605 {
2606 pkgIndexFile *Index;
2607 if (Sources->FindIndex(i.File(),Index) == false)
2608 continue;
6c34ccca
DK
2609
2610 if (debugAuth == true)
b3d44315 2611 std::cerr << "Checking index: " << Index->Describe()
6c34ccca
DK
2612 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2613
2614 if (Index->IsTrusted() == true)
2615 {
b3d44315 2616 Trusted = true;
6c34ccca
DK
2617 if (allowUnauth == false)
2618 break;
b3d44315 2619 }
6c34ccca
DK
2620 else
2621 seenUntrusted = true;
b3d44315
MV
2622 }
2623
a3371852
MV
2624 // "allow-unauthenticated" restores apts old fetching behaviour
2625 // that means that e.g. unauthenticated file:// uris are higher
2626 // priority than authenticated http:// uris
6c34ccca 2627 if (allowUnauth == true && seenUntrusted == true)
a3371852
MV
2628 Trusted = false;
2629
03e39e59 2630 // Select a source
b185acc2 2631 if (QueueNext() == false && _error->PendingError() == false)
d57f6084
DK
2632 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2633 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
b185acc2
AL
2634}
2635 /*}}}*/
2636// AcqArchive::QueueNext - Queue the next file source /*{{{*/
2637// ---------------------------------------------------------------------
17caf1b1
AL
2638/* This queues the next available file version for download. It checks if
2639 the archive is already available in the cache and stashs the MD5 for
2640 checking later. */
b185acc2 2641bool pkgAcqArchive::QueueNext()
a722b2c5 2642{
f7f0d6c7 2643 for (; Vf.end() == false; ++Vf)
03e39e59 2644 {
448c38bd 2645 pkgCache::PkgFileIterator const PkgF = Vf.File();
03e39e59 2646 // Ignore not source sources
448c38bd 2647 if ((PkgF->Flags & pkgCache::Flag::NotSource) != 0)
03e39e59
AL
2648 continue;
2649
2650 // Try to cross match against the source list
b2e465d6 2651 pkgIndexFile *Index;
448c38bd 2652 if (Sources->FindIndex(PkgF, Index) == false)
b2e465d6 2653 continue;
448c38bd
DK
2654 LocalSource = (PkgF->Flags & pkgCache::Flag::LocalSource) == pkgCache::Flag::LocalSource;
2655
b3d44315
MV
2656 // only try to get a trusted package from another source if that source
2657 // is also trusted
2658 if(Trusted && !Index->IsTrusted())
2659 continue;
2660
03e39e59
AL
2661 // Grab the text package record
2662 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2663 if (_error->PendingError() == true)
b185acc2 2664 return false;
b3501edb 2665
b2e465d6 2666 string PkgFile = Parse.FileName();
b3501edb
DK
2667 ExpectedHashes = Parse.Hashes();
2668
03e39e59 2669 if (PkgFile.empty() == true)
b2e465d6
AL
2670 return _error->Error(_("The package index files are corrupted. No Filename: "
2671 "field for package %s."),
2672 Version.ParentPkg().Name());
a6568219 2673
b3d44315
MV
2674 Desc.URI = Index->ArchiveURI(PkgFile);
2675 Desc.Description = Index->ArchiveInfo(Version);
2676 Desc.Owner = this;
40f8a8ba 2677 Desc.ShortDesc = Version.ParentPkg().FullName(true);
b3d44315 2678
17caf1b1 2679 // See if we already have the file. (Legacy filenames)
a6568219
AL
2680 FileSize = Version->Size;
2681 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2682 struct stat Buf;
2683 if (stat(FinalFile.c_str(),&Buf) == 0)
2684 {
2685 // Make sure the size matches
73da43e9 2686 if ((unsigned long long)Buf.st_size == Version->Size)
a6568219
AL
2687 {
2688 Complete = true;
2689 Local = true;
2690 Status = StatDone;
30e1eab5 2691 StoreFilename = DestFile = FinalFile;
b185acc2 2692 return true;
a6568219
AL
2693 }
2694
6b1ff003
AL
2695 /* Hmm, we have a file and its size does not match, this means it is
2696 an old style mismatched arch */
a6568219
AL
2697 unlink(FinalFile.c_str());
2698 }
17caf1b1
AL
2699
2700 // Check it again using the new style output filenames
2701 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2702 if (stat(FinalFile.c_str(),&Buf) == 0)
2703 {
2704 // Make sure the size matches
73da43e9 2705 if ((unsigned long long)Buf.st_size == Version->Size)
17caf1b1
AL
2706 {
2707 Complete = true;
2708 Local = true;
2709 Status = StatDone;
2710 StoreFilename = DestFile = FinalFile;
2711 return true;
2712 }
2713
1e3f4083 2714 /* Hmm, we have a file and its size does not match, this shouldn't
17caf1b1
AL
2715 happen.. */
2716 unlink(FinalFile.c_str());
2717 }
2718
2719 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
6b1ff003
AL
2720
2721 // Check the destination file
2722 if (stat(DestFile.c_str(),&Buf) == 0)
2723 {
2724 // Hmm, the partial file is too big, erase it
73da43e9 2725 if ((unsigned long long)Buf.st_size > Version->Size)
6b1ff003
AL
2726 unlink(DestFile.c_str());
2727 else
2728 PartialSize = Buf.st_size;
2729 }
de31189f
DK
2730
2731 // Disables download of archives - useful if no real installation follows,
2732 // e.g. if we are just interested in proposed installation order
2733 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2734 {
2735 Complete = true;
2736 Local = true;
2737 Status = StatDone;
2738 StoreFilename = DestFile = FinalFile;
2739 return true;
2740 }
2741
03e39e59 2742 // Create the item
b2e465d6 2743 Local = false;
03e39e59 2744 QueueURI(Desc);
b185acc2 2745
f7f0d6c7 2746 ++Vf;
b185acc2 2747 return true;
03e39e59 2748 }
b185acc2
AL
2749 return false;
2750}
03e39e59
AL
2751 /*}}}*/
2752// AcqArchive::Done - Finished fetching /*{{{*/
2753// ---------------------------------------------------------------------
2754/* */
448c38bd
DK
2755void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
2756 pkgAcquire::MethodConfig const * const Cfg)
03e39e59 2757{
448c38bd 2758 Item::Done(Message, Hashes, Cfg);
a6568219
AL
2759
2760 // Grab the output filename
03e39e59
AL
2761 string FileName = LookupTag(Message,"Filename");
2762 if (FileName.empty() == true)
2763 {
2764 Status = StatError;
2765 ErrorText = "Method gave a blank filename";
2766 return;
2767 }
a6568219 2768
30e1eab5 2769 // Reference filename
a6568219
AL
2770 if (FileName != DestFile)
2771 {
30e1eab5 2772 StoreFilename = DestFile = FileName;
a6568219 2773 Local = true;
5684f71f 2774 Complete = true;
a6568219
AL
2775 return;
2776 }
5684f71f 2777
a6568219 2778 // Done, move it into position
295d848b 2779 string const FinalFile = GetFinalFilename();
a6568219 2780 Rename(DestFile,FinalFile);
30e1eab5 2781 StoreFilename = DestFile = FinalFile;
03e39e59
AL
2782 Complete = true;
2783}
2784 /*}}}*/
db890fdb
AL
2785// AcqArchive::Failed - Failure handler /*{{{*/
2786// ---------------------------------------------------------------------
2787/* Here we try other sources */
448c38bd 2788void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
db890fdb 2789{
03aa0847
DK
2790 Item::Failed(Message,Cnf);
2791
448c38bd 2792 /* We don't really want to retry on failed media swaps, this prevents
b2e465d6
AL
2793 that. An interesting observation is that permanent failures are not
2794 recorded. */
448c38bd 2795 if (Cnf->Removable == true &&
b2e465d6
AL
2796 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2797 {
2798 // Vf = Version.FileList();
f7f0d6c7 2799 while (Vf.end() == false) ++Vf;
b2e465d6 2800 StoreFilename = string();
b2e465d6
AL
2801 return;
2802 }
03aa0847
DK
2803
2804 Status = StatIdle;
db890fdb 2805 if (QueueNext() == false)
7d8afa39
AL
2806 {
2807 // This is the retry counter
2808 if (Retries != 0 &&
2809 Cnf->LocalOnly == false &&
2810 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2811 {
2812 Retries--;
2813 Vf = Version.FileList();
2814 if (QueueNext() == true)
2815 return;
2816 }
03aa0847 2817
9dbb421f 2818 StoreFilename = string();
03aa0847 2819 Status = StatError;
7d8afa39 2820 }
db890fdb
AL
2821}
2822 /*}}}*/
448c38bd 2823APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
b3d44315
MV
2824{
2825 return Trusted;
2826}
92fcbfc1 2827 /*}}}*/
448c38bd 2828void pkgAcqArchive::Finished() /*{{{*/
ab559b35
AL
2829{
2830 if (Status == pkgAcquire::Item::StatDone &&
2831 Complete == true)
2832 return;
2833 StoreFilename = string();
2834}
2835 /*}}}*/
448c38bd
DK
2836std::string pkgAcqArchive::DescURI() const /*{{{*/
2837{
2838 return Desc.URI;
2839}
2840 /*}}}*/
2841std::string pkgAcqArchive::ShortDesc() const /*{{{*/
2842{
2843 return Desc.ShortDesc;
2844}
2845 /*}}}*/
2846
36375005 2847// AcqFile::pkgAcqFile - Constructor /*{{{*/
448c38bd
DK
2848pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
2849 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
77278c2b 2850 const string &DestDir, const string &DestFilename,
448c38bd
DK
2851 bool const IsIndexFile) :
2852 Item(Owner), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
36375005 2853{
08cfc005 2854 Retries = _config->FindI("Acquire::Retries",0);
448c38bd 2855
46e00f9d
MV
2856 if(!DestFilename.empty())
2857 DestFile = DestFilename;
2858 else if(!DestDir.empty())
2859 DestFile = DestDir + "/" + flNotDir(URI);
2860 else
2861 DestFile = flNotDir(URI);
2862
36375005
AL
2863 // Create the item
2864 Desc.URI = URI;
2865 Desc.Description = Dsc;
2866 Desc.Owner = this;
2867
2868 // Set the short description to the archive component
2869 Desc.ShortDesc = ShortDesc;
448c38bd 2870
36375005
AL
2871 // Get the transfer sizes
2872 FileSize = Size;
2873 struct stat Buf;
2874 if (stat(DestFile.c_str(),&Buf) == 0)
2875 {
2876 // Hmm, the partial file is too big, erase it
ed9665ae 2877 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
36375005
AL
2878 unlink(DestFile.c_str());
2879 else
2880 PartialSize = Buf.st_size;
2881 }
092ae175 2882
36375005
AL
2883 QueueURI(Desc);
2884}
2885 /*}}}*/
2886// AcqFile::Done - Item downloaded OK /*{{{*/
448c38bd
DK
2887void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
2888 pkgAcquire::MethodConfig const * const Cnf)
36375005 2889{
448c38bd 2890 Item::Done(Message,CalcHashes,Cnf);
495e5cb2 2891
36375005
AL
2892 string FileName = LookupTag(Message,"Filename");
2893 if (FileName.empty() == true)
2894 {
2895 Status = StatError;
2896 ErrorText = "Method gave a blank filename";
2897 return;
2898 }
2899
2900 Complete = true;
448c38bd 2901
36375005
AL
2902 // The files timestamp matches
2903 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2904 return;
448c38bd 2905
36375005
AL
2906 // We have to copy it into place
2907 if (FileName != DestFile)
2908 {
2909 Local = true;
459681d3
AL
2910 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2911 Cnf->Removable == true)
917ae805
AL
2912 {
2913 Desc.URI = "copy:" + FileName;
2914 QueueURI(Desc);
2915 return;
2916 }
448c38bd 2917
83ab33fc
AL
2918 // Erase the file if it is a symlink so we can overwrite it
2919 struct stat St;
2920 if (lstat(DestFile.c_str(),&St) == 0)
2921 {
2922 if (S_ISLNK(St.st_mode) != 0)
2923 unlink(DestFile.c_str());
2924 }
448c38bd 2925
83ab33fc 2926 // Symlink the file
917ae805
AL
2927 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2928 {
03aa0847
DK
2929 _error->PushToStack();
2930 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
2931 std::stringstream msg;
2932 _error->DumpErrors(msg);
2933 _error->RevertToStack();
2934 ErrorText = msg.str();
917ae805
AL
2935 Status = StatError;
2936 Complete = false;
448c38bd 2937 }
36375005
AL
2938 }
2939}
2940 /*}}}*/
08cfc005
AL
2941// AcqFile::Failed - Failure handler /*{{{*/
2942// ---------------------------------------------------------------------
2943/* Here we try other sources */
448c38bd 2944void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
08cfc005 2945{
03aa0847
DK
2946 Item::Failed(Message,Cnf);
2947
08cfc005
AL
2948 // This is the retry counter
2949 if (Retries != 0 &&
2950 Cnf->LocalOnly == false &&
2951 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2952 {
03aa0847 2953 --Retries;
08cfc005 2954 QueueURI(Desc);
03aa0847 2955 Status = StatIdle;
08cfc005
AL
2956 return;
2957 }
03aa0847 2958
08cfc005
AL
2959}
2960 /*}}}*/
448c38bd 2961string pkgAcqFile::Custom600Headers() const /*{{{*/
77278c2b
MV
2962{
2963 if (IsIndexFile)
2964 return "\nIndex-File: true";
61a07c57 2965 return "";
77278c2b
MV
2966}
2967 /*}}}*/