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