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