]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/acquire-item.cc
add pkgAcquire::TransactionHasError()
[apt.git] / apt-pkg / acquire-item.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
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.
12
13 ##################################################################### */
14 /*}}}*/
15// Include Files /*{{{*/
16#include <config.h>
17
18#include <apt-pkg/acquire-item.h>
19#include <apt-pkg/configuration.h>
20#include <apt-pkg/aptconfiguration.h>
21#include <apt-pkg/sourcelist.h>
22#include <apt-pkg/error.h>
23#include <apt-pkg/strutl.h>
24#include <apt-pkg/fileutl.h>
25#include <apt-pkg/sha1.h>
26#include <apt-pkg/tagfile.h>
27#include <apt-pkg/indexrecords.h>
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>
40#include <sys/stat.h>
41#include <unistd.h>
42#include <errno.h>
43#include <string>
44#include <sstream>
45#include <stdio.h>
46#include <ctime>
47
48#include <apti18n.h>
49 /*}}}*/
50
51using namespace std;
52
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 /*}}}*/
65
66// Acquire::Item::Item - Constructor /*{{{*/
67pkgAcquire::Item::Item(pkgAcquire *Owner, HashStringList const &ExpectedHashes) :
68 Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
69 Local(false), QueueCounter(0), TransactionID(0), ExpectedAdditionalItems(0),
70 ExpectedHashes(ExpectedHashes)
71{
72 Owner->Add(this);
73 Status = StatIdle;
74}
75 /*}}}*/
76// Acquire::Item::~Item - Destructor /*{{{*/
77// ---------------------------------------------------------------------
78/* */
79pkgAcquire::Item::~Item()
80{
81 Owner->Remove(this);
82}
83 /*}}}*/
84// Acquire::Item::Failed - Item failed to download /*{{{*/
85// ---------------------------------------------------------------------
86/* We return to an idle state if there are still other queues that could
87 fetch this object */
88void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
89{
90 Status = StatIdle;
91 ErrorText = LookupTag(Message,"Message");
92 UsedMirror = LookupTag(Message,"UsedMirror");
93 if (QueueCounter <= 1)
94 {
95 /* This indicates that the file is not available right now but might
96 be sometime later. If we do a retry cycle then this should be
97 retried [CDROMs] */
98 if (Cnf->LocalOnly == true &&
99 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
100 {
101 Status = StatIdle;
102 Dequeue();
103 return;
104 }
105
106 Status = StatError;
107 Dequeue();
108 }
109
110 // report mirror failure back to LP if we actually use a mirror
111 string FailReason = LookupTag(Message, "FailReason");
112 if(FailReason.size() != 0)
113 ReportMirrorFailure(FailReason);
114 else
115 ReportMirrorFailure(ErrorText);
116}
117 /*}}}*/
118// Acquire::Item::Start - Item has begun to download /*{{{*/
119// ---------------------------------------------------------------------
120/* Stash status and the file size. Note that setting Complete means
121 sub-phases of the acquire process such as decompresion are operating */
122void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
123{
124 Status = StatFetching;
125 if (FileSize == 0 && Complete == false)
126 FileSize = Size;
127}
128 /*}}}*/
129// Acquire::Item::Done - Item downloaded OK /*{{{*/
130// ---------------------------------------------------------------------
131/* */
132void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringList const &/*Hash*/,
133 pkgAcquire::MethodConfig * /*Cnf*/)
134{
135 // We just downloaded something..
136 string FileName = LookupTag(Message,"Filename");
137 UsedMirror = LookupTag(Message,"UsedMirror");
138 if (Complete == false && !Local && FileName == DestFile)
139 {
140 if (Owner->Log != 0)
141 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
142 }
143
144 if (FileSize == 0)
145 FileSize= Size;
146 Status = StatDone;
147 ErrorText = string();
148 Owner->Dequeue(this);
149}
150 /*}}}*/
151// Acquire::Item::Rename - Rename a file /*{{{*/
152// ---------------------------------------------------------------------
153/* This helper function is used by a lot of item methods as their final
154 step */
155void pkgAcquire::Item::Rename(string From,string To)
156{
157 if (rename(From.c_str(),To.c_str()) != 0)
158 {
159 char S[300];
160 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
161 From.c_str(),To.c_str());
162 Status = StatError;
163 ErrorText = S;
164 }
165}
166 /*}}}*/
167bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
168{
169 if(FileExists(DestFile))
170 Rename(DestFile, DestFile + ".FAILED");
171
172 switch (error)
173 {
174 case HashSumMismatch:
175 ErrorText = _("Hash Sum mismatch");
176 Status = StatAuthError;
177 ReportMirrorFailure("HashChecksumFailure");
178 break;
179 case SizeMismatch:
180 ErrorText = _("Size mismatch");
181 Status = StatAuthError;
182 ReportMirrorFailure("SizeFailure");
183 break;
184 case InvalidFormat:
185 ErrorText = _("Invalid file format");
186 Status = StatError;
187 // do not report as usually its not the mirrors fault, but Portal/Proxy
188 break;
189 }
190 return false;
191}
192 /*}}}*/
193// Acquire::Item::ReportMirrorFailure /*{{{*/
194// ---------------------------------------------------------------------
195void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
196{
197 // we only act if a mirror was used at all
198 if(UsedMirror.empty())
199 return;
200#if 0
201 std::cerr << "\nReportMirrorFailure: "
202 << UsedMirror
203 << " Uri: " << DescURI()
204 << " FailCode: "
205 << FailCode << std::endl;
206#endif
207 const char *Args[40];
208 unsigned int i = 0;
209 string report = _config->Find("Methods::Mirror::ProblemReporting",
210 "/usr/lib/apt/apt-report-mirror-failure");
211 if(!FileExists(report))
212 return;
213 Args[i++] = report.c_str();
214 Args[i++] = UsedMirror.c_str();
215 Args[i++] = DescURI().c_str();
216 Args[i++] = FailCode.c_str();
217 Args[i++] = NULL;
218 pid_t pid = ExecFork();
219 if(pid < 0)
220 {
221 _error->Error("ReportMirrorFailure Fork failed");
222 return;
223 }
224 else if(pid == 0)
225 {
226 execvp(Args[0], (char**)Args);
227 std::cerr << "Could not exec " << Args[0] << std::endl;
228 _exit(100);
229 }
230 if(!ExecWait(pid, "report-mirror-failure"))
231 {
232 _error->Warning("Couldn't report problem to '%s'",
233 _config->Find("Methods::Mirror::ProblemReporting").c_str());
234 }
235}
236 /*}}}*/
237// AcqSubIndex::AcqSubIndex - Constructor /*{{{*/
238// ---------------------------------------------------------------------
239/* Get a sub-index file based on checksums from a 'master' file and
240 possibly query additional files */
241pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
242 string const &URIDesc, string const &ShortDesc,
243 HashStringList const &ExpectedHashes)
244 : Item(Owner, ExpectedHashes)
245{
246 /* XXX: Beware: Currently this class does nothing (of value) anymore ! */
247 Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
248
249 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
250 DestFile += URItoFileName(URI);
251
252 Desc.URI = URI;
253 Desc.Description = URIDesc;
254 Desc.Owner = this;
255 Desc.ShortDesc = ShortDesc;
256
257 QueueURI(Desc);
258
259 if(Debug)
260 std::clog << "pkgAcqSubIndex: " << Desc.URI << std::endl;
261}
262 /*}}}*/
263// AcqSubIndex::Custom600Headers - Insert custom request headers /*{{{*/
264// ---------------------------------------------------------------------
265/* The only header we use is the last-modified header. */
266string pkgAcqSubIndex::Custom600Headers() const
267{
268 string Final = _config->FindDir("Dir::State::lists");
269 Final += URItoFileName(Desc.URI);
270
271 struct stat Buf;
272 if (stat(Final.c_str(),&Buf) != 0)
273 return "\nIndex-File: true\nFail-Ignore: true\n";
274 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
275}
276 /*}}}*/
277void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
278{
279 if(Debug)
280 std::clog << "pkgAcqSubIndex failed: " << Desc.URI << " with " << Message << std::endl;
281
282 Complete = false;
283 Status = StatDone;
284 Dequeue();
285
286 // No good Index is provided
287}
288 /*}}}*/
289void pkgAcqSubIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
290 pkgAcquire::MethodConfig *Cnf)
291{
292 if(Debug)
293 std::clog << "pkgAcqSubIndex::Done(): " << Desc.URI << std::endl;
294
295 string FileName = LookupTag(Message,"Filename");
296 if (FileName.empty() == true)
297 {
298 Status = StatError;
299 ErrorText = "Method gave a blank filename";
300 return;
301 }
302
303 if (FileName != DestFile)
304 {
305 Local = true;
306 Desc.URI = "copy:" + FileName;
307 QueueURI(Desc);
308 return;
309 }
310
311 Item::Done(Message, Size, Hashes, Cnf);
312
313 string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI);
314
315 /* Downloaded invalid transindex => Error (LP: #346386) (Closes: #627642) */
316 indexRecords SubIndexParser;
317 if (FileExists(DestFile) == true && !SubIndexParser.Load(DestFile)) {
318 Status = StatError;
319 ErrorText = SubIndexParser.ErrorText;
320 return;
321 }
322
323 // success in downloading the index
324 // rename the index
325 if(Debug)
326 std::clog << "Renaming: " << DestFile << " -> " << FinalFile << std::endl;
327 Rename(DestFile,FinalFile);
328 chmod(FinalFile.c_str(),0644);
329 DestFile = FinalFile;
330
331 if(ParseIndex(DestFile) == false)
332 return Failed("", NULL);
333
334 Complete = true;
335 Status = StatDone;
336 Dequeue();
337 return;
338}
339 /*}}}*/
340bool pkgAcqSubIndex::ParseIndex(string const &IndexFile) /*{{{*/
341{
342 indexRecords SubIndexParser;
343 if (FileExists(IndexFile) == false || SubIndexParser.Load(IndexFile) == false)
344 return false;
345 // so something with the downloaded index
346 return true;
347}
348 /*}}}*/
349// AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
350// ---------------------------------------------------------------------
351/* Get the DiffIndex file first and see if there are patches available
352 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
353 * patches. If anything goes wrong in that process, it will fall back to
354 * the original packages file
355 */
356pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcqMetaIndex *MetaOwner,
357 IndexTarget const * const Target,
358 HashStringList const &ExpectedHashes,
359 indexRecords *MetaIndexParser)
360 : pkgAcqBaseIndex(MetaOwner, Target, ExpectedHashes,
361 MetaIndexParser)
362{
363
364 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
365
366 RealURI = Target->URI;
367 Desc.Owner = this;
368 Desc.Description = Target->Description + "/DiffIndex";
369 Desc.ShortDesc = Target->ShortDesc;
370 Desc.URI = Target->URI + ".diff/Index";
371
372 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
373 DestFile += URItoFileName(Target->URI) + string(".DiffIndex");
374
375 if(Debug)
376 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
377
378 // look for the current package file
379 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
380 CurrentPackagesFile += URItoFileName(RealURI);
381
382 // FIXME: this file:/ check is a hack to prevent fetching
383 // from local sources. this is really silly, and
384 // should be fixed cleanly as soon as possible
385 if(!FileExists(CurrentPackagesFile) ||
386 Desc.URI.substr(0,strlen("file:/")) == "file:/")
387 {
388 // we don't have a pkg file or we don't want to queue
389 if(Debug)
390 std::clog << "No index file, local or canceld by user" << std::endl;
391 Failed("", NULL);
392 return;
393 }
394
395 if(Debug)
396 std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): "
397 << CurrentPackagesFile << std::endl;
398
399 QueueURI(Desc);
400
401}
402 /*}}}*/
403// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
404// ---------------------------------------------------------------------
405/* The only header we use is the last-modified header. */
406string pkgAcqDiffIndex::Custom600Headers() const
407{
408 string Final = _config->FindDir("Dir::State::lists");
409 Final += URItoFileName(RealURI) + string(".IndexDiff");
410
411 if(Debug)
412 std::clog << "Custom600Header-IMS: " << Final << std::endl;
413
414 struct stat Buf;
415 if (stat(Final.c_str(),&Buf) != 0)
416 return "\nIndex-File: true";
417
418 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
419}
420 /*}}}*/
421bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
422{
423 if(Debug)
424 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
425 << std::endl;
426
427 pkgTagSection Tags;
428 string ServerSha1;
429 vector<DiffInfo> available_patches;
430
431 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
432 pkgTagFile TF(&Fd);
433 if (_error->PendingError() == true)
434 return false;
435
436 if(TF.Step(Tags) == true)
437 {
438 bool found = false;
439 DiffInfo d;
440 string size;
441
442 string const tmp = Tags.FindS("SHA1-Current");
443 std::stringstream ss(tmp);
444 ss >> ServerSha1 >> size;
445 unsigned long const ServerSize = atol(size.c_str());
446
447 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
448 SHA1Summation SHA1;
449 SHA1.AddFD(fd);
450 string const local_sha1 = SHA1.Result();
451
452 if(local_sha1 == ServerSha1)
453 {
454 // we have the same sha1 as the server so we are done here
455 if(Debug)
456 std::clog << "Package file is up-to-date" << std::endl;
457 // list cleanup needs to know that this file as well as the already
458 // present index is ours, so we create an empty diff to save it for us
459 new pkgAcqIndexDiffs(MetaOwner, Target, ExpectedHashes, MetaIndexParser,
460 ServerSha1, available_patches);
461 return true;
462 }
463 else
464 {
465 if(Debug)
466 std::clog << "SHA1-Current: " << ServerSha1 << " and we start at "<< fd.Name() << " " << fd.Size() << " " << local_sha1 << std::endl;
467
468 // check the historie and see what patches we need
469 string const history = Tags.FindS("SHA1-History");
470 std::stringstream hist(history);
471 while(hist >> d.sha1 >> size >> d.file)
472 {
473 // read until the first match is found
474 // from that point on, we probably need all diffs
475 if(d.sha1 == local_sha1)
476 found=true;
477 else if (found == false)
478 continue;
479
480 if(Debug)
481 std::clog << "Need to get diff: " << d.file << std::endl;
482 available_patches.push_back(d);
483 }
484
485 if (available_patches.empty() == false)
486 {
487 // patching with too many files is rather slow compared to a fast download
488 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
489 if (fileLimit != 0 && fileLimit < available_patches.size())
490 {
491 if (Debug)
492 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
493 << ") so fallback to complete download" << std::endl;
494 return false;
495 }
496
497 // see if the patches are too big
498 found = false; // it was true and it will be true again at the end
499 d = *available_patches.begin();
500 string const firstPatch = d.file;
501 unsigned long patchesSize = 0;
502 std::stringstream patches(Tags.FindS("SHA1-Patches"));
503 while(patches >> d.sha1 >> size >> d.file)
504 {
505 if (firstPatch == d.file)
506 found = true;
507 else if (found == false)
508 continue;
509
510 patchesSize += atol(size.c_str());
511 }
512 unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
513 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
514 {
515 if (Debug)
516 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
517 << ") so fallback to complete download" << std::endl;
518 return false;
519 }
520 }
521 }
522
523 // we have something, queue the next diff
524 if(found)
525 {
526 // queue the diffs
527 string::size_type const last_space = Description.rfind(" ");
528 if(last_space != string::npos)
529 Description.erase(last_space, Description.size()-last_space);
530
531 /* decide if we should download patches one by one or in one go:
532 The first is good if the server merges patches, but many don't so client
533 based merging can be attempt in which case the second is better.
534 "bad things" will happen if patches are merged on the server,
535 but client side merging is attempt as well */
536 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
537 if (pdiff_merge == true)
538 {
539 // reprepro adds this flag if it has merged patches on the server
540 std::string const precedence = Tags.FindS("X-Patch-Precedence");
541 pdiff_merge = (precedence != "merged");
542 }
543
544 if (pdiff_merge == false)
545 {
546 new pkgAcqIndexDiffs(MetaOwner, Target, ExpectedHashes, MetaIndexParser,
547 ServerSha1, available_patches);
548 }
549 else
550 {
551 std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
552 for(size_t i = 0; i < available_patches.size(); ++i)
553 (*diffs)[i] = new pkgAcqIndexMergeDiffs(MetaOwner, Target,
554 ExpectedHashes,
555 MetaIndexParser,
556 available_patches[i],
557 diffs);
558 }
559
560 Complete = false;
561 Status = StatDone;
562 Dequeue();
563 return true;
564 }
565 }
566
567 // Nothing found, report and return false
568 // Failing here is ok, if we return false later, the full
569 // IndexFile is queued
570 if(Debug)
571 std::clog << "Can't find a patch in the index file" << std::endl;
572 return false;
573}
574 /*}}}*/
575void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
576{
577 if(Debug)
578 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
579 << "Falling back to normal index file acquire" << std::endl;
580
581 new pkgAcqIndex(MetaOwner, Target, ExpectedHashes, MetaIndexParser);
582
583 Complete = false;
584 Status = StatDone;
585 Dequeue();
586}
587 /*}}}*/
588void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
589 pkgAcquire::MethodConfig *Cnf)
590{
591 if(Debug)
592 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
593
594 Item::Done(Message, Size, Hashes, Cnf);
595
596 string FinalFile;
597 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
598
599 // success in downloading the index
600 // rename the index
601 FinalFile += string(".IndexDiff");
602 if(Debug)
603 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
604 << std::endl;
605 Rename(DestFile,FinalFile);
606 chmod(FinalFile.c_str(),0644);
607 DestFile = FinalFile;
608
609 if(!ParseDiffIndex(DestFile))
610 return Failed("", NULL);
611
612 Complete = true;
613 Status = StatDone;
614 Dequeue();
615 return;
616}
617 /*}}}*/
618// AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
619// ---------------------------------------------------------------------
620/* The package diff is added to the queue. one object is constructed
621 * for each diff and the index
622 */
623pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcqMetaIndex *MetaOwner,
624 struct IndexTarget const * const Target,
625 HashStringList const &ExpectedHashes,
626 indexRecords *MetaIndexParser,
627 string ServerSha1,
628 vector<DiffInfo> diffs)
629 : pkgAcqBaseIndex(MetaOwner, Target, ExpectedHashes, MetaIndexParser),
630 available_patches(diffs), ServerSha1(ServerSha1)
631{
632
633 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
634 DestFile += URItoFileName(Target->URI);
635
636 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
637
638 RealURI = Target->URI;
639 Desc.Owner = this;
640 Description = Target->Description;
641 Desc.ShortDesc = Target->ShortDesc;
642
643 if(available_patches.empty() == true)
644 {
645 // we are done (yeah!)
646 Finish(true);
647 }
648 else
649 {
650 // get the next diff
651 State = StateFetchDiff;
652 QueueNextDiff();
653 }
654}
655 /*}}}*/
656void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
657{
658 if(Debug)
659 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
660 << "Falling back to normal index file acquire" << std::endl;
661 new pkgAcqIndex(MetaOwner, Target, ExpectedHashes, MetaIndexParser);
662 Finish();
663}
664 /*}}}*/
665// Finish - helper that cleans the item out of the fetcher queue /*{{{*/
666void pkgAcqIndexDiffs::Finish(bool allDone)
667{
668 // we restore the original name, this is required, otherwise
669 // the file will be cleaned
670 if(allDone)
671 {
672 DestFile = _config->FindDir("Dir::State::lists");
673 DestFile += URItoFileName(RealURI);
674
675 if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
676 {
677 RenameOnError(HashSumMismatch);
678 Dequeue();
679 return;
680 }
681
682 // this is for the "real" finish
683 Complete = true;
684 Status = StatDone;
685 Dequeue();
686 if(Debug)
687 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
688 return;
689 }
690
691 if(Debug)
692 std::clog << "Finishing: " << Desc.URI << std::endl;
693 Complete = false;
694 Status = StatDone;
695 Dequeue();
696 return;
697}
698 /*}}}*/
699bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
700{
701
702 // calc sha1 of the just patched file
703 string FinalFile = _config->FindDir("Dir::State::lists");
704 FinalFile += URItoFileName(RealURI);
705
706 FileFd fd(FinalFile, FileFd::ReadOnly);
707 SHA1Summation SHA1;
708 SHA1.AddFD(fd);
709 string local_sha1 = string(SHA1.Result());
710 if(Debug)
711 std::clog << "QueueNextDiff: "
712 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
713
714 // final file reached before all patches are applied
715 if(local_sha1 == ServerSha1)
716 {
717 Finish(true);
718 return true;
719 }
720
721 // remove all patches until the next matching patch is found
722 // this requires the Index file to be ordered
723 for(vector<DiffInfo>::iterator I=available_patches.begin();
724 available_patches.empty() == false &&
725 I != available_patches.end() &&
726 I->sha1 != local_sha1;
727 ++I)
728 {
729 available_patches.erase(I);
730 }
731
732 // error checking and falling back if no patch was found
733 if(available_patches.empty() == true)
734 {
735 Failed("", NULL);
736 return false;
737 }
738
739 // queue the right diff
740 Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz";
741 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
742 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
743 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
744
745 if(Debug)
746 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
747
748 QueueURI(Desc);
749
750 return true;
751}
752 /*}}}*/
753void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringList const &Hashes, /*{{{*/
754 pkgAcquire::MethodConfig *Cnf)
755{
756 if(Debug)
757 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
758
759 Item::Done(Message, Size, Hashes, Cnf);
760
761 string FinalFile;
762 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
763
764 // success in downloading a diff, enter ApplyDiff state
765 if(State == StateFetchDiff)
766 {
767
768 // rred excepts the patch as $FinalFile.ed
769 Rename(DestFile,FinalFile+".ed");
770
771 if(Debug)
772 std::clog << "Sending to rred method: " << FinalFile << std::endl;
773
774 State = StateApplyDiff;
775 Local = true;
776 Desc.URI = "rred:" + FinalFile;
777 QueueURI(Desc);
778 Mode = "rred";
779 return;
780 }
781
782
783 // success in download/apply a diff, queue next (if needed)
784 if(State == StateApplyDiff)
785 {
786 // remove the just applied patch
787 available_patches.erase(available_patches.begin());
788 unlink((FinalFile + ".ed").c_str());
789
790 // move into place
791 if(Debug)
792 {
793 std::clog << "Moving patched file in place: " << std::endl
794 << DestFile << " -> " << FinalFile << std::endl;
795 }
796 Rename(DestFile,FinalFile);
797 chmod(FinalFile.c_str(),0644);
798
799 // see if there is more to download
800 if(available_patches.empty() == false) {
801 new pkgAcqIndexDiffs(MetaOwner, Target,
802 ExpectedHashes, MetaIndexParser,
803 ServerSha1, available_patches);
804 return Finish();
805 } else
806 return Finish(true);
807 }
808}
809 /*}}}*/
810// AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
811pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcqMetaIndex *MetaOwner,
812 struct IndexTarget const * const Target,
813 HashStringList const &ExpectedHashes,
814 indexRecords *MetaIndexParser,
815 DiffInfo const &patch,
816 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
817 : pkgAcqBaseIndex(MetaOwner, Target, ExpectedHashes, MetaIndexParser),
818 patch(patch), allPatches(allPatches), State(StateFetchDiff)
819{
820
821 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
822 DestFile += URItoFileName(Target->URI);
823
824 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
825
826 RealURI = Target->URI;
827 Desc.Owner = this;
828 Description = Target->Description;
829 Desc.ShortDesc = Target->ShortDesc;
830
831 Desc.URI = RealURI + ".diff/" + patch.file + ".gz";
832 Desc.Description = Description + " " + patch.file + string(".pdiff");
833 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
834 DestFile += URItoFileName(RealURI + ".diff/" + patch.file);
835
836 if(Debug)
837 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
838
839 QueueURI(Desc);
840}
841 /*}}}*/
842void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
843{
844 if(Debug)
845 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
846 Complete = false;
847 Status = StatDone;
848 Dequeue();
849
850 // check if we are the first to fail, otherwise we are done here
851 State = StateDoneDiff;
852 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
853 I != allPatches->end(); ++I)
854 if ((*I)->State == StateErrorDiff)
855 return;
856
857 // first failure means we should fallback
858 State = StateErrorDiff;
859 std::clog << "Falling back to normal index file acquire" << std::endl;
860 new pkgAcqIndex(MetaOwner, Target, ExpectedHashes, MetaIndexParser);
861}
862 /*}}}*/
863void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
864 pkgAcquire::MethodConfig *Cnf)
865{
866 if(Debug)
867 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
868
869 Item::Done(Message,Size,Hashes,Cnf);
870
871 string const FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
872
873 if (State == StateFetchDiff)
874 {
875 // rred expects the patch as $FinalFile.ed.$patchname.gz
876 Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
877
878 // check if this is the last completed diff
879 State = StateDoneDiff;
880 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
881 I != allPatches->end(); ++I)
882 if ((*I)->State != StateDoneDiff)
883 {
884 if(Debug)
885 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
886 return;
887 }
888
889 // this is the last completed diff, so we are ready to apply now
890 State = StateApplyDiff;
891
892 if(Debug)
893 std::clog << "Sending to rred method: " << FinalFile << std::endl;
894
895 Local = true;
896 Desc.URI = "rred:" + FinalFile;
897 QueueURI(Desc);
898 Mode = "rred";
899 return;
900 }
901 // success in download/apply all diffs, clean up
902 else if (State == StateApplyDiff)
903 {
904 // see if we really got the expected file
905 if(ExpectedHashes.usable() && !ExpectedHashes.VerifyFile(DestFile))
906 {
907 RenameOnError(HashSumMismatch);
908 return;
909 }
910
911 // move the result into place
912 if(Debug)
913 std::clog << "Moving patched file in place: " << std::endl
914 << DestFile << " -> " << FinalFile << std::endl;
915 Rename(DestFile, FinalFile);
916 chmod(FinalFile.c_str(), 0644);
917
918 // otherwise lists cleanup will eat the file
919 DestFile = FinalFile;
920
921 // ensure the ed's are gone regardless of list-cleanup
922 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
923 I != allPatches->end(); ++I)
924 {
925 std::string patch = FinalFile + ".ed." + (*I)->patch.file + ".gz";
926 unlink(patch.c_str());
927 }
928
929 // all set and done
930 Complete = true;
931 if(Debug)
932 std::clog << "allDone: " << DestFile << "\n" << std::endl;
933 }
934}
935 /*}}}*/
936// AcqIndex::AcqIndex - Constructor /*{{{*/
937// ---------------------------------------------------------------------
938/* The package file is added to the queue and a second class is
939 instantiated to fetch the revision file */
940pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
941 string URI,string URIDesc,string ShortDesc,
942 HashStringList const &ExpectedHash, string comprExt)
943 : pkgAcqBaseIndex(Owner, NULL, ExpectedHash, NULL), RealURI(URI)
944{
945 if(comprExt.empty() == true)
946 {
947 // autoselect the compression method
948 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
949 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
950 comprExt.append(*t).append(" ");
951 if (comprExt.empty() == false)
952 comprExt.erase(comprExt.end()-1);
953 }
954 CompressionExtension = comprExt;
955
956 Init(URI, URIDesc, ShortDesc);
957}
958#if 0
959pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
960 HashStringList const &ExpectedHash,
961 indexRecords *MetaIndexParser)
962 : pkgAcqBaseIndex(Owner, Target, ExpectedHash, MetaIndexParser),
963 RealURI(Target->URI)
964{
965 // autoselect the compression method
966 AutoSelectCompression();
967 Init(Target->URI, Target->Description, Target->ShortDesc);
968}
969#endif
970 /*}}}*/
971pkgAcqIndex::pkgAcqIndex(pkgAcqMetaIndex *MetaOwner,
972 IndexTarget const *Target,
973 HashStringList const &ExpectedHash,
974 indexRecords *MetaIndexParser)
975 : pkgAcqBaseIndex(MetaOwner->GetOwner(), Target, ExpectedHash,
976 MetaIndexParser), RealURI(Target->URI)
977{
978 // autoselect the compression method
979 AutoSelectCompression();
980 Init(Target->URI, Target->Description, Target->ShortDesc);
981
982 TransactionID = (unsigned long)MetaOwner;
983}
984 /*}}}*/
985void pkgAcqIndex::AutoSelectCompression()
986{
987 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
988 CompressionExtension = "";
989 if (ExpectedHashes.usable())
990 {
991 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
992 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
993 CompressionExtension.append(*t).append(" ");
994 }
995 else
996 {
997 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
998 CompressionExtension.append(*t).append(" ");
999 }
1000 if (CompressionExtension.empty() == false)
1001 CompressionExtension.erase(CompressionExtension.end()-1);
1002}
1003// AcqIndex::Init - defered Constructor /*{{{*/
1004void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) {
1005 Decompression = false;
1006 Erase = false;
1007
1008 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1009 DestFile += URItoFileName(URI);
1010
1011 std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1012 std::string MetaKey;
1013 if (comprExt == "uncompressed")
1014 {
1015 Desc.URI = URI;
1016 if(Target)
1017 MetaKey = string(Target->MetaKey);
1018 }
1019 else
1020 {
1021 Desc.URI = URI + '.' + comprExt;
1022 if(Target)
1023 MetaKey = string(Target->MetaKey) + '.' + comprExt;
1024 }
1025
1026 // load the filesize
1027 if(MetaIndexParser)
1028 {
1029 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1030 if(Record)
1031 FileSize = Record->Size;
1032
1033 InitByHashIfNeeded(MetaKey);
1034 }
1035
1036 Desc.Description = URIDesc;
1037 Desc.Owner = this;
1038 Desc.ShortDesc = ShortDesc;
1039
1040 QueueURI(Desc);
1041}
1042 /*}}}*/
1043// AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
1044// ---------------------------------------------------------------------
1045/* */
1046void pkgAcqIndex::InitByHashIfNeeded(const std::string MetaKey)
1047{
1048 // TODO:
1049 // - (maybe?) add support for by-hash into the sources.list as flag
1050 // - make apt-ftparchive generate the hashes (and expire?)
1051 std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
1052 if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
1053 _config->FindB(HostKnob, false) == true ||
1054 MetaIndexParser->GetSupportsAcquireByHash())
1055 {
1056 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1057 if(Record)
1058 {
1059 // FIXME: should we really use the best hash here? or a fixed one?
1060 const HashString *TargetHash = Record->Hashes.find("");
1061 std::string ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
1062 size_t trailing_slash = Desc.URI.find_last_of("/");
1063 Desc.URI = Desc.URI.replace(
1064 trailing_slash,
1065 Desc.URI.substr(trailing_slash+1).size()+1,
1066 ByHash);
1067 } else {
1068 _error->Warning(
1069 "Fetching ByHash requested but can not find record for %s",
1070 MetaKey.c_str());
1071 }
1072 }
1073}
1074 /*}}}*/
1075// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1076// ---------------------------------------------------------------------
1077/* The only header we use is the last-modified header. */
1078string pkgAcqIndex::Custom600Headers() const
1079{
1080 string Final = _config->FindDir("Dir::State::lists");
1081 Final += URItoFileName(RealURI);
1082 if (_config->FindB("Acquire::GzipIndexes",false))
1083 Final += ".gz";
1084
1085 string msg = "\nIndex-File: true";
1086 // FIXME: this really should use "IndexTarget::IsOptional()" but that
1087 // seems to be difficult without breaking ABI
1088 if (ShortDesc().find("Translation") != 0)
1089 msg += "\nFail-Ignore: true";
1090 struct stat Buf;
1091 if (stat(Final.c_str(),&Buf) == 0)
1092 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1093
1094 return msg;
1095}
1096 /*}}}*/
1097void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1098{
1099 size_t const nextExt = CompressionExtension.find(' ');
1100 if (nextExt != std::string::npos)
1101 {
1102 CompressionExtension = CompressionExtension.substr(nextExt+1);
1103 Init(RealURI, Desc.Description, Desc.ShortDesc);
1104 return;
1105 }
1106
1107 // on decompression failure, remove bad versions in partial/
1108 if (Decompression && Erase) {
1109 string s = _config->FindDir("Dir::State::lists") + "partial/";
1110 s.append(URItoFileName(RealURI));
1111 unlink(s.c_str());
1112 }
1113
1114 Item::Failed(Message,Cnf);
1115
1116 /// cancel the entire transaction
1117 Owner->AbortTransaction(TransactionID);
1118}
1119 /*}}}*/
1120// AcqIndex::Done - Finished a fetch /*{{{*/
1121// ---------------------------------------------------------------------
1122/* This goes through a number of states.. On the initial fetch the
1123 method could possibly return an alternate filename which points
1124 to the uncompressed version of the file. If this is so the file
1125 is copied into the partial directory. In all other cases the file
1126 is decompressed with a gzip uri. */
1127void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,
1128 pkgAcquire::MethodConfig *Cfg)
1129{
1130 Item::Done(Message,Size,Hashes,Cfg);
1131
1132 if (Decompression == true)
1133 {
1134 if (ExpectedHashes.usable() && ExpectedHashes != Hashes)
1135 {
1136 RenameOnError(HashSumMismatch);
1137 printHashSumComparision(RealURI, ExpectedHashes, Hashes);
1138 Failed(Message, Cfg);
1139 return;
1140 }
1141
1142 // FIXME: this can go away once we only ever download stuff that
1143 // has a valid hash and we never do GET based probing
1144 //
1145 /* Always verify the index file for correctness (all indexes must
1146 * have a Package field) (LP: #346386) (Closes: #627642)
1147 */
1148 FileFd fd(DestFile, FileFd::ReadOnly);
1149 // Only test for correctness if the file is not empty (empty is ok)
1150 if (fd.FileSize() > 0)
1151 {
1152 pkgTagSection sec;
1153 pkgTagFile tag(&fd);
1154
1155 // all our current indexes have a field 'Package' in each section
1156 if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false)
1157 {
1158 RenameOnError(InvalidFormat);
1159 Failed(Message, Cfg);
1160 return;
1161 }
1162 }
1163
1164 // Done, queue for rename on transaction finished
1165 PartialFile = DestFile;
1166
1167 string FinalFile = _config->FindDir("Dir::State::lists");
1168 FinalFile += URItoFileName(RealURI);
1169 DestFile = FinalFile;
1170#if 0
1171 /* We restore the original name to DestFile so that the clean operation
1172 will work OK */
1173 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1174 DestFile += URItoFileName(RealURI);
1175
1176 // Remove the compressed version.
1177 if (Erase == true)
1178 unlink(DestFile.c_str());
1179#endif
1180 return;
1181 }
1182
1183 Erase = false;
1184 Complete = true;
1185
1186 // Handle the unzipd case
1187 string FileName = LookupTag(Message,"Alt-Filename");
1188 if (FileName.empty() == false)
1189 {
1190 // The files timestamp matches
1191 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
1192 return;
1193 Decompression = true;
1194 Local = true;
1195 DestFile += ".decomp";
1196 Desc.URI = "copy:" + FileName;
1197 QueueURI(Desc);
1198 Mode = "copy";
1199 return;
1200 }
1201
1202 FileName = LookupTag(Message,"Filename");
1203 if (FileName.empty() == true)
1204 {
1205 Status = StatError;
1206 ErrorText = "Method gave a blank filename";
1207 }
1208
1209 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1210
1211 // The files timestamp matches
1212 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
1213 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
1214 // Update DestFile for .gz suffix so that the clean operation keeps it
1215 DestFile += ".gz";
1216 return;
1217 }
1218
1219 if (FileName == DestFile)
1220 Erase = true;
1221 else
1222 Local = true;
1223
1224 string decompProg;
1225
1226 // If we enable compressed indexes and already have gzip, keep it
1227 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
1228 string FinalFile = _config->FindDir("Dir::State::lists");
1229 FinalFile += URItoFileName(RealURI) + ".gz";
1230 Rename(DestFile,FinalFile);
1231 chmod(FinalFile.c_str(),0644);
1232
1233 // Update DestFile for .gz suffix so that the clean operation keeps it
1234 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1235 DestFile += URItoFileName(RealURI) + ".gz";
1236 return;
1237 }
1238
1239 // get the binary name for your used compression type
1240 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
1241 if(decompProg.empty() == false);
1242 else if(compExt == "uncompressed")
1243 decompProg = "copy";
1244 else {
1245 _error->Error("Unsupported extension: %s", compExt.c_str());
1246 return;
1247 }
1248
1249 Decompression = true;
1250 DestFile += ".decomp";
1251 Desc.URI = decompProg + ":" + FileName;
1252 QueueURI(Desc);
1253
1254 // FIXME: this points to a c++ string that goes out of scope
1255 Mode = decompProg.c_str();
1256}
1257 /*}}}*/
1258// AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1259// ---------------------------------------------------------------------
1260/* The Translation file is added to the queue */
1261pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1262 string URI,string URIDesc,string ShortDesc)
1263 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList(), "")
1264{
1265}
1266 /*}}}*/
1267pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcqMetaIndex *MetaOwner, IndexTarget const * const Target,
1268 HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser)
1269 : pkgAcqIndex(MetaOwner, Target, ExpectedHashes, MetaIndexParser)
1270{
1271 // load the filesize
1272 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1273 if(Record)
1274 FileSize = Record->Size;
1275}
1276 /*}}}*/
1277// AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1278// ---------------------------------------------------------------------
1279string pkgAcqIndexTrans::Custom600Headers() const
1280{
1281 string Final = _config->FindDir("Dir::State::lists");
1282 Final += URItoFileName(RealURI);
1283
1284 struct stat Buf;
1285 if (stat(Final.c_str(),&Buf) != 0)
1286 return "\nFail-Ignore: true\nIndex-File: true";
1287 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1288}
1289 /*}}}*/
1290// AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1291// ---------------------------------------------------------------------
1292/* */
1293void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1294{
1295 size_t const nextExt = CompressionExtension.find(' ');
1296 if (nextExt != std::string::npos)
1297 {
1298 CompressionExtension = CompressionExtension.substr(nextExt+1);
1299 Init(RealURI, Desc.Description, Desc.ShortDesc);
1300 Status = StatIdle;
1301 return;
1302 }
1303
1304 if (Cnf->LocalOnly == true ||
1305 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1306 {
1307 // Ignore this
1308 Status = StatDone;
1309 Complete = false;
1310 Dequeue();
1311 return;
1312 }
1313
1314 Item::Failed(Message,Cnf);
1315}
1316 /*}}}*/
1317pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
1318 string URI,string URIDesc,string ShortDesc,
1319 string MetaIndexURI, string MetaIndexURIDesc,
1320 string MetaIndexShortDesc,
1321 const vector<IndexTarget*>* IndexTargets,
1322 indexRecords* MetaIndexParser) :
1323 Item(Owner, HashStringList()), RealURI(URI), MetaIndexURI(MetaIndexURI),
1324 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1325 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
1326{
1327 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1328 DestFile += URItoFileName(URI);
1329
1330 // remove any partial downloaded sig-file in partial/.
1331 // it may confuse proxies and is too small to warrant a
1332 // partial download anyway
1333 unlink(DestFile.c_str());
1334
1335 // Create the item
1336 Desc.Description = URIDesc;
1337 Desc.Owner = this;
1338 Desc.ShortDesc = ShortDesc;
1339 Desc.URI = URI;
1340
1341 string Final = _config->FindDir("Dir::State::lists");
1342 Final += URItoFileName(RealURI);
1343 if (RealFileExists(Final) == true)
1344 {
1345 // File was already in place. It needs to be re-downloaded/verified
1346 // because Release might have changed, we do give it a different
1347 // name than DestFile because otherwise the http method will
1348 // send If-Range requests and there are too many broken servers
1349 // out there that do not understand them
1350 LastGoodSig = DestFile+".reverify";
1351 Rename(Final,LastGoodSig);
1352 }
1353
1354 // we expect the indextargets + one additional Release file
1355 ExpectedAdditionalItems = IndexTargets->size() + 1;
1356
1357 QueueURI(Desc);
1358}
1359 /*}}}*/
1360pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1361{
1362 // if the file was never queued undo file-changes done in the constructor
1363 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false &&
1364 LastGoodSig.empty() == false)
1365 {
1366 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1367 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1368 Rename(LastGoodSig, Final);
1369 }
1370
1371}
1372 /*}}}*/
1373// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1374// ---------------------------------------------------------------------
1375/* The only header we use is the last-modified header. */
1376string pkgAcqMetaSig::Custom600Headers() const
1377{
1378 struct stat Buf;
1379 if (stat(LastGoodSig.c_str(),&Buf) != 0)
1380 return "\nIndex-File: true";
1381
1382 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1383}
1384
1385void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList const &Hashes,
1386 pkgAcquire::MethodConfig *Cfg)
1387{
1388 Item::Done(Message, Size, Hashes, Cfg);
1389
1390 string FileName = LookupTag(Message,"Filename");
1391 if (FileName.empty() == true)
1392 {
1393 Status = StatError;
1394 ErrorText = "Method gave a blank filename";
1395 return;
1396 }
1397
1398 if (FileName != DestFile)
1399 {
1400 // We have to copy it into place
1401 Local = true;
1402 Desc.URI = "copy:" + FileName;
1403 QueueURI(Desc);
1404 return;
1405 }
1406
1407 Complete = true;
1408
1409 // at this point pkgAcqMetaIndex takes over
1410 ExpectedAdditionalItems = 0;
1411
1412 // put the last known good file back on i-m-s hit (it will
1413 // be re-verified again)
1414 // Else do nothing, we have the new file in DestFile then
1415 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1416 Rename(LastGoodSig, DestFile);
1417
1418 // queue for copy
1419 PartialFile = DestFile;
1420 DestFile = _config->FindDir("Dir::State::lists");
1421 DestFile += URItoFileName(RealURI);
1422
1423 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
1424 pkgAcqMetaIndex *metaindex = new pkgAcqMetaIndex(
1425 Owner, MetaIndexURI, MetaIndexURIDesc,
1426 MetaIndexShortDesc, DestFile, IndexTargets,
1427 MetaIndexParser);
1428
1429 TransactionID = (unsigned long)metaindex;
1430}
1431 /*}}}*/
1432void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1433{
1434 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1435
1436 // at this point pkgAcqMetaIndex takes over
1437 ExpectedAdditionalItems = 0;
1438
1439 // if we get a network error we fail gracefully
1440 if(Status == StatTransientNetworkError)
1441 {
1442 Item::Failed(Message,Cnf);
1443 // move the sigfile back on transient network failures
1444 if(FileExists(LastGoodSig))
1445 Rename(LastGoodSig,Final);
1446
1447 // set the status back to , Item::Failed likes to reset it
1448 Status = pkgAcquire::Item::StatTransientNetworkError;
1449 return;
1450 }
1451
1452 // Delete any existing sigfile when the acquire failed
1453 unlink(Final.c_str());
1454
1455 // queue a pkgAcqMetaIndex with no sigfile
1456 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1457 "", IndexTargets, MetaIndexParser);
1458
1459 if (Cnf->LocalOnly == true ||
1460 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1461 {
1462 // Ignore this
1463 Status = StatDone;
1464 Complete = false;
1465 Dequeue();
1466 return;
1467 }
1468
1469 Item::Failed(Message,Cnf);
1470}
1471 /*}}}*/
1472pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1473 string URI,string URIDesc,string ShortDesc,
1474 string SigFile,
1475 const vector<IndexTarget*>* IndexTargets,
1476 indexRecords* MetaIndexParser) :
1477 Item(Owner, HashStringList()), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
1478 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
1479{
1480 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1481 DestFile += URItoFileName(URI);
1482
1483 TransactionID = (unsigned long)this;
1484
1485 // Create the item
1486 Desc.Description = URIDesc;
1487 Desc.Owner = this;
1488 Desc.ShortDesc = ShortDesc;
1489 Desc.URI = URI;
1490
1491 // we expect more item
1492 ExpectedAdditionalItems = IndexTargets->size();
1493
1494 QueueURI(Desc);
1495}
1496 /*}}}*/
1497// pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1498// ---------------------------------------------------------------------
1499/* The only header we use is the last-modified header. */
1500string pkgAcqMetaIndex::Custom600Headers() const
1501{
1502 string Final = _config->FindDir("Dir::State::lists");
1503 Final += URItoFileName(RealURI);
1504
1505 struct stat Buf;
1506 if (stat(Final.c_str(),&Buf) != 0)
1507 return "\nIndex-File: true";
1508
1509 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1510}
1511 /*}}}*/
1512void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
1513 pkgAcquire::MethodConfig *Cfg)
1514{
1515 Item::Done(Message,Size,Hashes,Cfg);
1516
1517 // MetaIndexes are done in two passes: one to download the
1518 // metaindex with an appropriate method, and a second to verify it
1519 // with the gpgv method
1520
1521 if (AuthPass == true)
1522 {
1523 AuthDone(Message);
1524
1525 // all cool, move Release file into place
1526 Complete = true;
1527 }
1528 else
1529 {
1530 RetrievalDone(Message);
1531 if (!Complete)
1532 // Still more retrieving to do
1533 return;
1534
1535 if (SigFile == "")
1536 {
1537 // There was no signature file, so we are finished. Download
1538 // the indexes and do only hashsum verification if possible
1539 MetaIndexParser->Load(DestFile);
1540 QueueIndexes(false);
1541 }
1542 else
1543 {
1544 // FIXME: move this into pkgAcqMetaClearSig::Done on the next
1545 // ABI break
1546
1547 // if we expect a ClearTextSignature (InRelase), ensure that
1548 // this is what we get and if not fail to queue a
1549 // Release/Release.gpg, see #346386
1550 if (SigFile == DestFile && !StartsWithGPGClearTextSignature(DestFile))
1551 {
1552 Failed(Message, Cfg);
1553 return;
1554 }
1555
1556 // There was a signature file, so pass it to gpgv for
1557 // verification
1558 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1559 std::cerr << "Metaindex acquired, queueing gpg verification ("
1560 << SigFile << "," << DestFile << ")\n";
1561 AuthPass = true;
1562 Desc.URI = "gpgv:" + SigFile;
1563 QueueURI(Desc);
1564 Mode = "gpgv";
1565 return;
1566 }
1567 }
1568
1569 if (Complete == true)
1570 {
1571 string FinalFile = _config->FindDir("Dir::State::lists");
1572 FinalFile += URItoFileName(RealURI);
1573 if (SigFile == DestFile)
1574 SigFile = FinalFile;
1575 PartialFile = DestFile;
1576 DestFile = FinalFile;
1577 }
1578}
1579 /*}}}*/
1580void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1581{
1582 // We have just finished downloading a Release file (it is not
1583 // verified yet)
1584
1585 string FileName = LookupTag(Message,"Filename");
1586 if (FileName.empty() == true)
1587 {
1588 Status = StatError;
1589 ErrorText = "Method gave a blank filename";
1590 return;
1591 }
1592
1593 if (FileName != DestFile)
1594 {
1595 Local = true;
1596 Desc.URI = "copy:" + FileName;
1597 QueueURI(Desc);
1598 return;
1599 }
1600
1601 // make sure to verify against the right file on I-M-S hit
1602 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1603 if(IMSHit)
1604 {
1605 string FinalFile = _config->FindDir("Dir::State::lists");
1606 FinalFile += URItoFileName(RealURI);
1607 if (SigFile == DestFile)
1608 {
1609 SigFile = FinalFile;
1610 // constructor of pkgAcqMetaClearSig moved it out of the way,
1611 // now move it back in on IMS hit for the 'old' file
1612 string const OldClearSig = DestFile + ".reverify";
1613 if (RealFileExists(OldClearSig) == true)
1614 Rename(OldClearSig, FinalFile);
1615 }
1616 DestFile = FinalFile;
1617 }
1618 Complete = true;
1619}
1620 /*}}}*/
1621void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1622{
1623 // At this point, the gpgv method has succeeded, so there is a
1624 // valid signature from a key in the trusted keyring. We
1625 // perform additional verification of its contents, and use them
1626 // to verify the indexes we are about to download
1627
1628 if (!MetaIndexParser->Load(DestFile))
1629 {
1630 Status = StatAuthError;
1631 ErrorText = MetaIndexParser->ErrorText;
1632 return;
1633 }
1634
1635 if (!VerifyVendor(Message))
1636 {
1637 return;
1638 }
1639
1640 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1641 std::cerr << "Signature verification succeeded: "
1642 << DestFile << std::endl;
1643
1644 // Download further indexes with verification
1645 QueueIndexes(true);
1646
1647#if 0
1648 // is it a clearsigned MetaIndex file?
1649 if (DestFile == SigFile)
1650 return;
1651
1652 // Done, move signature file into position
1653 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1654 URItoFileName(RealURI) + ".gpg";
1655 Rename(SigFile,VerifiedSigFile);
1656 chmod(VerifiedSigFile.c_str(),0644);
1657#endif
1658}
1659 /*}}}*/
1660void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1661{
1662#if 0
1663 /* Reject invalid, existing Release files (LP: #346386) (Closes: #627642)
1664 * FIXME: Disabled; it breaks unsigned repositories without hashes */
1665 if (!verify && FileExists(DestFile) && !MetaIndexParser->Load(DestFile))
1666 {
1667 Status = StatError;
1668 ErrorText = MetaIndexParser->ErrorText;
1669 return;
1670 }
1671#endif
1672 bool transInRelease = false;
1673 {
1674 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1675 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1676 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1677 if (k->find("Translation-") != std::string::npos)
1678 {
1679 transInRelease = true;
1680 break;
1681 }
1682 }
1683
1684 // at this point the real Items are loaded in the fetcher
1685 ExpectedAdditionalItems = 0;
1686 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1687 Target != IndexTargets->end();
1688 ++Target)
1689 {
1690 HashStringList ExpectedIndexHashes;
1691 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1692 bool compressedAvailable = false;
1693 if (Record == NULL)
1694 {
1695 if ((*Target)->IsOptional() == true)
1696 {
1697 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1698 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1699 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1700 {
1701 compressedAvailable = true;
1702 break;
1703 }
1704 }
1705 else if (verify == true)
1706 {
1707 Status = StatAuthError;
1708 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1709 return;
1710 }
1711 }
1712 else
1713 {
1714 ExpectedIndexHashes = Record->Hashes;
1715 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1716 {
1717 std::cerr << "Queueing: " << (*Target)->URI << std::endl
1718 << "Expected Hash:" << std::endl;
1719 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
1720 std::cerr << "\t- " << hs->toStr() << std::endl;
1721 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
1722 }
1723 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
1724 {
1725 Status = StatAuthError;
1726 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1727 return;
1728 }
1729 }
1730
1731 if ((*Target)->IsOptional() == true)
1732 {
1733 if ((*Target)->IsSubIndex() == true)
1734 new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
1735 (*Target)->ShortDesc, ExpectedIndexHashes);
1736 else if (transInRelease == false || Record != NULL || compressedAvailable == true)
1737 {
1738 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
1739 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
1740 new pkgAcqDiffIndex(this, *Target, ExpectedIndexHashes, MetaIndexParser);
1741 else
1742 new pkgAcqIndexTrans(this, *Target, ExpectedIndexHashes, MetaIndexParser);
1743 }
1744 continue;
1745 }
1746
1747 /* Queue Packages file (either diff or full packages files, depending
1748 on the users option) - we also check if the PDiff Index file is listed
1749 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1750 instead, but passing the required info to it is to much hassle */
1751 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1752 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
1753 new pkgAcqDiffIndex(this, *Target, ExpectedIndexHashes, MetaIndexParser);
1754 else
1755 new pkgAcqIndex(this, *Target, ExpectedIndexHashes, MetaIndexParser);
1756 }
1757}
1758 /*}}}*/
1759bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1760{
1761 string::size_type pos;
1762
1763 // check for missing sigs (that where not fatal because otherwise we had
1764 // bombed earlier)
1765 string missingkeys;
1766 string msg = _("There is no public key available for the "
1767 "following key IDs:\n");
1768 pos = Message.find("NO_PUBKEY ");
1769 if (pos != std::string::npos)
1770 {
1771 string::size_type start = pos+strlen("NO_PUBKEY ");
1772 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1773 missingkeys += (Fingerprint);
1774 }
1775 if(!missingkeys.empty())
1776 _error->Warning("%s", (msg + missingkeys).c_str());
1777
1778 string Transformed = MetaIndexParser->GetExpectedDist();
1779
1780 if (Transformed == "../project/experimental")
1781 {
1782 Transformed = "experimental";
1783 }
1784
1785 pos = Transformed.rfind('/');
1786 if (pos != string::npos)
1787 {
1788 Transformed = Transformed.substr(0, pos);
1789 }
1790
1791 if (Transformed == ".")
1792 {
1793 Transformed = "";
1794 }
1795
1796 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1797 MetaIndexParser->GetValidUntil() > 0) {
1798 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1799 if (invalid_since > 0)
1800 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1801 // the time since then the file is invalid - formated in the same way as in
1802 // the download progress display (e.g. 7d 3h 42min 1s)
1803 return _error->Error(
1804 _("Release file for %s is expired (invalid since %s). "
1805 "Updates for this repository will not be applied."),
1806 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1807 }
1808
1809 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1810 {
1811 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1812 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1813 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1814 }
1815
1816 if (MetaIndexParser->CheckDist(Transformed) == false)
1817 {
1818 // This might become fatal one day
1819// Status = StatAuthError;
1820// ErrorText = "Conflicting distribution; expected "
1821// + MetaIndexParser->GetExpectedDist() + " but got "
1822// + MetaIndexParser->GetDist();
1823// return false;
1824 if (!Transformed.empty())
1825 {
1826 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1827 Desc.Description.c_str(),
1828 Transformed.c_str(),
1829 MetaIndexParser->GetDist().c_str());
1830 }
1831 }
1832
1833 return true;
1834}
1835 /*}}}*/
1836// pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1837// ---------------------------------------------------------------------
1838/* */
1839void pkgAcqMetaIndex::Failed(string /*Message*/,
1840 pkgAcquire::MethodConfig * /*Cnf*/)
1841{
1842#if 0
1843 if (AuthPass == true)
1844 {
1845 // gpgv method failed, if we have a good signature
1846 string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
1847 if (DestFile != SigFile)
1848 LastGoodSigFile.append(".gpg");
1849 LastGoodSigFile.append(".reverify");
1850
1851 if(FileExists(LastGoodSigFile))
1852 {
1853 string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1854 if (DestFile != SigFile)
1855 VerifiedSigFile.append(".gpg");
1856 Rename(LastGoodSigFile, VerifiedSigFile);
1857 Status = StatTransientNetworkError;
1858 _error->Warning(_("An error occurred during the signature "
1859 "verification. The repository is not updated "
1860 "and the previous index files will be used. "
1861 "GPG error: %s: %s\n"),
1862 Desc.Description.c_str(),
1863 LookupTag(Message,"Message").c_str());
1864 RunScripts("APT::Update::Auth-Failure");
1865 return;
1866 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1867 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1868 _error->Error(_("GPG error: %s: %s"),
1869 Desc.Description.c_str(),
1870 LookupTag(Message,"Message").c_str());
1871 return;
1872 } else {
1873 _error->Warning(_("GPG error: %s: %s"),
1874 Desc.Description.c_str(),
1875 LookupTag(Message,"Message").c_str());
1876 }
1877 // gpgv method failed
1878 ReportMirrorFailure("GPGFailure");
1879 }
1880#endif
1881 /* Always move the meta index, even if gpgv failed. This ensures
1882 * that PackageFile objects are correctly filled in */
1883 if (FileExists(DestFile)) {
1884 string FinalFile = _config->FindDir("Dir::State::lists");
1885 FinalFile += URItoFileName(RealURI);
1886 /* InRelease files become Release files, otherwise
1887 * they would be considered as trusted later on */
1888 if (SigFile == DestFile) {
1889 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
1890 "Release");
1891 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
1892 "Release");
1893 SigFile = FinalFile;
1894 }
1895 Rename(DestFile,FinalFile);
1896 chmod(FinalFile.c_str(),0644);
1897
1898 DestFile = FinalFile;
1899 }
1900
1901 // No Release file was present, or verification failed, so fall
1902 // back to queueing Packages files without verification
1903 QueueIndexes(false);
1904}
1905 /*}}}*/
1906
1907void pkgAcqMetaIndex::Finished()
1908{
1909 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1910 std::clog << "Finished: " << DestFile <<std::endl;
1911 if(Owner->TransactionHasError((unsigned long)this) == false)
1912 Owner->CommitTransaction((unsigned long)this);
1913}
1914
1915
1916pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
1917 string const &URI, string const &URIDesc, string const &ShortDesc,
1918 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
1919 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
1920 const vector<IndexTarget*>* IndexTargets,
1921 indexRecords* MetaIndexParser) :
1922 pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
1923 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1924 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
1925{
1926 SigFile = DestFile;
1927
1928 // index targets + (worst case:) Release/Release.gpg
1929 ExpectedAdditionalItems = IndexTargets->size() + 2;
1930
1931
1932 // keep the old InRelease around in case of transistent network errors
1933 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1934 if (RealFileExists(Final) == true)
1935 {
1936 string const LastGoodSig = DestFile + ".reverify";
1937 Rename(Final,LastGoodSig);
1938 }
1939}
1940 /*}}}*/
1941pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1942{
1943 // if the file was never queued undo file-changes done in the constructor
1944 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
1945 {
1946 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1947 string const LastGoodSig = DestFile + ".reverify";
1948 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1949 Rename(LastGoodSig, Final);
1950 }
1951}
1952 /*}}}*/
1953// pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1954// ---------------------------------------------------------------------
1955// FIXME: this can go away once the InRelease file is used widely
1956string pkgAcqMetaClearSig::Custom600Headers() const
1957{
1958 string Final = _config->FindDir("Dir::State::lists");
1959 Final += URItoFileName(RealURI);
1960
1961 struct stat Buf;
1962 if (stat(Final.c_str(),&Buf) != 0)
1963 {
1964 Final = DestFile + ".reverify";
1965 if (stat(Final.c_str(),&Buf) != 0)
1966 return "\nIndex-File: true\nFail-Ignore: true\n";
1967 }
1968
1969 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1970}
1971 /*}}}*/
1972void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1973{
1974 // we failed, we will not get additional items from this method
1975 ExpectedAdditionalItems = 0;
1976
1977 if (AuthPass == false)
1978 {
1979 // Remove the 'old' InRelease file if we try Release.gpg now as otherwise
1980 // the file will stay around and gives a false-auth impression (CVE-2012-0214)
1981 string FinalFile = _config->FindDir("Dir::State::lists");
1982 FinalFile.append(URItoFileName(RealURI));
1983 if (FileExists(FinalFile))
1984 unlink(FinalFile.c_str());
1985
1986 new pkgAcqMetaSig(Owner,
1987 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
1988 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1989 IndexTargets, MetaIndexParser);
1990 if (Cnf->LocalOnly == true ||
1991 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
1992 Dequeue();
1993 }
1994 else
1995 pkgAcqMetaIndex::Failed(Message, Cnf);
1996}
1997 /*}}}*/
1998// AcqArchive::AcqArchive - Constructor /*{{{*/
1999// ---------------------------------------------------------------------
2000/* This just sets up the initial fetch environment and queues the first
2001 possibilitiy */
2002pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
2003 pkgRecords *Recs,pkgCache::VerIterator const &Version,
2004 string &StoreFilename) :
2005 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
2006 StoreFilename(StoreFilename), Vf(Version.FileList()),
2007 Trusted(false)
2008{
2009 Retries = _config->FindI("Acquire::Retries",0);
2010
2011 if (Version.Arch() == 0)
2012 {
2013 _error->Error(_("I wasn't able to locate a file for the %s package. "
2014 "This might mean you need to manually fix this package. "
2015 "(due to missing arch)"),
2016 Version.ParentPkg().FullName().c_str());
2017 return;
2018 }
2019
2020 /* We need to find a filename to determine the extension. We make the
2021 assumption here that all the available sources for this version share
2022 the same extension.. */
2023 // Skip not source sources, they do not have file fields.
2024 for (; Vf.end() == false; ++Vf)
2025 {
2026 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2027 continue;
2028 break;
2029 }
2030
2031 // Does not really matter here.. we are going to fail out below
2032 if (Vf.end() != true)
2033 {
2034 // If this fails to get a file name we will bomb out below.
2035 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2036 if (_error->PendingError() == true)
2037 return;
2038
2039 // Generate the final file name as: package_version_arch.foo
2040 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2041 QuoteString(Version.VerStr(),"_:") + '_' +
2042 QuoteString(Version.Arch(),"_:.") +
2043 "." + flExtension(Parse.FileName());
2044 }
2045
2046 // check if we have one trusted source for the package. if so, switch
2047 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2048 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2049 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2050 bool seenUntrusted = false;
2051 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2052 {
2053 pkgIndexFile *Index;
2054 if (Sources->FindIndex(i.File(),Index) == false)
2055 continue;
2056
2057 if (debugAuth == true)
2058 std::cerr << "Checking index: " << Index->Describe()
2059 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2060
2061 if (Index->IsTrusted() == true)
2062 {
2063 Trusted = true;
2064 if (allowUnauth == false)
2065 break;
2066 }
2067 else
2068 seenUntrusted = true;
2069 }
2070
2071 // "allow-unauthenticated" restores apts old fetching behaviour
2072 // that means that e.g. unauthenticated file:// uris are higher
2073 // priority than authenticated http:// uris
2074 if (allowUnauth == true && seenUntrusted == true)
2075 Trusted = false;
2076
2077 // Select a source
2078 if (QueueNext() == false && _error->PendingError() == false)
2079 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2080 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2081}
2082 /*}}}*/
2083// AcqArchive::QueueNext - Queue the next file source /*{{{*/
2084// ---------------------------------------------------------------------
2085/* This queues the next available file version for download. It checks if
2086 the archive is already available in the cache and stashs the MD5 for
2087 checking later. */
2088bool pkgAcqArchive::QueueNext()
2089{
2090 for (; Vf.end() == false; ++Vf)
2091 {
2092 // Ignore not source sources
2093 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2094 continue;
2095
2096 // Try to cross match against the source list
2097 pkgIndexFile *Index;
2098 if (Sources->FindIndex(Vf.File(),Index) == false)
2099 continue;
2100
2101 // only try to get a trusted package from another source if that source
2102 // is also trusted
2103 if(Trusted && !Index->IsTrusted())
2104 continue;
2105
2106 // Grab the text package record
2107 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2108 if (_error->PendingError() == true)
2109 return false;
2110
2111 string PkgFile = Parse.FileName();
2112 ExpectedHashes = Parse.Hashes();
2113
2114 if (PkgFile.empty() == true)
2115 return _error->Error(_("The package index files are corrupted. No Filename: "
2116 "field for package %s."),
2117 Version.ParentPkg().Name());
2118
2119 Desc.URI = Index->ArchiveURI(PkgFile);
2120 Desc.Description = Index->ArchiveInfo(Version);
2121 Desc.Owner = this;
2122 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2123
2124 // See if we already have the file. (Legacy filenames)
2125 FileSize = Version->Size;
2126 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2127 struct stat Buf;
2128 if (stat(FinalFile.c_str(),&Buf) == 0)
2129 {
2130 // Make sure the size matches
2131 if ((unsigned long long)Buf.st_size == Version->Size)
2132 {
2133 Complete = true;
2134 Local = true;
2135 Status = StatDone;
2136 StoreFilename = DestFile = FinalFile;
2137 return true;
2138 }
2139
2140 /* Hmm, we have a file and its size does not match, this means it is
2141 an old style mismatched arch */
2142 unlink(FinalFile.c_str());
2143 }
2144
2145 // Check it again using the new style output filenames
2146 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2147 if (stat(FinalFile.c_str(),&Buf) == 0)
2148 {
2149 // Make sure the size matches
2150 if ((unsigned long long)Buf.st_size == Version->Size)
2151 {
2152 Complete = true;
2153 Local = true;
2154 Status = StatDone;
2155 StoreFilename = DestFile = FinalFile;
2156 return true;
2157 }
2158
2159 /* Hmm, we have a file and its size does not match, this shouldn't
2160 happen.. */
2161 unlink(FinalFile.c_str());
2162 }
2163
2164 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2165
2166 // Check the destination file
2167 if (stat(DestFile.c_str(),&Buf) == 0)
2168 {
2169 // Hmm, the partial file is too big, erase it
2170 if ((unsigned long long)Buf.st_size > Version->Size)
2171 unlink(DestFile.c_str());
2172 else
2173 PartialSize = Buf.st_size;
2174 }
2175
2176 // Disables download of archives - useful if no real installation follows,
2177 // e.g. if we are just interested in proposed installation order
2178 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2179 {
2180 Complete = true;
2181 Local = true;
2182 Status = StatDone;
2183 StoreFilename = DestFile = FinalFile;
2184 return true;
2185 }
2186
2187 // Create the item
2188 Local = false;
2189 QueueURI(Desc);
2190
2191 ++Vf;
2192 return true;
2193 }
2194 return false;
2195}
2196 /*}}}*/
2197// AcqArchive::Done - Finished fetching /*{{{*/
2198// ---------------------------------------------------------------------
2199/* */
2200void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2201 pkgAcquire::MethodConfig *Cfg)
2202{
2203 Item::Done(Message, Size, CalcHashes, Cfg);
2204
2205 // Check the size
2206 if (Size != Version->Size)
2207 {
2208 RenameOnError(SizeMismatch);
2209 return;
2210 }
2211
2212 // FIXME: could this empty() check impose *any* sort of security issue?
2213 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2214 {
2215 RenameOnError(HashSumMismatch);
2216 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2217 return;
2218 }
2219
2220 // Grab the output filename
2221 string FileName = LookupTag(Message,"Filename");
2222 if (FileName.empty() == true)
2223 {
2224 Status = StatError;
2225 ErrorText = "Method gave a blank filename";
2226 return;
2227 }
2228
2229 Complete = true;
2230
2231 // Reference filename
2232 if (FileName != DestFile)
2233 {
2234 StoreFilename = DestFile = FileName;
2235 Local = true;
2236 return;
2237 }
2238
2239 // Done, move it into position
2240 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2241 FinalFile += flNotDir(StoreFilename);
2242 Rename(DestFile,FinalFile);
2243
2244 StoreFilename = DestFile = FinalFile;
2245 Complete = true;
2246}
2247 /*}}}*/
2248// AcqArchive::Failed - Failure handler /*{{{*/
2249// ---------------------------------------------------------------------
2250/* Here we try other sources */
2251void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2252{
2253 ErrorText = LookupTag(Message,"Message");
2254
2255 /* We don't really want to retry on failed media swaps, this prevents
2256 that. An interesting observation is that permanent failures are not
2257 recorded. */
2258 if (Cnf->Removable == true &&
2259 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2260 {
2261 // Vf = Version.FileList();
2262 while (Vf.end() == false) ++Vf;
2263 StoreFilename = string();
2264 Item::Failed(Message,Cnf);
2265 return;
2266 }
2267
2268 if (QueueNext() == false)
2269 {
2270 // This is the retry counter
2271 if (Retries != 0 &&
2272 Cnf->LocalOnly == false &&
2273 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2274 {
2275 Retries--;
2276 Vf = Version.FileList();
2277 if (QueueNext() == true)
2278 return;
2279 }
2280
2281 StoreFilename = string();
2282 Item::Failed(Message,Cnf);
2283 }
2284}
2285 /*}}}*/
2286// AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2287// ---------------------------------------------------------------------
2288APT_PURE bool pkgAcqArchive::IsTrusted() const
2289{
2290 return Trusted;
2291}
2292 /*}}}*/
2293// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2294// ---------------------------------------------------------------------
2295/* */
2296void pkgAcqArchive::Finished()
2297{
2298 if (Status == pkgAcquire::Item::StatDone &&
2299 Complete == true)
2300 return;
2301 StoreFilename = string();
2302}
2303 /*}}}*/
2304// AcqFile::pkgAcqFile - Constructor /*{{{*/
2305// ---------------------------------------------------------------------
2306/* The file is added to the queue */
2307pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2308 unsigned long long Size,string Dsc,string ShortDesc,
2309 const string &DestDir, const string &DestFilename,
2310 bool IsIndexFile) :
2311 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2312{
2313 Retries = _config->FindI("Acquire::Retries",0);
2314
2315 if(!DestFilename.empty())
2316 DestFile = DestFilename;
2317 else if(!DestDir.empty())
2318 DestFile = DestDir + "/" + flNotDir(URI);
2319 else
2320 DestFile = flNotDir(URI);
2321
2322 // Create the item
2323 Desc.URI = URI;
2324 Desc.Description = Dsc;
2325 Desc.Owner = this;
2326
2327 // Set the short description to the archive component
2328 Desc.ShortDesc = ShortDesc;
2329
2330 // Get the transfer sizes
2331 FileSize = Size;
2332 struct stat Buf;
2333 if (stat(DestFile.c_str(),&Buf) == 0)
2334 {
2335 // Hmm, the partial file is too big, erase it
2336 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2337 unlink(DestFile.c_str());
2338 else
2339 PartialSize = Buf.st_size;
2340 }
2341
2342 QueueURI(Desc);
2343}
2344 /*}}}*/
2345// AcqFile::Done - Item downloaded OK /*{{{*/
2346// ---------------------------------------------------------------------
2347/* */
2348void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2349 pkgAcquire::MethodConfig *Cnf)
2350{
2351 Item::Done(Message,Size,CalcHashes,Cnf);
2352
2353 // Check the hash
2354 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2355 {
2356 RenameOnError(HashSumMismatch);
2357 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2358 return;
2359 }
2360
2361 string FileName = LookupTag(Message,"Filename");
2362 if (FileName.empty() == true)
2363 {
2364 Status = StatError;
2365 ErrorText = "Method gave a blank filename";
2366 return;
2367 }
2368
2369 Complete = true;
2370
2371 // The files timestamp matches
2372 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2373 return;
2374
2375 // We have to copy it into place
2376 if (FileName != DestFile)
2377 {
2378 Local = true;
2379 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2380 Cnf->Removable == true)
2381 {
2382 Desc.URI = "copy:" + FileName;
2383 QueueURI(Desc);
2384 return;
2385 }
2386
2387 // Erase the file if it is a symlink so we can overwrite it
2388 struct stat St;
2389 if (lstat(DestFile.c_str(),&St) == 0)
2390 {
2391 if (S_ISLNK(St.st_mode) != 0)
2392 unlink(DestFile.c_str());
2393 }
2394
2395 // Symlink the file
2396 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2397 {
2398 ErrorText = "Link to " + DestFile + " failure ";
2399 Status = StatError;
2400 Complete = false;
2401 }
2402 }
2403}
2404 /*}}}*/
2405// AcqFile::Failed - Failure handler /*{{{*/
2406// ---------------------------------------------------------------------
2407/* Here we try other sources */
2408void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2409{
2410 ErrorText = LookupTag(Message,"Message");
2411
2412 // This is the retry counter
2413 if (Retries != 0 &&
2414 Cnf->LocalOnly == false &&
2415 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2416 {
2417 Retries--;
2418 QueueURI(Desc);
2419 return;
2420 }
2421
2422 Item::Failed(Message,Cnf);
2423}
2424 /*}}}*/
2425// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2426// ---------------------------------------------------------------------
2427/* The only header we use is the last-modified header. */
2428string pkgAcqFile::Custom600Headers() const
2429{
2430 if (IsIndexFile)
2431 return "\nIndex-File: true";
2432 return "";
2433}
2434 /*}}}*/