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