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