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