]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
promote ExpectedHashes to a pkgAcquire::Item member
[apt.git] / apt-pkg / acquire-item.cc
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
51 using namespace std;
52
53 static 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 pkgAcquire::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), ExpectedAdditionalItems(0),
70 ExpectedHashes(ExpectedHashes)
71 {
72 Owner->Add(this);
73 Status = StatIdle;
74 }
75 /*}}}*/
76 // Acquire::Item::~Item - Destructor /*{{{*/
77 // ---------------------------------------------------------------------
78 /* */
79 pkgAcquire::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 */
88 void 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 */
122 void 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 /* */
132 void 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 */
155 void 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 /*}}}*/
167 bool 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 // ---------------------------------------------------------------------
195 void 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 */
241 pkgAcqSubIndex::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. */
266 string 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 /*}}}*/
277 void 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 /*}}}*/
289 void 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 /*}}}*/
340 bool 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 */
356 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
357 IndexTarget const * const Target,
358 HashStringList const &ExpectedHashes,
359 indexRecords *MetaIndexParser)
360 : Item(Owner, ExpectedHashes), Target(Target),
361 MetaIndexParser(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. */
406 string 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 /*}}}*/
421 bool 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(Owner, RealURI, Description, Desc.ShortDesc,
460 ExpectedHashes, 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 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
546 ExpectedHashes, ServerSha1, available_patches);
547 else
548 {
549 std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
550 for(size_t i = 0; i < available_patches.size(); ++i)
551 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, RealURI, Description, Desc.ShortDesc, ExpectedHashes,
552 available_patches[i], diffs);
553 }
554
555 Complete = false;
556 Status = StatDone;
557 Dequeue();
558 return true;
559 }
560 }
561
562 // Nothing found, report and return false
563 // Failing here is ok, if we return false later, the full
564 // IndexFile is queued
565 if(Debug)
566 std::clog << "Can't find a patch in the index file" << std::endl;
567 return false;
568 }
569 /*}}}*/
570 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
571 {
572 if(Debug)
573 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
574 << "Falling back to normal index file acquire" << std::endl;
575
576 new pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser);
577
578 Complete = false;
579 Status = StatDone;
580 Dequeue();
581 }
582 /*}}}*/
583 void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
584 pkgAcquire::MethodConfig *Cnf)
585 {
586 if(Debug)
587 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
588
589 Item::Done(Message, Size, Hashes, Cnf);
590
591 string FinalFile;
592 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
593
594 // success in downloading the index
595 // rename the index
596 FinalFile += string(".IndexDiff");
597 if(Debug)
598 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
599 << std::endl;
600 Rename(DestFile,FinalFile);
601 chmod(FinalFile.c_str(),0644);
602 DestFile = FinalFile;
603
604 if(!ParseDiffIndex(DestFile))
605 return Failed("", NULL);
606
607 Complete = true;
608 Status = StatDone;
609 Dequeue();
610 return;
611 }
612 /*}}}*/
613 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
614 // ---------------------------------------------------------------------
615 /* The package diff is added to the queue. one object is constructed
616 * for each diff and the index
617 */
618 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
619 string URI,string URIDesc,string ShortDesc,
620 HashStringList const &ExpectedHashes,
621 string ServerSha1,
622 vector<DiffInfo> diffs)
623 : Item(Owner, ExpectedHashes), RealURI(URI),
624 available_patches(diffs), ServerSha1(ServerSha1)
625 {
626
627 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
628 DestFile += URItoFileName(URI);
629
630 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
631
632 Description = URIDesc;
633 Desc.Owner = this;
634 Desc.ShortDesc = ShortDesc;
635
636 if(available_patches.empty() == true)
637 {
638 // we are done (yeah!)
639 Finish(true);
640 }
641 else
642 {
643 // get the next diff
644 State = StateFetchDiff;
645 QueueNextDiff();
646 }
647 }
648 /*}}}*/
649 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
650 {
651 if(Debug)
652 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
653 << "Falling back to normal index file acquire" << std::endl;
654 new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc, HashSums());
655 Finish();
656 }
657 /*}}}*/
658 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
659 void pkgAcqIndexDiffs::Finish(bool allDone)
660 {
661 // we restore the original name, this is required, otherwise
662 // the file will be cleaned
663 if(allDone)
664 {
665 DestFile = _config->FindDir("Dir::State::lists");
666 DestFile += URItoFileName(RealURI);
667
668 if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
669 {
670 RenameOnError(HashSumMismatch);
671 Dequeue();
672 return;
673 }
674
675 // this is for the "real" finish
676 Complete = true;
677 Status = StatDone;
678 Dequeue();
679 if(Debug)
680 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
681 return;
682 }
683
684 if(Debug)
685 std::clog << "Finishing: " << Desc.URI << std::endl;
686 Complete = false;
687 Status = StatDone;
688 Dequeue();
689 return;
690 }
691 /*}}}*/
692 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
693 {
694
695 // calc sha1 of the just patched file
696 string FinalFile = _config->FindDir("Dir::State::lists");
697 FinalFile += URItoFileName(RealURI);
698
699 FileFd fd(FinalFile, FileFd::ReadOnly);
700 SHA1Summation SHA1;
701 SHA1.AddFD(fd);
702 string local_sha1 = string(SHA1.Result());
703 if(Debug)
704 std::clog << "QueueNextDiff: "
705 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
706
707 // final file reached before all patches are applied
708 if(local_sha1 == ServerSha1)
709 {
710 Finish(true);
711 return true;
712 }
713
714 // remove all patches until the next matching patch is found
715 // this requires the Index file to be ordered
716 for(vector<DiffInfo>::iterator I=available_patches.begin();
717 available_patches.empty() == false &&
718 I != available_patches.end() &&
719 I->sha1 != local_sha1;
720 ++I)
721 {
722 available_patches.erase(I);
723 }
724
725 // error checking and falling back if no patch was found
726 if(available_patches.empty() == true)
727 {
728 Failed("", NULL);
729 return false;
730 }
731
732 // queue the right diff
733 Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz";
734 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
735 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
736 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
737
738 if(Debug)
739 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
740
741 QueueURI(Desc);
742
743 return true;
744 }
745 /*}}}*/
746 void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringList const &Hashes, /*{{{*/
747 pkgAcquire::MethodConfig *Cnf)
748 {
749 if(Debug)
750 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
751
752 Item::Done(Message, Size, Hashes, Cnf);
753
754 string FinalFile;
755 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
756
757 // success in downloading a diff, enter ApplyDiff state
758 if(State == StateFetchDiff)
759 {
760
761 // rred excepts the patch as $FinalFile.ed
762 Rename(DestFile,FinalFile+".ed");
763
764 if(Debug)
765 std::clog << "Sending to rred method: " << FinalFile << std::endl;
766
767 State = StateApplyDiff;
768 Local = true;
769 Desc.URI = "rred:" + FinalFile;
770 QueueURI(Desc);
771 Mode = "rred";
772 return;
773 }
774
775
776 // success in download/apply a diff, queue next (if needed)
777 if(State == StateApplyDiff)
778 {
779 // remove the just applied patch
780 available_patches.erase(available_patches.begin());
781 unlink((FinalFile + ".ed").c_str());
782
783 // move into place
784 if(Debug)
785 {
786 std::clog << "Moving patched file in place: " << std::endl
787 << DestFile << " -> " << FinalFile << std::endl;
788 }
789 Rename(DestFile,FinalFile);
790 chmod(FinalFile.c_str(),0644);
791
792 // see if there is more to download
793 if(available_patches.empty() == false) {
794 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
795 HashSums(), ServerSha1, available_patches);
796 return Finish();
797 } else
798 return Finish(true);
799 }
800 }
801 /*}}}*/
802 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
803 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
804 string const &URI, string const &URIDesc,
805 string const &ShortDesc, HashStringList const &ExpectedHashes,
806 DiffInfo const &patch,
807 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
808 : Item(Owner, ExpectedHashes), RealURI(URI),
809 patch(patch),allPatches(allPatches), State(StateFetchDiff)
810 {
811
812 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
813 DestFile += URItoFileName(URI);
814
815 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
816
817 Description = URIDesc;
818 Desc.Owner = this;
819 Desc.ShortDesc = ShortDesc;
820
821 Desc.URI = RealURI + ".diff/" + patch.file + ".gz";
822 Desc.Description = Description + " " + patch.file + string(".pdiff");
823 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
824 DestFile += URItoFileName(RealURI + ".diff/" + patch.file);
825
826 if(Debug)
827 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
828
829 QueueURI(Desc);
830 }
831 /*}}}*/
832 void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
833 {
834 if(Debug)
835 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
836 Complete = false;
837 Status = StatDone;
838 Dequeue();
839
840 // check if we are the first to fail, otherwise we are done here
841 State = StateDoneDiff;
842 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
843 I != allPatches->end(); ++I)
844 if ((*I)->State == StateErrorDiff)
845 return;
846
847 // first failure means we should fallback
848 State = StateErrorDiff;
849 std::clog << "Falling back to normal index file acquire" << std::endl;
850 new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
851 ExpectedHashes);
852 }
853 /*}}}*/
854 void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
855 pkgAcquire::MethodConfig *Cnf)
856 {
857 if(Debug)
858 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
859
860 Item::Done(Message,Size,Hashes,Cnf);
861
862 string const FinalFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
863
864 if (State == StateFetchDiff)
865 {
866 // rred expects the patch as $FinalFile.ed.$patchname.gz
867 Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
868
869 // check if this is the last completed diff
870 State = StateDoneDiff;
871 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
872 I != allPatches->end(); ++I)
873 if ((*I)->State != StateDoneDiff)
874 {
875 if(Debug)
876 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
877 return;
878 }
879
880 // this is the last completed diff, so we are ready to apply now
881 State = StateApplyDiff;
882
883 if(Debug)
884 std::clog << "Sending to rred method: " << FinalFile << std::endl;
885
886 Local = true;
887 Desc.URI = "rred:" + FinalFile;
888 QueueURI(Desc);
889 Mode = "rred";
890 return;
891 }
892 // success in download/apply all diffs, clean up
893 else if (State == StateApplyDiff)
894 {
895 // see if we really got the expected file
896 if(ExpectedHashes.usable() && !ExpectedHashes.VerifyFile(DestFile))
897 {
898 RenameOnError(HashSumMismatch);
899 return;
900 }
901
902 // move the result into place
903 if(Debug)
904 std::clog << "Moving patched file in place: " << std::endl
905 << DestFile << " -> " << FinalFile << std::endl;
906 Rename(DestFile, FinalFile);
907 chmod(FinalFile.c_str(), 0644);
908
909 // otherwise lists cleanup will eat the file
910 DestFile = FinalFile;
911
912 // ensure the ed's are gone regardless of list-cleanup
913 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
914 I != allPatches->end(); ++I)
915 {
916 std::string patch = FinalFile + ".ed." + (*I)->patch.file + ".gz";
917 unlink(patch.c_str());
918 }
919
920 // all set and done
921 Complete = true;
922 if(Debug)
923 std::clog << "allDone: " << DestFile << "\n" << std::endl;
924 }
925 }
926 /*}}}*/
927 // AcqIndex::AcqIndex - Constructor /*{{{*/
928 // ---------------------------------------------------------------------
929 /* The package file is added to the queue and a second class is
930 instantiated to fetch the revision file */
931 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
932 string URI,string URIDesc,string ShortDesc,
933 HashStringList const &ExpectedHashes, string comprExt)
934 : Item(Owner, ExpectedHashes), RealURI(URI), Target(0),
935 MetaIndexParser(0)
936 {
937 if(comprExt.empty() == true)
938 {
939 // autoselect the compression method
940 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
941 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
942 comprExt.append(*t).append(" ");
943 if (comprExt.empty() == false)
944 comprExt.erase(comprExt.end()-1);
945 }
946 CompressionExtension = comprExt;
947
948 Verify = true;
949
950 Init(URI, URIDesc, ShortDesc);
951 }
952 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const * const Target,
953 HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser)
954 : Item(Owner, ExpectedHashes), RealURI(Target->URI)
955 {
956 // autoselect the compression method
957 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
958 CompressionExtension = "";
959 if (ExpectedHashes.usable())
960 {
961 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
962 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
963 CompressionExtension.append(*t).append(" ");
964 }
965 else
966 {
967 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
968 CompressionExtension.append(*t).append(" ");
969 }
970 if (CompressionExtension.empty() == false)
971 CompressionExtension.erase(CompressionExtension.end()-1);
972
973 // only verify non-optional targets, see acquire-item.h for a FIXME
974 // to make this more flexible
975 if (Target->IsOptional())
976 Verify = false;
977 else
978 Verify = true;
979
980 // we need this in Init()
981 this->Target = Target;
982 this->MetaIndexParser = MetaIndexParser;
983
984 Init(Target->URI, Target->Description, Target->ShortDesc);
985 }
986 /*}}}*/
987 // AcqIndex::Init - defered Constructor /*{{{*/
988 void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) {
989 Decompression = false;
990 Erase = false;
991
992 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
993 DestFile += URItoFileName(URI);
994
995 std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
996 std::string MetaKey;
997 if (comprExt == "uncompressed")
998 {
999 Desc.URI = URI;
1000 if(Target)
1001 MetaKey = string(Target->MetaKey);
1002 }
1003 else
1004 {
1005 Desc.URI = URI + '.' + comprExt;
1006 if(Target)
1007 MetaKey = string(Target->MetaKey) + '.' + comprExt;
1008 }
1009
1010 // load the filesize
1011 if(MetaIndexParser)
1012 {
1013 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1014 if(Record)
1015 FileSize = Record->Size;
1016 }
1017
1018 Desc.Description = URIDesc;
1019 Desc.Owner = this;
1020 Desc.ShortDesc = ShortDesc;
1021
1022 QueueURI(Desc);
1023 }
1024 /*}}}*/
1025 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1026 // ---------------------------------------------------------------------
1027 /* The only header we use is the last-modified header. */
1028 string pkgAcqIndex::Custom600Headers() const
1029 {
1030 string Final = _config->FindDir("Dir::State::lists");
1031 Final += URItoFileName(RealURI);
1032 if (_config->FindB("Acquire::GzipIndexes",false))
1033 Final += ".gz";
1034
1035 string msg = "\nIndex-File: true";
1036 // FIXME: this really should use "IndexTarget::IsOptional()" but that
1037 // seems to be difficult without breaking ABI
1038 if (ShortDesc().find("Translation") != 0)
1039 msg += "\nFail-Ignore: true";
1040 struct stat Buf;
1041 if (stat(Final.c_str(),&Buf) == 0)
1042 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1043
1044 return msg;
1045 }
1046 /*}}}*/
1047 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1048 {
1049 size_t const nextExt = CompressionExtension.find(' ');
1050 if (nextExt != std::string::npos)
1051 {
1052 CompressionExtension = CompressionExtension.substr(nextExt+1);
1053 Init(RealURI, Desc.Description, Desc.ShortDesc);
1054 return;
1055 }
1056
1057 // on decompression failure, remove bad versions in partial/
1058 if (Decompression && Erase) {
1059 string s = _config->FindDir("Dir::State::lists") + "partial/";
1060 s.append(URItoFileName(RealURI));
1061 unlink(s.c_str());
1062 }
1063
1064 Item::Failed(Message,Cnf);
1065 }
1066 /*}}}*/
1067 // AcqIndex::Done - Finished a fetch /*{{{*/
1068 // ---------------------------------------------------------------------
1069 /* This goes through a number of states.. On the initial fetch the
1070 method could possibly return an alternate filename which points
1071 to the uncompressed version of the file. If this is so the file
1072 is copied into the partial directory. In all other cases the file
1073 is decompressed with a gzip uri. */
1074 void pkgAcqIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes,
1075 pkgAcquire::MethodConfig *Cfg)
1076 {
1077 Item::Done(Message,Size,Hashes,Cfg);
1078
1079 if (Decompression == true)
1080 {
1081 if (ExpectedHashes.usable() && ExpectedHashes != Hashes)
1082 {
1083 RenameOnError(HashSumMismatch);
1084 printHashSumComparision(RealURI, ExpectedHashes, Hashes);
1085 return;
1086 }
1087
1088 /* Verify the index file for correctness (all indexes must
1089 * have a Package field) (LP: #346386) (Closes: #627642) */
1090 if (Verify == true)
1091 {
1092 FileFd fd(DestFile, FileFd::ReadOnly);
1093 // Only test for correctness if the file is not empty (empty is ok)
1094 if (fd.FileSize() > 0)
1095 {
1096 pkgTagSection sec;
1097 pkgTagFile tag(&fd);
1098
1099 // all our current indexes have a field 'Package' in each section
1100 if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false)
1101 {
1102 RenameOnError(InvalidFormat);
1103 return;
1104 }
1105 }
1106 }
1107
1108 // Done, move it into position
1109 string FinalFile = _config->FindDir("Dir::State::lists");
1110 FinalFile += URItoFileName(RealURI);
1111 Rename(DestFile,FinalFile);
1112 chmod(FinalFile.c_str(),0644);
1113
1114 /* We restore the original name to DestFile so that the clean operation
1115 will work OK */
1116 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1117 DestFile += URItoFileName(RealURI);
1118
1119 // Remove the compressed version.
1120 if (Erase == true)
1121 unlink(DestFile.c_str());
1122
1123 return;
1124 }
1125
1126 Erase = false;
1127 Complete = true;
1128
1129 // Handle the unzipd case
1130 string FileName = LookupTag(Message,"Alt-Filename");
1131 if (FileName.empty() == false)
1132 {
1133 // The files timestamp matches
1134 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
1135 return;
1136 Decompression = true;
1137 Local = true;
1138 DestFile += ".decomp";
1139 Desc.URI = "copy:" + FileName;
1140 QueueURI(Desc);
1141 Mode = "copy";
1142 return;
1143 }
1144
1145 FileName = LookupTag(Message,"Filename");
1146 if (FileName.empty() == true)
1147 {
1148 Status = StatError;
1149 ErrorText = "Method gave a blank filename";
1150 }
1151
1152 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1153
1154 // The files timestamp matches
1155 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
1156 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
1157 // Update DestFile for .gz suffix so that the clean operation keeps it
1158 DestFile += ".gz";
1159 return;
1160 }
1161
1162 if (FileName == DestFile)
1163 Erase = true;
1164 else
1165 Local = true;
1166
1167 string decompProg;
1168
1169 // If we enable compressed indexes and already have gzip, keep it
1170 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
1171 string FinalFile = _config->FindDir("Dir::State::lists");
1172 FinalFile += URItoFileName(RealURI) + ".gz";
1173 Rename(DestFile,FinalFile);
1174 chmod(FinalFile.c_str(),0644);
1175
1176 // Update DestFile for .gz suffix so that the clean operation keeps it
1177 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1178 DestFile += URItoFileName(RealURI) + ".gz";
1179 return;
1180 }
1181
1182 // get the binary name for your used compression type
1183 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
1184 if(decompProg.empty() == false);
1185 else if(compExt == "uncompressed")
1186 decompProg = "copy";
1187 else {
1188 _error->Error("Unsupported extension: %s", compExt.c_str());
1189 return;
1190 }
1191
1192 Decompression = true;
1193 DestFile += ".decomp";
1194 Desc.URI = decompProg + ":" + FileName;
1195 QueueURI(Desc);
1196
1197 // FIXME: this points to a c++ string that goes out of scope
1198 Mode = decompProg.c_str();
1199 }
1200 /*}}}*/
1201 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1202 // ---------------------------------------------------------------------
1203 /* The Translation file is added to the queue */
1204 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1205 string URI,string URIDesc,string ShortDesc)
1206 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList(), "")
1207 {
1208 }
1209 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const * const Target,
1210 HashStringList const &ExpectedHashes, indexRecords *MetaIndexParser)
1211 : pkgAcqIndex(Owner, Target, ExpectedHashes, MetaIndexParser)
1212 {
1213 // load the filesize
1214 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1215 if(Record)
1216 FileSize = Record->Size;
1217 }
1218 /*}}}*/
1219 // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1220 // ---------------------------------------------------------------------
1221 string pkgAcqIndexTrans::Custom600Headers() const
1222 {
1223 string Final = _config->FindDir("Dir::State::lists");
1224 Final += URItoFileName(RealURI);
1225
1226 struct stat Buf;
1227 if (stat(Final.c_str(),&Buf) != 0)
1228 return "\nFail-Ignore: true\nIndex-File: true";
1229 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1230 }
1231 /*}}}*/
1232 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1233 // ---------------------------------------------------------------------
1234 /* */
1235 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1236 {
1237 size_t const nextExt = CompressionExtension.find(' ');
1238 if (nextExt != std::string::npos)
1239 {
1240 CompressionExtension = CompressionExtension.substr(nextExt+1);
1241 Init(RealURI, Desc.Description, Desc.ShortDesc);
1242 Status = StatIdle;
1243 return;
1244 }
1245
1246 if (Cnf->LocalOnly == true ||
1247 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1248 {
1249 // Ignore this
1250 Status = StatDone;
1251 Complete = false;
1252 Dequeue();
1253 return;
1254 }
1255
1256 Item::Failed(Message,Cnf);
1257 }
1258 /*}}}*/
1259 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
1260 string URI,string URIDesc,string ShortDesc,
1261 string MetaIndexURI, string MetaIndexURIDesc,
1262 string MetaIndexShortDesc,
1263 const vector<IndexTarget*>* IndexTargets,
1264 indexRecords* MetaIndexParser) :
1265 Item(Owner, HashStringList()), RealURI(URI), MetaIndexURI(MetaIndexURI),
1266 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1267 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
1268 {
1269 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1270 DestFile += URItoFileName(URI);
1271
1272 // remove any partial downloaded sig-file in partial/.
1273 // it may confuse proxies and is too small to warrant a
1274 // partial download anyway
1275 unlink(DestFile.c_str());
1276
1277 // Create the item
1278 Desc.Description = URIDesc;
1279 Desc.Owner = this;
1280 Desc.ShortDesc = ShortDesc;
1281 Desc.URI = URI;
1282
1283 string Final = _config->FindDir("Dir::State::lists");
1284 Final += URItoFileName(RealURI);
1285 if (RealFileExists(Final) == true)
1286 {
1287 // File was already in place. It needs to be re-downloaded/verified
1288 // because Release might have changed, we do give it a different
1289 // name than DestFile because otherwise the http method will
1290 // send If-Range requests and there are too many broken servers
1291 // out there that do not understand them
1292 LastGoodSig = DestFile+".reverify";
1293 Rename(Final,LastGoodSig);
1294 }
1295
1296 // we expect the indextargets + one additional Release file
1297 ExpectedAdditionalItems = IndexTargets->size() + 1;
1298
1299 QueueURI(Desc);
1300 }
1301 /*}}}*/
1302 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1303 {
1304 // if the file was never queued undo file-changes done in the constructor
1305 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false &&
1306 LastGoodSig.empty() == false)
1307 {
1308 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1309 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1310 Rename(LastGoodSig, Final);
1311 }
1312
1313 }
1314 /*}}}*/
1315 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1316 // ---------------------------------------------------------------------
1317 /* The only header we use is the last-modified header. */
1318 string pkgAcqMetaSig::Custom600Headers() const
1319 {
1320 struct stat Buf;
1321 if (stat(LastGoodSig.c_str(),&Buf) != 0)
1322 return "\nIndex-File: true";
1323
1324 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1325 }
1326
1327 void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList const &Hashes,
1328 pkgAcquire::MethodConfig *Cfg)
1329 {
1330 Item::Done(Message, Size, Hashes, Cfg);
1331
1332 string FileName = LookupTag(Message,"Filename");
1333 if (FileName.empty() == true)
1334 {
1335 Status = StatError;
1336 ErrorText = "Method gave a blank filename";
1337 return;
1338 }
1339
1340 if (FileName != DestFile)
1341 {
1342 // We have to copy it into place
1343 Local = true;
1344 Desc.URI = "copy:" + FileName;
1345 QueueURI(Desc);
1346 return;
1347 }
1348
1349 Complete = true;
1350
1351 // at this point pkgAcqMetaIndex takes over
1352 ExpectedAdditionalItems = 0;
1353
1354 // put the last known good file back on i-m-s hit (it will
1355 // be re-verified again)
1356 // Else do nothing, we have the new file in DestFile then
1357 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1358 Rename(LastGoodSig, DestFile);
1359
1360 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
1361 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc,
1362 MetaIndexShortDesc, DestFile, IndexTargets,
1363 MetaIndexParser);
1364
1365 }
1366 /*}}}*/
1367 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1368 {
1369 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1370
1371 // at this point pkgAcqMetaIndex takes over
1372 ExpectedAdditionalItems = 0;
1373
1374 // if we get a network error we fail gracefully
1375 if(Status == StatTransientNetworkError)
1376 {
1377 Item::Failed(Message,Cnf);
1378 // move the sigfile back on transient network failures
1379 if(FileExists(LastGoodSig))
1380 Rename(LastGoodSig,Final);
1381
1382 // set the status back to , Item::Failed likes to reset it
1383 Status = pkgAcquire::Item::StatTransientNetworkError;
1384 return;
1385 }
1386
1387 // Delete any existing sigfile when the acquire failed
1388 unlink(Final.c_str());
1389
1390 // queue a pkgAcqMetaIndex with no sigfile
1391 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1392 "", IndexTargets, MetaIndexParser);
1393
1394 if (Cnf->LocalOnly == true ||
1395 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1396 {
1397 // Ignore this
1398 Status = StatDone;
1399 Complete = false;
1400 Dequeue();
1401 return;
1402 }
1403
1404 Item::Failed(Message,Cnf);
1405 }
1406 /*}}}*/
1407 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1408 string URI,string URIDesc,string ShortDesc,
1409 string SigFile,
1410 const vector<IndexTarget*>* IndexTargets,
1411 indexRecords* MetaIndexParser) :
1412 Item(Owner, HashStringList()), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
1413 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
1414 {
1415 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1416 DestFile += URItoFileName(URI);
1417
1418 // Create the item
1419 Desc.Description = URIDesc;
1420 Desc.Owner = this;
1421 Desc.ShortDesc = ShortDesc;
1422 Desc.URI = URI;
1423
1424 // we expect more item
1425 ExpectedAdditionalItems = IndexTargets->size();
1426
1427 QueueURI(Desc);
1428 }
1429 /*}}}*/
1430 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1431 // ---------------------------------------------------------------------
1432 /* The only header we use is the last-modified header. */
1433 string pkgAcqMetaIndex::Custom600Headers() const
1434 {
1435 string Final = _config->FindDir("Dir::State::lists");
1436 Final += URItoFileName(RealURI);
1437
1438 struct stat Buf;
1439 if (stat(Final.c_str(),&Buf) != 0)
1440 return "\nIndex-File: true";
1441
1442 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1443 }
1444 /*}}}*/
1445 void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
1446 pkgAcquire::MethodConfig *Cfg)
1447 {
1448 Item::Done(Message,Size,Hashes,Cfg);
1449
1450 // MetaIndexes are done in two passes: one to download the
1451 // metaindex with an appropriate method, and a second to verify it
1452 // with the gpgv method
1453
1454 if (AuthPass == true)
1455 {
1456 AuthDone(Message);
1457
1458 // all cool, move Release file into place
1459 Complete = true;
1460 }
1461 else
1462 {
1463 RetrievalDone(Message);
1464 if (!Complete)
1465 // Still more retrieving to do
1466 return;
1467
1468 if (SigFile == "")
1469 {
1470 // There was no signature file, so we are finished. Download
1471 // the indexes and do only hashsum verification if possible
1472 MetaIndexParser->Load(DestFile);
1473 QueueIndexes(false);
1474 }
1475 else
1476 {
1477 // FIXME: move this into pkgAcqMetaClearSig::Done on the next
1478 // ABI break
1479
1480 // if we expect a ClearTextSignature (InRelase), ensure that
1481 // this is what we get and if not fail to queue a
1482 // Release/Release.gpg, see #346386
1483 if (SigFile == DestFile && !StartsWithGPGClearTextSignature(DestFile))
1484 {
1485 Failed(Message, Cfg);
1486 return;
1487 }
1488
1489 // There was a signature file, so pass it to gpgv for
1490 // verification
1491 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1492 std::cerr << "Metaindex acquired, queueing gpg verification ("
1493 << SigFile << "," << DestFile << ")\n";
1494 AuthPass = true;
1495 Desc.URI = "gpgv:" + SigFile;
1496 QueueURI(Desc);
1497 Mode = "gpgv";
1498 return;
1499 }
1500 }
1501
1502 if (Complete == true)
1503 {
1504 string FinalFile = _config->FindDir("Dir::State::lists");
1505 FinalFile += URItoFileName(RealURI);
1506 if (SigFile == DestFile)
1507 SigFile = FinalFile;
1508 Rename(DestFile,FinalFile);
1509 chmod(FinalFile.c_str(),0644);
1510 DestFile = FinalFile;
1511 }
1512 }
1513 /*}}}*/
1514 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1515 {
1516 // We have just finished downloading a Release file (it is not
1517 // verified yet)
1518
1519 string FileName = LookupTag(Message,"Filename");
1520 if (FileName.empty() == true)
1521 {
1522 Status = StatError;
1523 ErrorText = "Method gave a blank filename";
1524 return;
1525 }
1526
1527 if (FileName != DestFile)
1528 {
1529 Local = true;
1530 Desc.URI = "copy:" + FileName;
1531 QueueURI(Desc);
1532 return;
1533 }
1534
1535 // make sure to verify against the right file on I-M-S hit
1536 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1537 if(IMSHit)
1538 {
1539 string FinalFile = _config->FindDir("Dir::State::lists");
1540 FinalFile += URItoFileName(RealURI);
1541 if (SigFile == DestFile)
1542 {
1543 SigFile = FinalFile;
1544 // constructor of pkgAcqMetaClearSig moved it out of the way,
1545 // now move it back in on IMS hit for the 'old' file
1546 string const OldClearSig = DestFile + ".reverify";
1547 if (RealFileExists(OldClearSig) == true)
1548 Rename(OldClearSig, FinalFile);
1549 }
1550 DestFile = FinalFile;
1551 }
1552 Complete = true;
1553 }
1554 /*}}}*/
1555 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1556 {
1557 // At this point, the gpgv method has succeeded, so there is a
1558 // valid signature from a key in the trusted keyring. We
1559 // perform additional verification of its contents, and use them
1560 // to verify the indexes we are about to download
1561
1562 if (!MetaIndexParser->Load(DestFile))
1563 {
1564 Status = StatAuthError;
1565 ErrorText = MetaIndexParser->ErrorText;
1566 return;
1567 }
1568
1569 if (!VerifyVendor(Message))
1570 {
1571 return;
1572 }
1573
1574 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1575 std::cerr << "Signature verification succeeded: "
1576 << DestFile << std::endl;
1577
1578 // Download further indexes with verification
1579 QueueIndexes(true);
1580
1581 // is it a clearsigned MetaIndex file?
1582 if (DestFile == SigFile)
1583 return;
1584
1585 // Done, move signature file into position
1586 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1587 URItoFileName(RealURI) + ".gpg";
1588 Rename(SigFile,VerifiedSigFile);
1589 chmod(VerifiedSigFile.c_str(),0644);
1590 }
1591 /*}}}*/
1592 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1593 {
1594 #if 0
1595 /* Reject invalid, existing Release files (LP: #346386) (Closes: #627642)
1596 * FIXME: Disabled; it breaks unsigned repositories without hashes */
1597 if (!verify && FileExists(DestFile) && !MetaIndexParser->Load(DestFile))
1598 {
1599 Status = StatError;
1600 ErrorText = MetaIndexParser->ErrorText;
1601 return;
1602 }
1603 #endif
1604 bool transInRelease = false;
1605 {
1606 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1607 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1608 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1609 if (k->find("Translation-") != std::string::npos)
1610 {
1611 transInRelease = true;
1612 break;
1613 }
1614 }
1615
1616 // at this point the real Items are loaded in the fetcher
1617 ExpectedAdditionalItems = 0;
1618
1619 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1620 Target != IndexTargets->end();
1621 ++Target)
1622 {
1623 HashStringList ExpectedIndexHashes;
1624 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1625 bool compressedAvailable = false;
1626 if (Record == NULL)
1627 {
1628 if ((*Target)->IsOptional() == true)
1629 {
1630 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1631 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1632 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1633 {
1634 compressedAvailable = true;
1635 break;
1636 }
1637 }
1638 else if (verify == true)
1639 {
1640 Status = StatAuthError;
1641 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1642 return;
1643 }
1644 }
1645 else
1646 {
1647 ExpectedIndexHashes = Record->Hashes;
1648 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1649 {
1650 std::cerr << "Queueing: " << (*Target)->URI << std::endl
1651 << "Expected Hash:" << std::endl;
1652 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
1653 std::cerr << "\t- " << hs->toStr() << std::endl;
1654 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
1655 }
1656 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
1657 {
1658 Status = StatAuthError;
1659 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1660 return;
1661 }
1662 }
1663
1664 if ((*Target)->IsOptional() == true)
1665 {
1666 if ((*Target)->IsSubIndex() == true)
1667 new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
1668 (*Target)->ShortDesc, ExpectedIndexHashes);
1669 else if (transInRelease == false || Record != NULL || compressedAvailable == true)
1670 {
1671 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
1672 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
1673 new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
1674 else
1675 new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
1676 }
1677 continue;
1678 }
1679
1680 /* Queue Packages file (either diff or full packages files, depending
1681 on the users option) - we also check if the PDiff Index file is listed
1682 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1683 instead, but passing the required info to it is to much hassle */
1684 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1685 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
1686 new pkgAcqDiffIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
1687 else
1688 new pkgAcqIndex(Owner, *Target, ExpectedIndexHashes, MetaIndexParser);
1689 }
1690 }
1691 /*}}}*/
1692 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1693 {
1694 string::size_type pos;
1695
1696 // check for missing sigs (that where not fatal because otherwise we had
1697 // bombed earlier)
1698 string missingkeys;
1699 string msg = _("There is no public key available for the "
1700 "following key IDs:\n");
1701 pos = Message.find("NO_PUBKEY ");
1702 if (pos != std::string::npos)
1703 {
1704 string::size_type start = pos+strlen("NO_PUBKEY ");
1705 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1706 missingkeys += (Fingerprint);
1707 }
1708 if(!missingkeys.empty())
1709 _error->Warning("%s", (msg + missingkeys).c_str());
1710
1711 string Transformed = MetaIndexParser->GetExpectedDist();
1712
1713 if (Transformed == "../project/experimental")
1714 {
1715 Transformed = "experimental";
1716 }
1717
1718 pos = Transformed.rfind('/');
1719 if (pos != string::npos)
1720 {
1721 Transformed = Transformed.substr(0, pos);
1722 }
1723
1724 if (Transformed == ".")
1725 {
1726 Transformed = "";
1727 }
1728
1729 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1730 MetaIndexParser->GetValidUntil() > 0) {
1731 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1732 if (invalid_since > 0)
1733 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1734 // the time since then the file is invalid - formated in the same way as in
1735 // the download progress display (e.g. 7d 3h 42min 1s)
1736 return _error->Error(
1737 _("Release file for %s is expired (invalid since %s). "
1738 "Updates for this repository will not be applied."),
1739 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1740 }
1741
1742 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1743 {
1744 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1745 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1746 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1747 }
1748
1749 if (MetaIndexParser->CheckDist(Transformed) == false)
1750 {
1751 // This might become fatal one day
1752 // Status = StatAuthError;
1753 // ErrorText = "Conflicting distribution; expected "
1754 // + MetaIndexParser->GetExpectedDist() + " but got "
1755 // + MetaIndexParser->GetDist();
1756 // return false;
1757 if (!Transformed.empty())
1758 {
1759 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1760 Desc.Description.c_str(),
1761 Transformed.c_str(),
1762 MetaIndexParser->GetDist().c_str());
1763 }
1764 }
1765
1766 return true;
1767 }
1768 /*}}}*/
1769 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1770 // ---------------------------------------------------------------------
1771 /* */
1772 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)
1773 {
1774 if (AuthPass == true)
1775 {
1776 // gpgv method failed, if we have a good signature
1777 string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
1778 if (DestFile != SigFile)
1779 LastGoodSigFile.append(".gpg");
1780 LastGoodSigFile.append(".reverify");
1781
1782 if(FileExists(LastGoodSigFile))
1783 {
1784 string VerifiedSigFile = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1785 if (DestFile != SigFile)
1786 VerifiedSigFile.append(".gpg");
1787 Rename(LastGoodSigFile, VerifiedSigFile);
1788 Status = StatTransientNetworkError;
1789 _error->Warning(_("An error occurred during the signature "
1790 "verification. The repository is not updated "
1791 "and the previous index files will be used. "
1792 "GPG error: %s: %s\n"),
1793 Desc.Description.c_str(),
1794 LookupTag(Message,"Message").c_str());
1795 RunScripts("APT::Update::Auth-Failure");
1796 return;
1797 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1798 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1799 _error->Error(_("GPG error: %s: %s"),
1800 Desc.Description.c_str(),
1801 LookupTag(Message,"Message").c_str());
1802 return;
1803 } else {
1804 _error->Warning(_("GPG error: %s: %s"),
1805 Desc.Description.c_str(),
1806 LookupTag(Message,"Message").c_str());
1807 }
1808 // gpgv method failed
1809 ReportMirrorFailure("GPGFailure");
1810 }
1811
1812 /* Always move the meta index, even if gpgv failed. This ensures
1813 * that PackageFile objects are correctly filled in */
1814 if (FileExists(DestFile)) {
1815 string FinalFile = _config->FindDir("Dir::State::lists");
1816 FinalFile += URItoFileName(RealURI);
1817 /* InRelease files become Release files, otherwise
1818 * they would be considered as trusted later on */
1819 if (SigFile == DestFile) {
1820 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
1821 "Release");
1822 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
1823 "Release");
1824 SigFile = FinalFile;
1825 }
1826 Rename(DestFile,FinalFile);
1827 chmod(FinalFile.c_str(),0644);
1828
1829 DestFile = FinalFile;
1830 }
1831
1832 // No Release file was present, or verification failed, so fall
1833 // back to queueing Packages files without verification
1834 QueueIndexes(false);
1835 }
1836 /*}}}*/
1837 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
1838 string const &URI, string const &URIDesc, string const &ShortDesc,
1839 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
1840 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
1841 const vector<IndexTarget*>* IndexTargets,
1842 indexRecords* MetaIndexParser) :
1843 pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
1844 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1845 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
1846 {
1847 SigFile = DestFile;
1848
1849 // index targets + (worst case:) Release/Release.gpg
1850 ExpectedAdditionalItems = IndexTargets->size() + 2;
1851
1852
1853 // keep the old InRelease around in case of transistent network errors
1854 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1855 if (RealFileExists(Final) == true)
1856 {
1857 string const LastGoodSig = DestFile + ".reverify";
1858 Rename(Final,LastGoodSig);
1859 }
1860 }
1861 /*}}}*/
1862 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1863 {
1864 // if the file was never queued undo file-changes done in the constructor
1865 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
1866 {
1867 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1868 string const LastGoodSig = DestFile + ".reverify";
1869 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1870 Rename(LastGoodSig, Final);
1871 }
1872 }
1873 /*}}}*/
1874 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1875 // ---------------------------------------------------------------------
1876 // FIXME: this can go away once the InRelease file is used widely
1877 string pkgAcqMetaClearSig::Custom600Headers() const
1878 {
1879 string Final = _config->FindDir("Dir::State::lists");
1880 Final += URItoFileName(RealURI);
1881
1882 struct stat Buf;
1883 if (stat(Final.c_str(),&Buf) != 0)
1884 {
1885 Final = DestFile + ".reverify";
1886 if (stat(Final.c_str(),&Buf) != 0)
1887 return "\nIndex-File: true\nFail-Ignore: true\n";
1888 }
1889
1890 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1891 }
1892 /*}}}*/
1893 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1894 {
1895 // we failed, we will not get additional items from this method
1896 ExpectedAdditionalItems = 0;
1897
1898 if (AuthPass == false)
1899 {
1900 // Remove the 'old' InRelease file if we try Release.gpg now as otherwise
1901 // the file will stay around and gives a false-auth impression (CVE-2012-0214)
1902 string FinalFile = _config->FindDir("Dir::State::lists");
1903 FinalFile.append(URItoFileName(RealURI));
1904 if (FileExists(FinalFile))
1905 unlink(FinalFile.c_str());
1906
1907 new pkgAcqMetaSig(Owner,
1908 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
1909 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1910 IndexTargets, MetaIndexParser);
1911 if (Cnf->LocalOnly == true ||
1912 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
1913 Dequeue();
1914 }
1915 else
1916 pkgAcqMetaIndex::Failed(Message, Cnf);
1917 }
1918 /*}}}*/
1919 // AcqArchive::AcqArchive - Constructor /*{{{*/
1920 // ---------------------------------------------------------------------
1921 /* This just sets up the initial fetch environment and queues the first
1922 possibilitiy */
1923 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1924 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1925 string &StoreFilename) :
1926 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
1927 StoreFilename(StoreFilename), Vf(Version.FileList()),
1928 Trusted(false)
1929 {
1930 Retries = _config->FindI("Acquire::Retries",0);
1931
1932 if (Version.Arch() == 0)
1933 {
1934 _error->Error(_("I wasn't able to locate a file for the %s package. "
1935 "This might mean you need to manually fix this package. "
1936 "(due to missing arch)"),
1937 Version.ParentPkg().FullName().c_str());
1938 return;
1939 }
1940
1941 /* We need to find a filename to determine the extension. We make the
1942 assumption here that all the available sources for this version share
1943 the same extension.. */
1944 // Skip not source sources, they do not have file fields.
1945 for (; Vf.end() == false; ++Vf)
1946 {
1947 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1948 continue;
1949 break;
1950 }
1951
1952 // Does not really matter here.. we are going to fail out below
1953 if (Vf.end() != true)
1954 {
1955 // If this fails to get a file name we will bomb out below.
1956 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1957 if (_error->PendingError() == true)
1958 return;
1959
1960 // Generate the final file name as: package_version_arch.foo
1961 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1962 QuoteString(Version.VerStr(),"_:") + '_' +
1963 QuoteString(Version.Arch(),"_:.") +
1964 "." + flExtension(Parse.FileName());
1965 }
1966
1967 // check if we have one trusted source for the package. if so, switch
1968 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
1969 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
1970 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
1971 bool seenUntrusted = false;
1972 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
1973 {
1974 pkgIndexFile *Index;
1975 if (Sources->FindIndex(i.File(),Index) == false)
1976 continue;
1977
1978 if (debugAuth == true)
1979 std::cerr << "Checking index: " << Index->Describe()
1980 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
1981
1982 if (Index->IsTrusted() == true)
1983 {
1984 Trusted = true;
1985 if (allowUnauth == false)
1986 break;
1987 }
1988 else
1989 seenUntrusted = true;
1990 }
1991
1992 // "allow-unauthenticated" restores apts old fetching behaviour
1993 // that means that e.g. unauthenticated file:// uris are higher
1994 // priority than authenticated http:// uris
1995 if (allowUnauth == true && seenUntrusted == true)
1996 Trusted = false;
1997
1998 // Select a source
1999 if (QueueNext() == false && _error->PendingError() == false)
2000 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2001 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2002 }
2003 /*}}}*/
2004 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2005 // ---------------------------------------------------------------------
2006 /* This queues the next available file version for download. It checks if
2007 the archive is already available in the cache and stashs the MD5 for
2008 checking later. */
2009 bool pkgAcqArchive::QueueNext()
2010 {
2011 for (; Vf.end() == false; ++Vf)
2012 {
2013 // Ignore not source sources
2014 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2015 continue;
2016
2017 // Try to cross match against the source list
2018 pkgIndexFile *Index;
2019 if (Sources->FindIndex(Vf.File(),Index) == false)
2020 continue;
2021
2022 // only try to get a trusted package from another source if that source
2023 // is also trusted
2024 if(Trusted && !Index->IsTrusted())
2025 continue;
2026
2027 // Grab the text package record
2028 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2029 if (_error->PendingError() == true)
2030 return false;
2031
2032 string PkgFile = Parse.FileName();
2033 ExpectedHashes = Parse.Hashes();
2034
2035 if (PkgFile.empty() == true)
2036 return _error->Error(_("The package index files are corrupted. No Filename: "
2037 "field for package %s."),
2038 Version.ParentPkg().Name());
2039
2040 Desc.URI = Index->ArchiveURI(PkgFile);
2041 Desc.Description = Index->ArchiveInfo(Version);
2042 Desc.Owner = this;
2043 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2044
2045 // See if we already have the file. (Legacy filenames)
2046 FileSize = Version->Size;
2047 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2048 struct stat Buf;
2049 if (stat(FinalFile.c_str(),&Buf) == 0)
2050 {
2051 // Make sure the size matches
2052 if ((unsigned long long)Buf.st_size == Version->Size)
2053 {
2054 Complete = true;
2055 Local = true;
2056 Status = StatDone;
2057 StoreFilename = DestFile = FinalFile;
2058 return true;
2059 }
2060
2061 /* Hmm, we have a file and its size does not match, this means it is
2062 an old style mismatched arch */
2063 unlink(FinalFile.c_str());
2064 }
2065
2066 // Check it again using the new style output filenames
2067 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2068 if (stat(FinalFile.c_str(),&Buf) == 0)
2069 {
2070 // Make sure the size matches
2071 if ((unsigned long long)Buf.st_size == Version->Size)
2072 {
2073 Complete = true;
2074 Local = true;
2075 Status = StatDone;
2076 StoreFilename = DestFile = FinalFile;
2077 return true;
2078 }
2079
2080 /* Hmm, we have a file and its size does not match, this shouldn't
2081 happen.. */
2082 unlink(FinalFile.c_str());
2083 }
2084
2085 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2086
2087 // Check the destination file
2088 if (stat(DestFile.c_str(),&Buf) == 0)
2089 {
2090 // Hmm, the partial file is too big, erase it
2091 if ((unsigned long long)Buf.st_size > Version->Size)
2092 unlink(DestFile.c_str());
2093 else
2094 PartialSize = Buf.st_size;
2095 }
2096
2097 // Disables download of archives - useful if no real installation follows,
2098 // e.g. if we are just interested in proposed installation order
2099 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2100 {
2101 Complete = true;
2102 Local = true;
2103 Status = StatDone;
2104 StoreFilename = DestFile = FinalFile;
2105 return true;
2106 }
2107
2108 // Create the item
2109 Local = false;
2110 QueueURI(Desc);
2111
2112 ++Vf;
2113 return true;
2114 }
2115 return false;
2116 }
2117 /*}}}*/
2118 // AcqArchive::Done - Finished fetching /*{{{*/
2119 // ---------------------------------------------------------------------
2120 /* */
2121 void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2122 pkgAcquire::MethodConfig *Cfg)
2123 {
2124 Item::Done(Message, Size, CalcHashes, Cfg);
2125
2126 // Check the size
2127 if (Size != Version->Size)
2128 {
2129 RenameOnError(SizeMismatch);
2130 return;
2131 }
2132
2133 // FIXME: could this empty() check impose *any* sort of security issue?
2134 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2135 {
2136 RenameOnError(HashSumMismatch);
2137 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2138 return;
2139 }
2140
2141 // Grab the output filename
2142 string FileName = LookupTag(Message,"Filename");
2143 if (FileName.empty() == true)
2144 {
2145 Status = StatError;
2146 ErrorText = "Method gave a blank filename";
2147 return;
2148 }
2149
2150 Complete = true;
2151
2152 // Reference filename
2153 if (FileName != DestFile)
2154 {
2155 StoreFilename = DestFile = FileName;
2156 Local = true;
2157 return;
2158 }
2159
2160 // Done, move it into position
2161 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2162 FinalFile += flNotDir(StoreFilename);
2163 Rename(DestFile,FinalFile);
2164
2165 StoreFilename = DestFile = FinalFile;
2166 Complete = true;
2167 }
2168 /*}}}*/
2169 // AcqArchive::Failed - Failure handler /*{{{*/
2170 // ---------------------------------------------------------------------
2171 /* Here we try other sources */
2172 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2173 {
2174 ErrorText = LookupTag(Message,"Message");
2175
2176 /* We don't really want to retry on failed media swaps, this prevents
2177 that. An interesting observation is that permanent failures are not
2178 recorded. */
2179 if (Cnf->Removable == true &&
2180 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2181 {
2182 // Vf = Version.FileList();
2183 while (Vf.end() == false) ++Vf;
2184 StoreFilename = string();
2185 Item::Failed(Message,Cnf);
2186 return;
2187 }
2188
2189 if (QueueNext() == false)
2190 {
2191 // This is the retry counter
2192 if (Retries != 0 &&
2193 Cnf->LocalOnly == false &&
2194 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2195 {
2196 Retries--;
2197 Vf = Version.FileList();
2198 if (QueueNext() == true)
2199 return;
2200 }
2201
2202 StoreFilename = string();
2203 Item::Failed(Message,Cnf);
2204 }
2205 }
2206 /*}}}*/
2207 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2208 // ---------------------------------------------------------------------
2209 APT_PURE bool pkgAcqArchive::IsTrusted() const
2210 {
2211 return Trusted;
2212 }
2213 /*}}}*/
2214 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2215 // ---------------------------------------------------------------------
2216 /* */
2217 void pkgAcqArchive::Finished()
2218 {
2219 if (Status == pkgAcquire::Item::StatDone &&
2220 Complete == true)
2221 return;
2222 StoreFilename = string();
2223 }
2224 /*}}}*/
2225 // AcqFile::pkgAcqFile - Constructor /*{{{*/
2226 // ---------------------------------------------------------------------
2227 /* The file is added to the queue */
2228 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2229 unsigned long long Size,string Dsc,string ShortDesc,
2230 const string &DestDir, const string &DestFilename,
2231 bool IsIndexFile) :
2232 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2233 {
2234 Retries = _config->FindI("Acquire::Retries",0);
2235
2236 if(!DestFilename.empty())
2237 DestFile = DestFilename;
2238 else if(!DestDir.empty())
2239 DestFile = DestDir + "/" + flNotDir(URI);
2240 else
2241 DestFile = flNotDir(URI);
2242
2243 // Create the item
2244 Desc.URI = URI;
2245 Desc.Description = Dsc;
2246 Desc.Owner = this;
2247
2248 // Set the short description to the archive component
2249 Desc.ShortDesc = ShortDesc;
2250
2251 // Get the transfer sizes
2252 FileSize = Size;
2253 struct stat Buf;
2254 if (stat(DestFile.c_str(),&Buf) == 0)
2255 {
2256 // Hmm, the partial file is too big, erase it
2257 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2258 unlink(DestFile.c_str());
2259 else
2260 PartialSize = Buf.st_size;
2261 }
2262
2263 QueueURI(Desc);
2264 }
2265 /*}}}*/
2266 // AcqFile::Done - Item downloaded OK /*{{{*/
2267 // ---------------------------------------------------------------------
2268 /* */
2269 void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2270 pkgAcquire::MethodConfig *Cnf)
2271 {
2272 Item::Done(Message,Size,CalcHashes,Cnf);
2273
2274 // Check the hash
2275 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2276 {
2277 RenameOnError(HashSumMismatch);
2278 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2279 return;
2280 }
2281
2282 string FileName = LookupTag(Message,"Filename");
2283 if (FileName.empty() == true)
2284 {
2285 Status = StatError;
2286 ErrorText = "Method gave a blank filename";
2287 return;
2288 }
2289
2290 Complete = true;
2291
2292 // The files timestamp matches
2293 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2294 return;
2295
2296 // We have to copy it into place
2297 if (FileName != DestFile)
2298 {
2299 Local = true;
2300 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2301 Cnf->Removable == true)
2302 {
2303 Desc.URI = "copy:" + FileName;
2304 QueueURI(Desc);
2305 return;
2306 }
2307
2308 // Erase the file if it is a symlink so we can overwrite it
2309 struct stat St;
2310 if (lstat(DestFile.c_str(),&St) == 0)
2311 {
2312 if (S_ISLNK(St.st_mode) != 0)
2313 unlink(DestFile.c_str());
2314 }
2315
2316 // Symlink the file
2317 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2318 {
2319 ErrorText = "Link to " + DestFile + " failure ";
2320 Status = StatError;
2321 Complete = false;
2322 }
2323 }
2324 }
2325 /*}}}*/
2326 // AcqFile::Failed - Failure handler /*{{{*/
2327 // ---------------------------------------------------------------------
2328 /* Here we try other sources */
2329 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2330 {
2331 ErrorText = LookupTag(Message,"Message");
2332
2333 // This is the retry counter
2334 if (Retries != 0 &&
2335 Cnf->LocalOnly == false &&
2336 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2337 {
2338 Retries--;
2339 QueueURI(Desc);
2340 return;
2341 }
2342
2343 Item::Failed(Message,Cnf);
2344 }
2345 /*}}}*/
2346 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2347 // ---------------------------------------------------------------------
2348 /* The only header we use is the last-modified header. */
2349 string pkgAcqFile::Custom600Headers() const
2350 {
2351 if (IsIndexFile)
2352 return "\nIndex-File: true";
2353 return "";
2354 }
2355 /*}}}*/