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