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