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