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