merged from lp:~mvo/apt/mvo
[ntk/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 <apt-pkg/acquire-item.h>
17 #include <apt-pkg/configuration.h>
18 #include <apt-pkg/aptconfiguration.h>
19 #include <apt-pkg/sourcelist.h>
20 #include <apt-pkg/vendorlist.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/md5.h>
25 #include <apt-pkg/sha1.h>
26 #include <apt-pkg/tagfile.h>
27
28 #include <apti18n.h>
29
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string>
34 #include <sstream>
35 #include <stdio.h>
36 /*}}}*/
37
38 using namespace std;
39
40 // Acquire::Item::Item - Constructor /*{{{*/
41 // ---------------------------------------------------------------------
42 /* */
43 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
44 PartialSize(0), Mode(0), ID(0), Complete(false),
45 Local(false), QueueCounter(0)
46 {
47 Owner->Add(this);
48 Status = StatIdle;
49 }
50 /*}}}*/
51 // Acquire::Item::~Item - Destructor /*{{{*/
52 // ---------------------------------------------------------------------
53 /* */
54 pkgAcquire::Item::~Item()
55 {
56 Owner->Remove(this);
57 }
58 /*}}}*/
59 // Acquire::Item::Failed - Item failed to download /*{{{*/
60 // ---------------------------------------------------------------------
61 /* We return to an idle state if there are still other queues that could
62 fetch this object */
63 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
64 {
65 Status = StatIdle;
66 ErrorText = LookupTag(Message,"Message");
67 UsedMirror = LookupTag(Message,"UsedMirror");
68 if (QueueCounter <= 1)
69 {
70 /* This indicates that the file is not available right now but might
71 be sometime later. If we do a retry cycle then this should be
72 retried [CDROMs] */
73 if (Cnf->LocalOnly == true &&
74 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
75 {
76 Status = StatIdle;
77 Dequeue();
78 return;
79 }
80
81 Status = StatError;
82 Dequeue();
83 }
84
85 // report mirror failure back to LP if we actually use a mirror
86 string FailReason = LookupTag(Message, "FailReason");
87 if(FailReason.size() != 0)
88 ReportMirrorFailure(FailReason);
89 else
90 ReportMirrorFailure(ErrorText);
91 }
92 /*}}}*/
93 // Acquire::Item::Start - Item has begun to download /*{{{*/
94 // ---------------------------------------------------------------------
95 /* Stash status and the file size. Note that setting Complete means
96 sub-phases of the acquire process such as decompresion are operating */
97 void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
98 {
99 Status = StatFetching;
100 if (FileSize == 0 && Complete == false)
101 FileSize = Size;
102 }
103 /*}}}*/
104 // Acquire::Item::Done - Item downloaded OK /*{{{*/
105 // ---------------------------------------------------------------------
106 /* */
107 void pkgAcquire::Item::Done(string Message,unsigned long Size,string Hash,
108 pkgAcquire::MethodConfig *Cnf)
109 {
110 // We just downloaded something..
111 string FileName = LookupTag(Message,"Filename");
112 UsedMirror = LookupTag(Message,"UsedMirror");
113 if (Complete == false && !Local && FileName == DestFile)
114 {
115 if (Owner->Log != 0)
116 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
117 }
118
119 if (FileSize == 0)
120 FileSize= Size;
121 Status = StatDone;
122 ErrorText = string();
123 Owner->Dequeue(this);
124 }
125 /*}}}*/
126 // Acquire::Item::Rename - Rename a file /*{{{*/
127 // ---------------------------------------------------------------------
128 /* This helper function is used by alot of item methods as thier final
129 step */
130 void pkgAcquire::Item::Rename(string From,string To)
131 {
132 if (rename(From.c_str(),To.c_str()) != 0)
133 {
134 char S[300];
135 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
136 From.c_str(),To.c_str());
137 Status = StatError;
138 ErrorText = S;
139 }
140 }
141 /*}}}*/
142
143 void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
144 {
145 // we only act if a mirror was used at all
146 if(UsedMirror.empty())
147 return;
148 #if 0
149 std::cerr << "\nReportMirrorFailure: "
150 << UsedMirror
151 << " Uri: " << DescURI()
152 << " FailCode: "
153 << FailCode << std::endl;
154 #endif
155 const char *Args[40];
156 unsigned int i = 0;
157 string report = _config->Find("Methods::Mirror::ProblemReporting",
158 "/usr/lib/apt/apt-report-mirror-failure");
159 if(!FileExists(report))
160 return;
161 Args[i++] = report.c_str();
162 Args[i++] = UsedMirror.c_str();
163 Args[i++] = DescURI().c_str();
164 Args[i++] = FailCode.c_str();
165 Args[i++] = NULL;
166 pid_t pid = ExecFork();
167 if(pid < 0)
168 {
169 _error->Error("ReportMirrorFailure Fork failed");
170 return;
171 }
172 else if(pid == 0)
173 {
174 execvp(Args[0], (char**)Args);
175 std::cerr << "Could not exec " << Args[0] << std::endl;
176 _exit(100);
177 }
178 if(!ExecWait(pid, "report-mirror-failure"))
179 {
180 _error->Warning("Couldn't report problem to '%s'",
181 _config->Find("Methods::Mirror::ProblemReporting").c_str());
182 }
183 }
184
185 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
186 // ---------------------------------------------------------------------
187 /* Get the DiffIndex file first and see if there are patches availabe
188 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
189 * patches. If anything goes wrong in that process, it will fall back to
190 * the original packages file
191 */
192 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
193 string URI,string URIDesc,string ShortDesc,
194 HashString ExpectedHash)
195 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
196 Description(URIDesc)
197 {
198
199 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
200
201 Desc.Description = URIDesc + "/DiffIndex";
202 Desc.Owner = this;
203 Desc.ShortDesc = ShortDesc;
204 Desc.URI = URI + ".diff/Index";
205
206 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
207 DestFile += URItoFileName(URI) + string(".DiffIndex");
208
209 if(Debug)
210 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
211
212 // look for the current package file
213 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
214 CurrentPackagesFile += URItoFileName(RealURI);
215
216 // FIXME: this file:/ check is a hack to prevent fetching
217 // from local sources. this is really silly, and
218 // should be fixed cleanly as soon as possible
219 if(!FileExists(CurrentPackagesFile) ||
220 Desc.URI.substr(0,strlen("file:/")) == "file:/")
221 {
222 // we don't have a pkg file or we don't want to queue
223 if(Debug)
224 std::clog << "No index file, local or canceld by user" << std::endl;
225 Failed("", NULL);
226 return;
227 }
228
229 if(Debug)
230 std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
231 << CurrentPackagesFile << std::endl;
232
233 QueueURI(Desc);
234
235 }
236 /*}}}*/
237 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
238 // ---------------------------------------------------------------------
239 /* The only header we use is the last-modified header. */
240 string pkgAcqDiffIndex::Custom600Headers()
241 {
242 string Final = _config->FindDir("Dir::State::lists");
243 Final += URItoFileName(RealURI) + string(".IndexDiff");
244
245 if(Debug)
246 std::clog << "Custom600Header-IMS: " << Final << std::endl;
247
248 struct stat Buf;
249 if (stat(Final.c_str(),&Buf) != 0)
250 return "\nIndex-File: true";
251
252 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
253 }
254 /*}}}*/
255 bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
256 {
257 if(Debug)
258 std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
259 << std::endl;
260
261 pkgTagSection Tags;
262 string ServerSha1;
263 vector<DiffInfo> available_patches;
264
265 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
266 pkgTagFile TF(&Fd);
267 if (_error->PendingError() == true)
268 return false;
269
270 if(TF.Step(Tags) == true)
271 {
272 bool found = false;
273 DiffInfo d;
274 string size;
275
276 string const tmp = Tags.FindS("SHA1-Current");
277 std::stringstream ss(tmp);
278 ss >> ServerSha1 >> size;
279 unsigned long const ServerSize = atol(size.c_str());
280
281 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
282 SHA1Summation SHA1;
283 SHA1.AddFD(fd.Fd(), fd.Size());
284 string const local_sha1 = SHA1.Result();
285
286 if(local_sha1 == ServerSha1)
287 {
288 // we have the same sha1 as the server
289 if(Debug)
290 std::clog << "Package file is up-to-date" << std::endl;
291 // set found to true, this will queue a pkgAcqIndexDiffs with
292 // a empty availabe_patches
293 found = true;
294 }
295 else
296 {
297 if(Debug)
298 std::clog << "SHA1-Current: " << ServerSha1 << std::endl;
299
300 // check the historie and see what patches we need
301 string const history = Tags.FindS("SHA1-History");
302 std::stringstream hist(history);
303 while(hist >> d.sha1 >> size >> d.file)
304 {
305 // read until the first match is found
306 // from that point on, we probably need all diffs
307 if(d.sha1 == local_sha1)
308 found=true;
309 else if (found == false)
310 continue;
311
312 if(Debug)
313 std::clog << "Need to get diff: " << d.file << std::endl;
314 available_patches.push_back(d);
315 }
316
317 if (available_patches.empty() == false)
318 {
319 // patching with too many files is rather slow compared to a fast download
320 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
321 if (fileLimit != 0 && fileLimit < available_patches.size())
322 {
323 if (Debug)
324 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
325 << ") so fallback to complete download" << std::endl;
326 return false;
327 }
328
329 // see if the patches are too big
330 found = false; // it was true and it will be true again at the end
331 d = *available_patches.begin();
332 string const firstPatch = d.file;
333 unsigned long patchesSize = 0;
334 std::stringstream patches(Tags.FindS("SHA1-Patches"));
335 while(patches >> d.sha1 >> size >> d.file)
336 {
337 if (firstPatch == d.file)
338 found = true;
339 else if (found == false)
340 continue;
341
342 patchesSize += atol(size.c_str());
343 }
344 unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
345 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
346 {
347 if (Debug)
348 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
349 << ") so fallback to complete download" << std::endl;
350 return false;
351 }
352 }
353 }
354
355 // we have something, queue the next diff
356 if(found)
357 {
358 // queue the diffs
359 string::size_type const last_space = Description.rfind(" ");
360 if(last_space != string::npos)
361 Description.erase(last_space, Description.size()-last_space);
362 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
363 ExpectedHash, available_patches);
364 Complete = false;
365 Status = StatDone;
366 Dequeue();
367 return true;
368 }
369 }
370
371 // Nothing found, report and return false
372 // Failing here is ok, if we return false later, the full
373 // IndexFile is queued
374 if(Debug)
375 std::clog << "Can't find a patch in the index file" << std::endl;
376 return false;
377 }
378 /*}}}*/
379 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
380 {
381 if(Debug)
382 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl
383 << "Falling back to normal index file aquire" << std::endl;
384
385 new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc,
386 ExpectedHash);
387
388 Complete = false;
389 Status = StatDone;
390 Dequeue();
391 }
392 /*}}}*/
393 void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
394 pkgAcquire::MethodConfig *Cnf)
395 {
396 if(Debug)
397 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
398
399 Item::Done(Message,Size,Md5Hash,Cnf);
400
401 string FinalFile;
402 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
403
404 // sucess in downloading the index
405 // rename the index
406 FinalFile += string(".IndexDiff");
407 if(Debug)
408 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
409 << std::endl;
410 Rename(DestFile,FinalFile);
411 chmod(FinalFile.c_str(),0644);
412 DestFile = FinalFile;
413
414 if(!ParseDiffIndex(DestFile))
415 return Failed("", NULL);
416
417 Complete = true;
418 Status = StatDone;
419 Dequeue();
420 return;
421 }
422 /*}}}*/
423 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
424 // ---------------------------------------------------------------------
425 /* The package diff is added to the queue. one object is constructed
426 * for each diff and the index
427 */
428 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
429 string URI,string URIDesc,string ShortDesc,
430 HashString ExpectedHash,
431 vector<DiffInfo> diffs)
432 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
433 available_patches(diffs)
434 {
435
436 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
437 DestFile += URItoFileName(URI);
438
439 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
440
441 Description = URIDesc;
442 Desc.Owner = this;
443 Desc.ShortDesc = ShortDesc;
444
445 if(available_patches.size() == 0)
446 {
447 // we are done (yeah!)
448 Finish(true);
449 }
450 else
451 {
452 // get the next diff
453 State = StateFetchDiff;
454 QueueNextDiff();
455 }
456 }
457 /*}}}*/
458 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
459 {
460 if(Debug)
461 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl
462 << "Falling back to normal index file aquire" << std::endl;
463 new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
464 ExpectedHash);
465 Finish();
466 }
467 /*}}}*/
468 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
469 void pkgAcqIndexDiffs::Finish(bool allDone)
470 {
471 // we restore the original name, this is required, otherwise
472 // the file will be cleaned
473 if(allDone)
474 {
475 DestFile = _config->FindDir("Dir::State::lists");
476 DestFile += URItoFileName(RealURI);
477
478 if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
479 {
480 Status = StatAuthError;
481 ErrorText = _("MD5Sum mismatch");
482 Rename(DestFile,DestFile + ".FAILED");
483 Dequeue();
484 return;
485 }
486
487 // this is for the "real" finish
488 Complete = true;
489 Status = StatDone;
490 Dequeue();
491 if(Debug)
492 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
493 return;
494 }
495
496 if(Debug)
497 std::clog << "Finishing: " << Desc.URI << std::endl;
498 Complete = false;
499 Status = StatDone;
500 Dequeue();
501 return;
502 }
503 /*}}}*/
504 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
505 {
506
507 // calc sha1 of the just patched file
508 string FinalFile = _config->FindDir("Dir::State::lists");
509 FinalFile += URItoFileName(RealURI);
510
511 FileFd fd(FinalFile, FileFd::ReadOnly);
512 SHA1Summation SHA1;
513 SHA1.AddFD(fd.Fd(), fd.Size());
514 string local_sha1 = string(SHA1.Result());
515 if(Debug)
516 std::clog << "QueueNextDiff: "
517 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
518
519 // remove all patches until the next matching patch is found
520 // this requires the Index file to be ordered
521 for(vector<DiffInfo>::iterator I=available_patches.begin();
522 available_patches.size() > 0 &&
523 I != available_patches.end() &&
524 (*I).sha1 != local_sha1;
525 I++)
526 {
527 available_patches.erase(I);
528 }
529
530 // error checking and falling back if no patch was found
531 if(available_patches.size() == 0)
532 {
533 Failed("", NULL);
534 return false;
535 }
536
537 // queue the right diff
538 Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz";
539 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
540 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
541 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
542
543 if(Debug)
544 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
545
546 QueueURI(Desc);
547
548 return true;
549 }
550 /*}}}*/
551 void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
552 pkgAcquire::MethodConfig *Cnf)
553 {
554 if(Debug)
555 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
556
557 Item::Done(Message,Size,Md5Hash,Cnf);
558
559 string FinalFile;
560 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
561
562 // sucess in downloading a diff, enter ApplyDiff state
563 if(State == StateFetchDiff)
564 {
565
566 if(Debug)
567 std::clog << "Sending to gzip method: " << FinalFile << std::endl;
568
569 string FileName = LookupTag(Message,"Filename");
570 State = StateUnzipDiff;
571 Local = true;
572 Desc.URI = "gzip:" + FileName;
573 DestFile += ".decomp";
574 QueueURI(Desc);
575 Mode = "gzip";
576 return;
577 }
578
579 // sucess in downloading a diff, enter ApplyDiff state
580 if(State == StateUnzipDiff)
581 {
582
583 // rred excepts the patch as $FinalFile.ed
584 Rename(DestFile,FinalFile+".ed");
585
586 if(Debug)
587 std::clog << "Sending to rred method: " << FinalFile << std::endl;
588
589 State = StateApplyDiff;
590 Local = true;
591 Desc.URI = "rred:" + FinalFile;
592 QueueURI(Desc);
593 Mode = "rred";
594 return;
595 }
596
597
598 // success in download/apply a diff, queue next (if needed)
599 if(State == StateApplyDiff)
600 {
601 // remove the just applied patch
602 available_patches.erase(available_patches.begin());
603
604 // move into place
605 if(Debug)
606 {
607 std::clog << "Moving patched file in place: " << std::endl
608 << DestFile << " -> " << FinalFile << std::endl;
609 }
610 Rename(DestFile,FinalFile);
611 chmod(FinalFile.c_str(),0644);
612
613 // see if there is more to download
614 if(available_patches.size() > 0) {
615 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
616 ExpectedHash, available_patches);
617 return Finish();
618 } else
619 return Finish(true);
620 }
621 }
622 /*}}}*/
623 // AcqIndex::AcqIndex - Constructor /*{{{*/
624 // ---------------------------------------------------------------------
625 /* The package file is added to the queue and a second class is
626 instantiated to fetch the revision file */
627 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
628 string URI,string URIDesc,string ShortDesc,
629 HashString ExpectedHash, string comprExt)
630 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash)
631 {
632 Decompression = false;
633 Erase = false;
634
635 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
636 DestFile += URItoFileName(URI);
637
638 if(comprExt.empty())
639 {
640 // autoselect the compression method
641 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
642 if (types.empty() == true)
643 comprExt = "plain";
644 else
645 comprExt = "." + types[0];
646 }
647 CompressionExtension = ((comprExt == "plain" || comprExt == ".") ? "" : comprExt);
648
649 Desc.URI = URI + CompressionExtension;
650
651 Desc.Description = URIDesc;
652 Desc.Owner = this;
653 Desc.ShortDesc = ShortDesc;
654
655 QueueURI(Desc);
656 }
657 /*}}}*/
658 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
659 // ---------------------------------------------------------------------
660 /* The only header we use is the last-modified header. */
661 string pkgAcqIndex::Custom600Headers()
662 {
663 string Final = _config->FindDir("Dir::State::lists");
664 Final += URItoFileName(RealURI);
665
666 struct stat Buf;
667 if (stat(Final.c_str(),&Buf) != 0)
668 return "\nIndex-File: true";
669 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
670 }
671 /*}}}*/
672 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
673 {
674 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
675
676 for (std::vector<std::string>::const_iterator t = types.begin();
677 t != types.end(); t++)
678 {
679 // jump over all already tried compression types
680 const unsigned int nameLen = Desc.URI.size() - (*t).size();
681 if(Desc.URI.substr(nameLen) != *t)
682 continue;
683
684 // we want to try it with the next extension (and make sure to
685 // not skip over the end)
686 t++;
687 if (t == types.end())
688 break;
689
690 // queue new download
691 Desc.URI = Desc.URI.substr(0, nameLen) + *t;
692 new pkgAcqIndex(Owner, RealURI, Desc.Description, Desc.ShortDesc,
693 ExpectedHash, string(".").append(*t));
694
695 Status = StatDone;
696 Complete = false;
697 Dequeue();
698 return;
699 }
700
701 // on decompression failure, remove bad versions in partial/
702 if(Decompression && Erase) {
703 string s = _config->FindDir("Dir::State::lists") + "partial/";
704 s += URItoFileName(RealURI);
705 unlink(s.c_str());
706 }
707
708 Item::Failed(Message,Cnf);
709 }
710 /*}}}*/
711 // AcqIndex::Done - Finished a fetch /*{{{*/
712 // ---------------------------------------------------------------------
713 /* This goes through a number of states.. On the initial fetch the
714 method could possibly return an alternate filename which points
715 to the uncompressed version of the file. If this is so the file
716 is copied into the partial directory. In all other cases the file
717 is decompressed with a gzip uri. */
718 void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash,
719 pkgAcquire::MethodConfig *Cfg)
720 {
721 Item::Done(Message,Size,Hash,Cfg);
722
723 if (Decompression == true)
724 {
725 if (_config->FindB("Debug::pkgAcquire::Auth", false))
726 {
727 std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
728 std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
729 }
730
731 if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
732 {
733 Status = StatAuthError;
734 ErrorText = _("Hash Sum mismatch");
735 Rename(DestFile,DestFile + ".FAILED");
736 ReportMirrorFailure("HashChecksumFailure");
737 return;
738 }
739 // Done, move it into position
740 string FinalFile = _config->FindDir("Dir::State::lists");
741 FinalFile += URItoFileName(RealURI);
742 Rename(DestFile,FinalFile);
743 chmod(FinalFile.c_str(),0644);
744
745 /* We restore the original name to DestFile so that the clean operation
746 will work OK */
747 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
748 DestFile += URItoFileName(RealURI);
749
750 // Remove the compressed version.
751 if (Erase == true)
752 unlink(DestFile.c_str());
753 return;
754 }
755
756 Erase = false;
757 Complete = true;
758
759 // Handle the unzipd case
760 string FileName = LookupTag(Message,"Alt-Filename");
761 if (FileName.empty() == false)
762 {
763 // The files timestamp matches
764 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
765 return;
766 Decompression = true;
767 Local = true;
768 DestFile += ".decomp";
769 Desc.URI = "copy:" + FileName;
770 QueueURI(Desc);
771 Mode = "copy";
772 return;
773 }
774
775 FileName = LookupTag(Message,"Filename");
776 if (FileName.empty() == true)
777 {
778 Status = StatError;
779 ErrorText = "Method gave a blank filename";
780 }
781
782 // The files timestamp matches
783 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
784 return;
785
786 if (FileName == DestFile)
787 Erase = true;
788 else
789 Local = true;
790
791 string compExt = flExtension(flNotDir(URI(Desc.URI).Path));
792 string decompProg;
793
794 // get the binary name for your used compression type
795 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
796 if(decompProg.empty() == false);
797 // flExtensions returns the full name if no extension is found
798 // this is why we have this complicated compare operation here
799 // FIMXE: add a new flJustExtension() that return "" if no
800 // extension is found and use that above so that it can
801 // be tested against ""
802 else if(compExt == flNotDir(URI(Desc.URI).Path))
803 decompProg = "copy";
804 else {
805 _error->Error("Unsupported extension: %s", compExt.c_str());
806 return;
807 }
808
809 Decompression = true;
810 DestFile += ".decomp";
811 Desc.URI = decompProg + ":" + FileName;
812 QueueURI(Desc);
813 Mode = decompProg.c_str();
814 }
815 /*}}}*/
816 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
817 // ---------------------------------------------------------------------
818 /* The Translation file is added to the queue */
819 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
820 string URI,string URIDesc,string ShortDesc)
821 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
822 {
823 }
824 /*}}}*/
825 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
826 // ---------------------------------------------------------------------
827 /* */
828 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
829 {
830 if (Cnf->LocalOnly == true ||
831 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
832 {
833 // Ignore this
834 Status = StatDone;
835 Complete = false;
836 Dequeue();
837 return;
838 }
839
840 Item::Failed(Message,Cnf);
841 }
842 /*}}}*/
843 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
844 string URI,string URIDesc,string ShortDesc,
845 string MetaIndexURI, string MetaIndexURIDesc,
846 string MetaIndexShortDesc,
847 const vector<IndexTarget*>* IndexTargets,
848 indexRecords* MetaIndexParser) :
849 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
850 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
851 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
852 {
853 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
854 DestFile += URItoFileName(URI);
855
856 // remove any partial downloaded sig-file in partial/.
857 // it may confuse proxies and is too small to warrant a
858 // partial download anyway
859 unlink(DestFile.c_str());
860
861 // Create the item
862 Desc.Description = URIDesc;
863 Desc.Owner = this;
864 Desc.ShortDesc = ShortDesc;
865 Desc.URI = URI;
866
867 string Final = _config->FindDir("Dir::State::lists");
868 Final += URItoFileName(RealURI);
869 struct stat Buf;
870 if (stat(Final.c_str(),&Buf) == 0)
871 {
872 // File was already in place. It needs to be re-downloaded/verified
873 // because Release might have changed, we do give it a differnt
874 // name than DestFile because otherwise the http method will
875 // send If-Range requests and there are too many broken servers
876 // out there that do not understand them
877 LastGoodSig = DestFile+".reverify";
878 Rename(Final,LastGoodSig);
879 }
880
881 QueueURI(Desc);
882 }
883 /*}}}*/
884 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
885 // ---------------------------------------------------------------------
886 /* The only header we use is the last-modified header. */
887 string pkgAcqMetaSig::Custom600Headers()
888 {
889 struct stat Buf;
890 if (stat(LastGoodSig.c_str(),&Buf) != 0)
891 return "\nIndex-File: true";
892
893 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
894 }
895
896 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
897 pkgAcquire::MethodConfig *Cfg)
898 {
899 Item::Done(Message,Size,MD5,Cfg);
900
901 string FileName = LookupTag(Message,"Filename");
902 if (FileName.empty() == true)
903 {
904 Status = StatError;
905 ErrorText = "Method gave a blank filename";
906 return;
907 }
908
909 if (FileName != DestFile)
910 {
911 // We have to copy it into place
912 Local = true;
913 Desc.URI = "copy:" + FileName;
914 QueueURI(Desc);
915 return;
916 }
917
918 Complete = true;
919
920 // put the last known good file back on i-m-s hit (it will
921 // be re-verified again)
922 // Else do nothing, we have the new file in DestFile then
923 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
924 Rename(LastGoodSig, DestFile);
925
926 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
927 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc,
928 MetaIndexShortDesc, DestFile, IndexTargets,
929 MetaIndexParser);
930
931 }
932 /*}}}*/
933 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
934 {
935 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
936
937 // if we get a network error we fail gracefully
938 if(Status == StatTransientNetworkError)
939 {
940 Item::Failed(Message,Cnf);
941 // move the sigfile back on transient network failures
942 if(FileExists(LastGoodSig))
943 Rename(LastGoodSig,Final);
944
945 // set the status back to , Item::Failed likes to reset it
946 Status = pkgAcquire::Item::StatTransientNetworkError;
947 return;
948 }
949
950 // Delete any existing sigfile when the acquire failed
951 unlink(Final.c_str());
952
953 // queue a pkgAcqMetaIndex with no sigfile
954 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
955 "", IndexTargets, MetaIndexParser);
956
957 if (Cnf->LocalOnly == true ||
958 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
959 {
960 // Ignore this
961 Status = StatDone;
962 Complete = false;
963 Dequeue();
964 return;
965 }
966
967 Item::Failed(Message,Cnf);
968 }
969 /*}}}*/
970 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
971 string URI,string URIDesc,string ShortDesc,
972 string SigFile,
973 const vector<struct IndexTarget*>* IndexTargets,
974 indexRecords* MetaIndexParser) :
975 Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
976 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
977 {
978 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
979 DestFile += URItoFileName(URI);
980
981 // Create the item
982 Desc.Description = URIDesc;
983 Desc.Owner = this;
984 Desc.ShortDesc = ShortDesc;
985 Desc.URI = URI;
986
987 QueueURI(Desc);
988 }
989 /*}}}*/
990 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
991 // ---------------------------------------------------------------------
992 /* The only header we use is the last-modified header. */
993 string pkgAcqMetaIndex::Custom600Headers()
994 {
995 string Final = _config->FindDir("Dir::State::lists");
996 Final += URItoFileName(RealURI);
997
998 struct stat Buf;
999 if (stat(Final.c_str(),&Buf) != 0)
1000 return "\nIndex-File: true";
1001
1002 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1003 }
1004 /*}}}*/
1005 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, /*{{{*/
1006 pkgAcquire::MethodConfig *Cfg)
1007 {
1008 Item::Done(Message,Size,Hash,Cfg);
1009
1010 // MetaIndexes are done in two passes: one to download the
1011 // metaindex with an appropriate method, and a second to verify it
1012 // with the gpgv method
1013
1014 if (AuthPass == true)
1015 {
1016 AuthDone(Message);
1017
1018 // all cool, move Release file into place
1019 Complete = true;
1020
1021 string FinalFile = _config->FindDir("Dir::State::lists");
1022 FinalFile += URItoFileName(RealURI);
1023 Rename(DestFile,FinalFile);
1024 chmod(FinalFile.c_str(),0644);
1025 DestFile = FinalFile;
1026 }
1027 else
1028 {
1029 RetrievalDone(Message);
1030 if (!Complete)
1031 // Still more retrieving to do
1032 return;
1033
1034 if (SigFile == "")
1035 {
1036 // There was no signature file, so we are finished. Download
1037 // the indexes without verification.
1038 QueueIndexes(false);
1039 }
1040 else
1041 {
1042 // There was a signature file, so pass it to gpgv for
1043 // verification
1044
1045 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1046 std::cerr << "Metaindex acquired, queueing gpg verification ("
1047 << SigFile << "," << DestFile << ")\n";
1048 AuthPass = true;
1049 Desc.URI = "gpgv:" + SigFile;
1050 QueueURI(Desc);
1051 Mode = "gpgv";
1052 }
1053 }
1054 }
1055 /*}}}*/
1056 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1057 {
1058 // We have just finished downloading a Release file (it is not
1059 // verified yet)
1060
1061 string FileName = LookupTag(Message,"Filename");
1062 if (FileName.empty() == true)
1063 {
1064 Status = StatError;
1065 ErrorText = "Method gave a blank filename";
1066 return;
1067 }
1068
1069 if (FileName != DestFile)
1070 {
1071 Local = true;
1072 Desc.URI = "copy:" + FileName;
1073 QueueURI(Desc);
1074 return;
1075 }
1076
1077 // make sure to verify against the right file on I-M-S hit
1078 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1079 if(IMSHit)
1080 {
1081 string FinalFile = _config->FindDir("Dir::State::lists");
1082 FinalFile += URItoFileName(RealURI);
1083 DestFile = FinalFile;
1084 }
1085 Complete = true;
1086 }
1087 /*}}}*/
1088 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1089 {
1090 // At this point, the gpgv method has succeeded, so there is a
1091 // valid signature from a key in the trusted keyring. We
1092 // perform additional verification of its contents, and use them
1093 // to verify the indexes we are about to download
1094
1095 if (!MetaIndexParser->Load(DestFile))
1096 {
1097 Status = StatAuthError;
1098 ErrorText = MetaIndexParser->ErrorText;
1099 return;
1100 }
1101
1102 if (!VerifyVendor(Message))
1103 {
1104 return;
1105 }
1106
1107 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1108 std::cerr << "Signature verification succeeded: "
1109 << DestFile << std::endl;
1110
1111 // Download further indexes with verification
1112 QueueIndexes(true);
1113
1114 // Done, move signature file into position
1115 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1116 URItoFileName(RealURI) + ".gpg";
1117 Rename(SigFile,VerifiedSigFile);
1118 chmod(VerifiedSigFile.c_str(),0644);
1119 }
1120 /*}}}*/
1121 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1122 {
1123 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1124 Target != IndexTargets->end();
1125 Target++)
1126 {
1127 HashString ExpectedIndexHash;
1128 if (verify)
1129 {
1130 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1131 if (!Record)
1132 {
1133 Status = StatAuthError;
1134 ErrorText = "Unable to find expected entry "
1135 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
1136 return;
1137 }
1138 ExpectedIndexHash = Record->Hash;
1139 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1140 {
1141 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
1142 std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
1143 }
1144 if (ExpectedIndexHash.empty())
1145 {
1146 Status = StatAuthError;
1147 ErrorText = "Unable to find hash sum for "
1148 + (*Target)->MetaKey + " in Meta-index file";
1149 return;
1150 }
1151 }
1152
1153 // Queue Packages file (either diff or full packages files, depending
1154 // on the users option)
1155 if(_config->FindB("Acquire::PDiffs",false) == true)
1156 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
1157 (*Target)->ShortDesc, ExpectedIndexHash);
1158 else
1159 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
1160 (*Target)->ShortDesc, ExpectedIndexHash);
1161 }
1162 }
1163 /*}}}*/
1164 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1165 {
1166 // // Maybe this should be made available from above so we don't have
1167 // // to read and parse it every time?
1168 // pkgVendorList List;
1169 // List.ReadMainList();
1170
1171 // const Vendor* Vndr = NULL;
1172 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
1173 // {
1174 // string::size_type pos = (*I).find("VALIDSIG ");
1175 // if (_config->FindB("Debug::Vendor", false))
1176 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
1177 // << std::endl;
1178 // if (pos != std::string::npos)
1179 // {
1180 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
1181 // if (_config->FindB("Debug::Vendor", false))
1182 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
1183 // std::endl;
1184 // Vndr = List.FindVendor(Fingerprint) != "";
1185 // if (Vndr != NULL);
1186 // break;
1187 // }
1188 // }
1189 string::size_type pos;
1190
1191 // check for missing sigs (that where not fatal because otherwise we had
1192 // bombed earlier)
1193 string missingkeys;
1194 string msg = _("There is no public key available for the "
1195 "following key IDs:\n");
1196 pos = Message.find("NO_PUBKEY ");
1197 if (pos != std::string::npos)
1198 {
1199 string::size_type start = pos+strlen("NO_PUBKEY ");
1200 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1201 missingkeys += (Fingerprint);
1202 }
1203 if(!missingkeys.empty())
1204 _error->Warning("%s", string(msg+missingkeys).c_str());
1205
1206 string Transformed = MetaIndexParser->GetExpectedDist();
1207
1208 if (Transformed == "../project/experimental")
1209 {
1210 Transformed = "experimental";
1211 }
1212
1213 pos = Transformed.rfind('/');
1214 if (pos != string::npos)
1215 {
1216 Transformed = Transformed.substr(0, pos);
1217 }
1218
1219 if (Transformed == ".")
1220 {
1221 Transformed = "";
1222 }
1223
1224 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1225 {
1226 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1227 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1228 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1229 }
1230
1231 if (MetaIndexParser->CheckDist(Transformed) == false)
1232 {
1233 // This might become fatal one day
1234 // Status = StatAuthError;
1235 // ErrorText = "Conflicting distribution; expected "
1236 // + MetaIndexParser->GetExpectedDist() + " but got "
1237 // + MetaIndexParser->GetDist();
1238 // return false;
1239 if (!Transformed.empty())
1240 {
1241 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
1242 Desc.Description.c_str(),
1243 Transformed.c_str(),
1244 MetaIndexParser->GetDist().c_str());
1245 }
1246 }
1247
1248 return true;
1249 }
1250 /*}}}*/
1251 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1252 // ---------------------------------------------------------------------
1253 /* */
1254 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1255 {
1256 if (AuthPass == true)
1257 {
1258 // gpgv method failed, if we have a good signature
1259 string LastGoodSigFile = _config->FindDir("Dir::State::lists") +
1260 "partial/" + URItoFileName(RealURI) + ".gpg.reverify";
1261 if(FileExists(LastGoodSigFile))
1262 {
1263 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1264 URItoFileName(RealURI) + ".gpg";
1265 Rename(LastGoodSigFile,VerifiedSigFile);
1266 Status = StatTransientNetworkError;
1267 _error->Warning(_("A error occurred during the signature "
1268 "verification. The repository is not updated "
1269 "and the previous index files will be used."
1270 "GPG error: %s: %s\n"),
1271 Desc.Description.c_str(),
1272 LookupTag(Message,"Message").c_str());
1273 RunScripts("APT::Update::Auth-Failure");
1274 return;
1275 } else {
1276 _error->Warning(_("GPG error: %s: %s"),
1277 Desc.Description.c_str(),
1278 LookupTag(Message,"Message").c_str());
1279 }
1280 // gpgv method failed
1281 ReportMirrorFailure("GPGFailure");
1282 }
1283
1284 // No Release file was present, or verification failed, so fall
1285 // back to queueing Packages files without verification
1286 QueueIndexes(false);
1287 }
1288 /*}}}*/
1289 // AcqArchive::AcqArchive - Constructor /*{{{*/
1290 // ---------------------------------------------------------------------
1291 /* This just sets up the initial fetch environment and queues the first
1292 possibilitiy */
1293 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1294 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1295 string &StoreFilename) :
1296 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
1297 StoreFilename(StoreFilename), Vf(Version.FileList()),
1298 Trusted(false)
1299 {
1300 Retries = _config->FindI("Acquire::Retries",0);
1301
1302 if (Version.Arch() == 0)
1303 {
1304 _error->Error(_("I wasn't able to locate a file for the %s package. "
1305 "This might mean you need to manually fix this package. "
1306 "(due to missing arch)"),
1307 Version.ParentPkg().Name());
1308 return;
1309 }
1310
1311 /* We need to find a filename to determine the extension. We make the
1312 assumption here that all the available sources for this version share
1313 the same extension.. */
1314 // Skip not source sources, they do not have file fields.
1315 for (; Vf.end() == false; Vf++)
1316 {
1317 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1318 continue;
1319 break;
1320 }
1321
1322 // Does not really matter here.. we are going to fail out below
1323 if (Vf.end() != true)
1324 {
1325 // If this fails to get a file name we will bomb out below.
1326 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1327 if (_error->PendingError() == true)
1328 return;
1329
1330 // Generate the final file name as: package_version_arch.foo
1331 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1332 QuoteString(Version.VerStr(),"_:") + '_' +
1333 QuoteString(Version.Arch(),"_:.") +
1334 "." + flExtension(Parse.FileName());
1335 }
1336
1337 // check if we have one trusted source for the package. if so, switch
1338 // to "TrustedOnly" mode
1339 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
1340 {
1341 pkgIndexFile *Index;
1342 if (Sources->FindIndex(i.File(),Index) == false)
1343 continue;
1344 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1345 {
1346 std::cerr << "Checking index: " << Index->Describe()
1347 << "(Trusted=" << Index->IsTrusted() << ")\n";
1348 }
1349 if (Index->IsTrusted()) {
1350 Trusted = true;
1351 break;
1352 }
1353 }
1354
1355 // "allow-unauthenticated" restores apts old fetching behaviour
1356 // that means that e.g. unauthenticated file:// uris are higher
1357 // priority than authenticated http:// uris
1358 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
1359 Trusted = false;
1360
1361 // Select a source
1362 if (QueueNext() == false && _error->PendingError() == false)
1363 _error->Error(_("I wasn't able to locate file for the %s package. "
1364 "This might mean you need to manually fix this package."),
1365 Version.ParentPkg().Name());
1366 }
1367 /*}}}*/
1368 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1369 // ---------------------------------------------------------------------
1370 /* This queues the next available file version for download. It checks if
1371 the archive is already available in the cache and stashs the MD5 for
1372 checking later. */
1373 bool pkgAcqArchive::QueueNext()
1374 {
1375 for (; Vf.end() == false; Vf++)
1376 {
1377 // Ignore not source sources
1378 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1379 continue;
1380
1381 // Try to cross match against the source list
1382 pkgIndexFile *Index;
1383 if (Sources->FindIndex(Vf.File(),Index) == false)
1384 continue;
1385
1386 // only try to get a trusted package from another source if that source
1387 // is also trusted
1388 if(Trusted && !Index->IsTrusted())
1389 continue;
1390
1391 // Grab the text package record
1392 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1393 if (_error->PendingError() == true)
1394 return false;
1395
1396 string PkgFile = Parse.FileName();
1397 if(Parse.SHA256Hash() != "")
1398 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
1399 else if (Parse.SHA1Hash() != "")
1400 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
1401 else
1402 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1403 if (PkgFile.empty() == true)
1404 return _error->Error(_("The package index files are corrupted. No Filename: "
1405 "field for package %s."),
1406 Version.ParentPkg().Name());
1407
1408 Desc.URI = Index->ArchiveURI(PkgFile);
1409 Desc.Description = Index->ArchiveInfo(Version);
1410 Desc.Owner = this;
1411 Desc.ShortDesc = Version.ParentPkg().Name();
1412
1413 // See if we already have the file. (Legacy filenames)
1414 FileSize = Version->Size;
1415 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1416 struct stat Buf;
1417 if (stat(FinalFile.c_str(),&Buf) == 0)
1418 {
1419 // Make sure the size matches
1420 if ((unsigned)Buf.st_size == Version->Size)
1421 {
1422 Complete = true;
1423 Local = true;
1424 Status = StatDone;
1425 StoreFilename = DestFile = FinalFile;
1426 return true;
1427 }
1428
1429 /* Hmm, we have a file and its size does not match, this means it is
1430 an old style mismatched arch */
1431 unlink(FinalFile.c_str());
1432 }
1433
1434 // Check it again using the new style output filenames
1435 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1436 if (stat(FinalFile.c_str(),&Buf) == 0)
1437 {
1438 // Make sure the size matches
1439 if ((unsigned)Buf.st_size == Version->Size)
1440 {
1441 Complete = true;
1442 Local = true;
1443 Status = StatDone;
1444 StoreFilename = DestFile = FinalFile;
1445 return true;
1446 }
1447
1448 /* Hmm, we have a file and its size does not match, this shouldnt
1449 happen.. */
1450 unlink(FinalFile.c_str());
1451 }
1452
1453 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
1454
1455 // Check the destination file
1456 if (stat(DestFile.c_str(),&Buf) == 0)
1457 {
1458 // Hmm, the partial file is too big, erase it
1459 if ((unsigned)Buf.st_size > Version->Size)
1460 unlink(DestFile.c_str());
1461 else
1462 PartialSize = Buf.st_size;
1463 }
1464
1465 // Create the item
1466 Local = false;
1467 Desc.URI = Index->ArchiveURI(PkgFile);
1468 Desc.Description = Index->ArchiveInfo(Version);
1469 Desc.Owner = this;
1470 Desc.ShortDesc = Version.ParentPkg().Name();
1471 QueueURI(Desc);
1472
1473 Vf++;
1474 return true;
1475 }
1476 return false;
1477 }
1478 /*}}}*/
1479 // AcqArchive::Done - Finished fetching /*{{{*/
1480 // ---------------------------------------------------------------------
1481 /* */
1482 void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash,
1483 pkgAcquire::MethodConfig *Cfg)
1484 {
1485 Item::Done(Message,Size,CalcHash,Cfg);
1486
1487 // Check the size
1488 if (Size != Version->Size)
1489 {
1490 Status = StatError;
1491 ErrorText = _("Size mismatch");
1492 return;
1493 }
1494
1495 // Check the hash
1496 if(ExpectedHash.toStr() != CalcHash)
1497 {
1498 Status = StatError;
1499 ErrorText = _("Hash Sum mismatch");
1500 if(FileExists(DestFile))
1501 Rename(DestFile,DestFile + ".FAILED");
1502 return;
1503 }
1504
1505 // Grab the output filename
1506 string FileName = LookupTag(Message,"Filename");
1507 if (FileName.empty() == true)
1508 {
1509 Status = StatError;
1510 ErrorText = "Method gave a blank filename";
1511 return;
1512 }
1513
1514 Complete = true;
1515
1516 // Reference filename
1517 if (FileName != DestFile)
1518 {
1519 StoreFilename = DestFile = FileName;
1520 Local = true;
1521 return;
1522 }
1523
1524 // Done, move it into position
1525 string FinalFile = _config->FindDir("Dir::Cache::Archives");
1526 FinalFile += flNotDir(StoreFilename);
1527 Rename(DestFile,FinalFile);
1528
1529 StoreFilename = DestFile = FinalFile;
1530 Complete = true;
1531 }
1532 /*}}}*/
1533 // AcqArchive::Failed - Failure handler /*{{{*/
1534 // ---------------------------------------------------------------------
1535 /* Here we try other sources */
1536 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1537 {
1538 ErrorText = LookupTag(Message,"Message");
1539
1540 /* We don't really want to retry on failed media swaps, this prevents
1541 that. An interesting observation is that permanent failures are not
1542 recorded. */
1543 if (Cnf->Removable == true &&
1544 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1545 {
1546 // Vf = Version.FileList();
1547 while (Vf.end() == false) Vf++;
1548 StoreFilename = string();
1549 Item::Failed(Message,Cnf);
1550 return;
1551 }
1552
1553 if (QueueNext() == false)
1554 {
1555 // This is the retry counter
1556 if (Retries != 0 &&
1557 Cnf->LocalOnly == false &&
1558 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1559 {
1560 Retries--;
1561 Vf = Version.FileList();
1562 if (QueueNext() == true)
1563 return;
1564 }
1565
1566 StoreFilename = string();
1567 Item::Failed(Message,Cnf);
1568 }
1569 }
1570 /*}}}*/
1571 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
1572 // ---------------------------------------------------------------------
1573 bool pkgAcqArchive::IsTrusted()
1574 {
1575 return Trusted;
1576 }
1577 /*}}}*/
1578 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1579 // ---------------------------------------------------------------------
1580 /* */
1581 void pkgAcqArchive::Finished()
1582 {
1583 if (Status == pkgAcquire::Item::StatDone &&
1584 Complete == true)
1585 return;
1586 StoreFilename = string();
1587 }
1588 /*}}}*/
1589 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1590 // ---------------------------------------------------------------------
1591 /* The file is added to the queue */
1592 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
1593 unsigned long Size,string Dsc,string ShortDesc,
1594 const string &DestDir, const string &DestFilename) :
1595 Item(Owner), ExpectedHash(Hash)
1596 {
1597 Retries = _config->FindI("Acquire::Retries",0);
1598
1599 if(!DestFilename.empty())
1600 DestFile = DestFilename;
1601 else if(!DestDir.empty())
1602 DestFile = DestDir + "/" + flNotDir(URI);
1603 else
1604 DestFile = flNotDir(URI);
1605
1606 // Create the item
1607 Desc.URI = URI;
1608 Desc.Description = Dsc;
1609 Desc.Owner = this;
1610
1611 // Set the short description to the archive component
1612 Desc.ShortDesc = ShortDesc;
1613
1614 // Get the transfer sizes
1615 FileSize = Size;
1616 struct stat Buf;
1617 if (stat(DestFile.c_str(),&Buf) == 0)
1618 {
1619 // Hmm, the partial file is too big, erase it
1620 if ((unsigned)Buf.st_size > Size)
1621 unlink(DestFile.c_str());
1622 else
1623 PartialSize = Buf.st_size;
1624 }
1625
1626 QueueURI(Desc);
1627 }
1628 /*}}}*/
1629 // AcqFile::Done - Item downloaded OK /*{{{*/
1630 // ---------------------------------------------------------------------
1631 /* */
1632 void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash,
1633 pkgAcquire::MethodConfig *Cnf)
1634 {
1635 Item::Done(Message,Size,CalcHash,Cnf);
1636
1637 // Check the hash
1638 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
1639 {
1640 Status = StatError;
1641 ErrorText = "Hash Sum mismatch";
1642 Rename(DestFile,DestFile + ".FAILED");
1643 return;
1644 }
1645
1646 string FileName = LookupTag(Message,"Filename");
1647 if (FileName.empty() == true)
1648 {
1649 Status = StatError;
1650 ErrorText = "Method gave a blank filename";
1651 return;
1652 }
1653
1654 Complete = true;
1655
1656 // The files timestamp matches
1657 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1658 return;
1659
1660 // We have to copy it into place
1661 if (FileName != DestFile)
1662 {
1663 Local = true;
1664 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1665 Cnf->Removable == true)
1666 {
1667 Desc.URI = "copy:" + FileName;
1668 QueueURI(Desc);
1669 return;
1670 }
1671
1672 // Erase the file if it is a symlink so we can overwrite it
1673 struct stat St;
1674 if (lstat(DestFile.c_str(),&St) == 0)
1675 {
1676 if (S_ISLNK(St.st_mode) != 0)
1677 unlink(DestFile.c_str());
1678 }
1679
1680 // Symlink the file
1681 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1682 {
1683 ErrorText = "Link to " + DestFile + " failure ";
1684 Status = StatError;
1685 Complete = false;
1686 }
1687 }
1688 }
1689 /*}}}*/
1690 // AcqFile::Failed - Failure handler /*{{{*/
1691 // ---------------------------------------------------------------------
1692 /* Here we try other sources */
1693 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1694 {
1695 ErrorText = LookupTag(Message,"Message");
1696
1697 // This is the retry counter
1698 if (Retries != 0 &&
1699 Cnf->LocalOnly == false &&
1700 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1701 {
1702 Retries--;
1703 QueueURI(Desc);
1704 return;
1705 }
1706
1707 Item::Failed(Message,Cnf);
1708 }
1709 /*}}}*/