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