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