* make apt-get source smarter about not downloading the same sources twice
[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 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire-item.h"
18 #endif
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/vendorlist.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/fileutl.h>
26 #include <apt-pkg/md5.h>
27
28 #include <apti18n.h>
29
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string>
34 #include <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,
99 pkgAcquire::MethodConfig *Cnf)
100 {
101 // We just downloaded something..
102 string FileName = LookupTag(Message,"Filename");
103 if (Complete == false && FileName == DestFile)
104 {
105 if (Owner->Log != 0)
106 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
107 }
108
109 if (FileSize == 0)
110 FileSize= Size;
111
112 Status = StatDone;
113 ErrorText = string();
114 Owner->Dequeue(this);
115 }
116 /*}}}*/
117 // Acquire::Item::Rename - Rename a file /*{{{*/
118 // ---------------------------------------------------------------------
119 /* This helper function is used by alot of item methods as thier final
120 step */
121 void pkgAcquire::Item::Rename(string From,string To)
122 {
123 if (rename(From.c_str(),To.c_str()) != 0)
124 {
125 char S[300];
126 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
127 From.c_str(),To.c_str());
128 Status = StatError;
129 ErrorText = S;
130 }
131 }
132 /*}}}*/
133
134 // AcqIndex::AcqIndex - Constructor /*{{{*/
135 // ---------------------------------------------------------------------
136 /* The package file is added to the queue and a second class is
137 instantiated to fetch the revision file */
138 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
139 string URI,string URIDesc,string ShortDesc,
140 string ExpectedMD5, string comprExt) :
141 Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
142 {
143 Decompression = false;
144 Erase = false;
145
146 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
147 DestFile += URItoFileName(URI);
148
149 if(comprExt.empty())
150 {
151 // autoselect the compression method
152 if(FileExists("/usr/bin/bzip2"))
153 CompressionExtension = ".bz2";
154 else
155 CompressionExtension = ".gz";
156 } else {
157 CompressionExtension = comprExt;
158 }
159 Desc.URI = URI + CompressionExtension;
160
161 Desc.Description = URIDesc;
162 Desc.Owner = this;
163 Desc.ShortDesc = ShortDesc;
164
165 QueueURI(Desc);
166 }
167 /*}}}*/
168 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
169 // ---------------------------------------------------------------------
170 /* The only header we use is the last-modified header. */
171 string pkgAcqIndex::Custom600Headers()
172 {
173 string Final = _config->FindDir("Dir::State::lists");
174 Final += URItoFileName(RealURI);
175
176 struct stat Buf;
177 if (stat(Final.c_str(),&Buf) != 0)
178 return "\nIndex-File: true";
179
180 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
181 }
182 /*}}}*/
183
184 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
185 {
186 // no .bz2 found, retry with .gz
187 if(Desc.URI.substr(Desc.URI.size()-3) == "bz2") {
188 Desc.URI = Desc.URI.substr(0,Desc.URI.size()-3) + "gz";
189
190 // retry with a gzip one
191 new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc,
192 ExpectedMD5, string(".gz"));
193 Status = StatDone;
194 Complete = false;
195 Dequeue();
196 return;
197 }
198
199
200 Item::Failed(Message,Cnf);
201 }
202
203
204 // AcqIndex::Done - Finished a fetch /*{{{*/
205 // ---------------------------------------------------------------------
206 /* This goes through a number of states.. On the initial fetch the
207 method could possibly return an alternate filename which points
208 to the uncompressed version of the file. If this is so the file
209 is copied into the partial directory. In all other cases the file
210 is decompressed with a gzip uri. */
211 void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
212 pkgAcquire::MethodConfig *Cfg)
213 {
214 Item::Done(Message,Size,MD5,Cfg);
215
216 if (Decompression == true)
217 {
218 if (_config->FindB("Debug::pkgAcquire::Auth", false))
219 {
220 std::cerr << std::endl << RealURI << ": Computed MD5: " << MD5;
221 std::cerr << " Expected MD5: " << ExpectedMD5 << std::endl;
222 }
223
224 if (MD5.empty())
225 {
226 MD5Summation sum;
227 FileFd Fd(DestFile, FileFd::ReadOnly);
228 sum.AddFD(Fd.Fd(), Fd.Size());
229 Fd.Close();
230 MD5 = (string)sum.Result();
231 }
232
233 if (!ExpectedMD5.empty() && MD5 != ExpectedMD5)
234 {
235 Status = StatAuthError;
236 ErrorText = _("MD5Sum mismatch");
237 Rename(DestFile,DestFile + ".FAILED");
238 return;
239 }
240 // Done, move it into position
241 string FinalFile = _config->FindDir("Dir::State::lists");
242 FinalFile += URItoFileName(RealURI);
243 Rename(DestFile,FinalFile);
244 chmod(FinalFile.c_str(),0644);
245
246 /* We restore the original name to DestFile so that the clean operation
247 will work OK */
248 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
249 DestFile += URItoFileName(RealURI);
250
251 // Remove the compressed version.
252 if (Erase == true)
253 unlink(DestFile.c_str());
254 return;
255 }
256
257 Erase = false;
258 Complete = true;
259
260 // Handle the unzipd case
261 string FileName = LookupTag(Message,"Alt-Filename");
262 if (FileName.empty() == false)
263 {
264 // The files timestamp matches
265 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
266 return;
267
268 Decompression = true;
269 Local = true;
270 DestFile += ".decomp";
271 Desc.URI = "copy:" + FileName;
272 QueueURI(Desc);
273 Mode = "copy";
274 return;
275 }
276
277 FileName = LookupTag(Message,"Filename");
278 if (FileName.empty() == true)
279 {
280 Status = StatError;
281 ErrorText = "Method gave a blank filename";
282 }
283
284 // The files timestamp matches
285 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
286 return;
287
288 if (FileName == DestFile)
289 Erase = true;
290 else
291 Local = true;
292
293 string compExt = Desc.URI.substr(Desc.URI.size()-3);
294 char *decompProg;
295 if(compExt == "bz2")
296 decompProg = "bzip2";
297 else if(compExt == ".gz")
298 decompProg = "gzip";
299 else {
300 _error->Error("Unsupported extension: %s", compExt.c_str());
301 return;
302 }
303
304 Decompression = true;
305 DestFile += ".decomp";
306 Desc.URI = string(decompProg) + ":" + FileName;
307 QueueURI(Desc);
308 Mode = decompProg;
309 }
310
311 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
312 string URI,string URIDesc,string ShortDesc,
313 string MetaIndexURI, string MetaIndexURIDesc,
314 string MetaIndexShortDesc,
315 const vector<IndexTarget*>* IndexTargets,
316 indexRecords* MetaIndexParser) :
317 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
318 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
319 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
320 {
321 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
322 DestFile += URItoFileName(URI);
323
324 // remove any partial downloaded sig-file. it may confuse proxies
325 // and is too small to warrant a partial download anyway
326 unlink(DestFile.c_str());
327
328 // Create the item
329 Desc.Description = URIDesc;
330 Desc.Owner = this;
331 Desc.ShortDesc = ShortDesc;
332 Desc.URI = URI;
333
334
335 string Final = _config->FindDir("Dir::State::lists");
336 Final += URItoFileName(RealURI);
337 struct stat Buf;
338 if (stat(Final.c_str(),&Buf) == 0)
339 {
340 // File was already in place. It needs to be re-verified
341 // because Release might have changed, so Move it into partial
342 Rename(Final,DestFile);
343 // unlink the file and do not try to use I-M-S and Last-Modified
344 // if the users proxy is broken
345 if(_config->FindB("Acquire::BrokenProxy", false) == true) {
346 std::cerr << "forcing re-get of the signature file as requested" << std::endl;
347 unlink(DestFile.c_str());
348 }
349 }
350
351 QueueURI(Desc);
352 }
353 /*}}}*/
354 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
355 // ---------------------------------------------------------------------
356 /* The only header we use is the last-modified header. */
357 string pkgAcqMetaSig::Custom600Headers()
358 {
359 struct stat Buf;
360 if (stat(DestFile.c_str(),&Buf) != 0)
361 return "\nIndex-File: true";
362
363 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
364 }
365
366 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
367 pkgAcquire::MethodConfig *Cfg)
368 {
369 Item::Done(Message,Size,MD5,Cfg);
370
371 string FileName = LookupTag(Message,"Filename");
372 if (FileName.empty() == true)
373 {
374 Status = StatError;
375 ErrorText = "Method gave a blank filename";
376 return;
377 }
378
379 if (FileName != DestFile)
380 {
381 // We have to copy it into place
382 Local = true;
383 Desc.URI = "copy:" + FileName;
384 QueueURI(Desc);
385 return;
386 }
387
388 Complete = true;
389
390 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
391 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
392 DestFile, IndexTargets, MetaIndexParser);
393
394 }
395 /*}}}*/
396 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
397 {
398 // Delete any existing sigfile, so that this source isn't
399 // mistakenly trusted
400 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
401 unlink(Final.c_str());
402
403 // if we get a timeout if fail
404 if(LookupTag(Message,"FailReason") == "Timeout" ||
405 LookupTag(Message,"FailReason") == "TmpResolveFailure") {
406 Item::Failed(Message,Cnf);
407 return;
408 }
409
410 // queue a pkgAcqMetaIndex with no sigfile
411 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
412 "", IndexTargets, MetaIndexParser);
413
414 if (Cnf->LocalOnly == true ||
415 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
416 {
417 // Ignore this
418 Status = StatDone;
419 Complete = false;
420 Dequeue();
421 return;
422 }
423
424 Item::Failed(Message,Cnf);
425 }
426
427 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,
428 string URI,string URIDesc,string ShortDesc,
429 string SigFile,
430 const vector<struct IndexTarget*>* IndexTargets,
431 indexRecords* MetaIndexParser) :
432 Item(Owner), RealURI(URI), SigFile(SigFile), AuthPass(false),
433 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
434 {
435 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
436 DestFile += URItoFileName(URI);
437
438 // Create the item
439 Desc.Description = URIDesc;
440 Desc.Owner = this;
441 Desc.ShortDesc = ShortDesc;
442 Desc.URI = URI;
443
444 QueueURI(Desc);
445 }
446
447 /*}}}*/
448 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
449 // ---------------------------------------------------------------------
450 /* The only header we use is the last-modified header. */
451 string pkgAcqMetaIndex::Custom600Headers()
452 {
453 string Final = _config->FindDir("Dir::State::lists");
454 Final += URItoFileName(RealURI);
455
456 struct stat Buf;
457 if (stat(Final.c_str(),&Buf) != 0)
458 return "\nIndex-File: true";
459
460 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
461 }
462
463 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string MD5,
464 pkgAcquire::MethodConfig *Cfg)
465 {
466 Item::Done(Message,Size,MD5,Cfg);
467
468 // MetaIndexes are done in two passes: one to download the
469 // metaindex with an appropriate method, and a second to verify it
470 // with the gpgv method
471
472 if (AuthPass == true)
473 {
474 AuthDone(Message);
475 }
476 else
477 {
478 RetrievalDone(Message);
479 if (!Complete)
480 // Still more retrieving to do
481 return;
482
483 if (SigFile == "")
484 {
485 // There was no signature file, so we are finished. Download
486 // the indexes without verification.
487 QueueIndexes(false);
488 }
489 else
490 {
491 // There was a signature file, so pass it to gpgv for
492 // verification
493
494 if (_config->FindB("Debug::pkgAcquire::Auth", false))
495 std::cerr << "Metaindex acquired, queueing gpg verification ("
496 << SigFile << "," << DestFile << ")\n";
497 AuthPass = true;
498 Desc.URI = "gpgv:" + SigFile;
499 QueueURI(Desc);
500 Mode = "gpgv";
501 }
502 }
503 }
504
505 void pkgAcqMetaIndex::RetrievalDone(string Message)
506 {
507 // We have just finished downloading a Release file (it is not
508 // verified yet)
509
510 string FileName = LookupTag(Message,"Filename");
511 if (FileName.empty() == true)
512 {
513 Status = StatError;
514 ErrorText = "Method gave a blank filename";
515 return;
516 }
517
518 if (FileName != DestFile)
519 {
520 Local = true;
521 Desc.URI = "copy:" + FileName;
522 QueueURI(Desc);
523 return;
524 }
525
526 Complete = true;
527
528 string FinalFile = _config->FindDir("Dir::State::lists");
529 FinalFile += URItoFileName(RealURI);
530
531 // The files timestamp matches
532 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == false)
533 {
534 // Move it into position
535 Rename(DestFile,FinalFile);
536 }
537 DestFile = FinalFile;
538 }
539
540 void pkgAcqMetaIndex::AuthDone(string Message)
541 {
542 // At this point, the gpgv method has succeeded, so there is a
543 // valid signature from a key in the trusted keyring. We
544 // perform additional verification of its contents, and use them
545 // to verify the indexes we are about to download
546
547 if (!MetaIndexParser->Load(DestFile))
548 {
549 Status = StatAuthError;
550 ErrorText = MetaIndexParser->ErrorText;
551 return;
552 }
553
554 if (!VerifyVendor())
555 {
556 return;
557 }
558
559 if (_config->FindB("Debug::pkgAcquire::Auth", false))
560 std::cerr << "Signature verification succeeded: "
561 << DestFile << std::endl;
562
563 // Download further indexes with verification
564 QueueIndexes(true);
565
566 // Done, move signature file into position
567
568 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
569 URItoFileName(RealURI) + ".gpg";
570 Rename(SigFile,VerifiedSigFile);
571 chmod(VerifiedSigFile.c_str(),0644);
572 }
573
574 void pkgAcqMetaIndex::QueueIndexes(bool verify)
575 {
576 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
577 Target != IndexTargets->end();
578 Target++)
579 {
580 string ExpectedIndexMD5;
581 if (verify)
582 {
583 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
584 if (!Record)
585 {
586 Status = StatAuthError;
587 ErrorText = "Unable to find expected entry "
588 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
589 return;
590 }
591 ExpectedIndexMD5 = Record->MD5Hash;
592 if (_config->FindB("Debug::pkgAcquire::Auth", false))
593 {
594 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
595 std::cerr << "Expected MD5: " << ExpectedIndexMD5 << std::endl;
596 }
597 if (ExpectedIndexMD5.empty())
598 {
599 Status = StatAuthError;
600 ErrorText = "Unable to find MD5 sum for "
601 + (*Target)->MetaKey + " in Meta-index file";
602 return;
603 }
604 }
605
606 // Queue Packages file
607 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
608 (*Target)->ShortDesc, ExpectedIndexMD5);
609 }
610 }
611
612 bool pkgAcqMetaIndex::VerifyVendor()
613 {
614 // // Maybe this should be made available from above so we don't have
615 // // to read and parse it every time?
616 // pkgVendorList List;
617 // List.ReadMainList();
618
619 // const Vendor* Vndr = NULL;
620 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
621 // {
622 // string::size_type pos = (*I).find("VALIDSIG ");
623 // if (_config->FindB("Debug::Vendor", false))
624 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
625 // << std::endl;
626 // if (pos != std::string::npos)
627 // {
628 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
629 // if (_config->FindB("Debug::Vendor", false))
630 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
631 // std::endl;
632 // Vndr = List.FindVendor(Fingerprint) != "";
633 // if (Vndr != NULL);
634 // break;
635 // }
636 // }
637
638 string Transformed = MetaIndexParser->GetExpectedDist();
639
640 if (Transformed == "../project/experimental")
641 {
642 Transformed = "experimental";
643 }
644
645 string::size_type pos = Transformed.rfind('/');
646 if (pos != string::npos)
647 {
648 Transformed = Transformed.substr(0, pos);
649 }
650
651 if (Transformed == ".")
652 {
653 Transformed = "";
654 }
655
656 if (_config->FindB("Debug::pkgAcquire::Auth", false))
657 {
658 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
659 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
660 std::cerr << "Transformed Dist: " << Transformed << std::endl;
661 }
662
663 if (MetaIndexParser->CheckDist(Transformed) == false)
664 {
665 // This might become fatal one day
666 // Status = StatAuthError;
667 // ErrorText = "Conflicting distribution; expected "
668 // + MetaIndexParser->GetExpectedDist() + " but got "
669 // + MetaIndexParser->GetDist();
670 // return false;
671 if (!Transformed.empty())
672 {
673 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
674 Desc.Description.c_str(),
675 Transformed.c_str(),
676 MetaIndexParser->GetDist().c_str());
677 }
678 }
679
680 return true;
681 }
682 /*}}}*/
683 // pkgAcqMetaIndex::Failed - no Release file present or no signature
684 // file present /*{{{*/
685 // ---------------------------------------------------------------------
686 /* */
687 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
688 {
689 if (AuthPass == true)
690 {
691 // gpgv method failed
692 _error->Warning("GPG error: %s: %s",
693 Desc.Description.c_str(),
694 LookupTag(Message,"Message").c_str());
695 }
696
697 // No Release file was present, or verification failed, so fall
698 // back to queueing Packages files without verification
699 QueueIndexes(false);
700 }
701
702 /*}}}*/
703
704 // AcqArchive::AcqArchive - Constructor /*{{{*/
705 // ---------------------------------------------------------------------
706 /* This just sets up the initial fetch environment and queues the first
707 possibilitiy */
708 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
709 pkgRecords *Recs,pkgCache::VerIterator const &Version,
710 string &StoreFilename) :
711 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
712 StoreFilename(StoreFilename), Vf(Version.FileList()),
713 Trusted(false)
714 {
715 Retries = _config->FindI("Acquire::Retries",0);
716
717 if (Version.Arch() == 0)
718 {
719 _error->Error(_("I wasn't able to locate a file for the %s package. "
720 "This might mean you need to manually fix this package. "
721 "(due to missing arch)"),
722 Version.ParentPkg().Name());
723 return;
724 }
725
726 /* We need to find a filename to determine the extension. We make the
727 assumption here that all the available sources for this version share
728 the same extension.. */
729 // Skip not source sources, they do not have file fields.
730 for (; Vf.end() == false; Vf++)
731 {
732 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
733 continue;
734 break;
735 }
736
737 // Does not really matter here.. we are going to fail out below
738 if (Vf.end() != true)
739 {
740 // If this fails to get a file name we will bomb out below.
741 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
742 if (_error->PendingError() == true)
743 return;
744
745 // Generate the final file name as: package_version_arch.foo
746 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
747 QuoteString(Version.VerStr(),"_:") + '_' +
748 QuoteString(Version.Arch(),"_:.") +
749 "." + flExtension(Parse.FileName());
750 }
751
752 // check if we have one trusted source for the package. if so, switch
753 // to "TrustedOnly" mode
754 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
755 {
756 pkgIndexFile *Index;
757 if (Sources->FindIndex(i.File(),Index) == false)
758 continue;
759 if (_config->FindB("Debug::pkgAcquire::Auth", false))
760 {
761 std::cerr << "Checking index: " << Index->Describe()
762 << "(Trusted=" << Index->IsTrusted() << ")\n";
763 }
764 if (Index->IsTrusted()) {
765 Trusted = true;
766 break;
767 }
768 }
769
770 // "allow-unauthenticated" restores apts old fetching behaviour
771 // that means that e.g. unauthenticated file:// uris are higher
772 // priority than authenticated http:// uris
773 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
774 Trusted = false;
775
776 // Select a source
777 if (QueueNext() == false && _error->PendingError() == false)
778 _error->Error(_("I wasn't able to locate file for the %s package. "
779 "This might mean you need to manually fix this package."),
780 Version.ParentPkg().Name());
781 }
782 /*}}}*/
783 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
784 // ---------------------------------------------------------------------
785 /* This queues the next available file version for download. It checks if
786 the archive is already available in the cache and stashs the MD5 for
787 checking later. */
788 bool pkgAcqArchive::QueueNext()
789 {
790 for (; Vf.end() == false; Vf++)
791 {
792 // Ignore not source sources
793 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
794 continue;
795
796 // Try to cross match against the source list
797 pkgIndexFile *Index;
798 if (Sources->FindIndex(Vf.File(),Index) == false)
799 continue;
800
801 // only try to get a trusted package from another source if that source
802 // is also trusted
803 if(Trusted && !Index->IsTrusted())
804 continue;
805
806 // Grab the text package record
807 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
808 if (_error->PendingError() == true)
809 return false;
810
811 string PkgFile = Parse.FileName();
812 MD5 = Parse.MD5Hash();
813 if (PkgFile.empty() == true)
814 return _error->Error(_("The package index files are corrupted. No Filename: "
815 "field for package %s."),
816 Version.ParentPkg().Name());
817
818 Desc.URI = Index->ArchiveURI(PkgFile);
819 Desc.Description = Index->ArchiveInfo(Version);
820 Desc.Owner = this;
821 Desc.ShortDesc = Version.ParentPkg().Name();
822
823 // See if we already have the file. (Legacy filenames)
824 FileSize = Version->Size;
825 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
826 struct stat Buf;
827 if (stat(FinalFile.c_str(),&Buf) == 0)
828 {
829 // Make sure the size matches
830 if ((unsigned)Buf.st_size == Version->Size)
831 {
832 Complete = true;
833 Local = true;
834 Status = StatDone;
835 StoreFilename = DestFile = FinalFile;
836 return true;
837 }
838
839 /* Hmm, we have a file and its size does not match, this means it is
840 an old style mismatched arch */
841 unlink(FinalFile.c_str());
842 }
843
844 // Check it again using the new style output filenames
845 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
846 if (stat(FinalFile.c_str(),&Buf) == 0)
847 {
848 // Make sure the size matches
849 if ((unsigned)Buf.st_size == Version->Size)
850 {
851 Complete = true;
852 Local = true;
853 Status = StatDone;
854 StoreFilename = DestFile = FinalFile;
855 return true;
856 }
857
858 /* Hmm, we have a file and its size does not match, this shouldnt
859 happen.. */
860 unlink(FinalFile.c_str());
861 }
862
863 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
864
865 // Check the destination file
866 if (stat(DestFile.c_str(),&Buf) == 0)
867 {
868 // Hmm, the partial file is too big, erase it
869 if ((unsigned)Buf.st_size > Version->Size)
870 unlink(DestFile.c_str());
871 else
872 PartialSize = Buf.st_size;
873 }
874
875 // Create the item
876 Local = false;
877 Desc.URI = Index->ArchiveURI(PkgFile);
878 Desc.Description = Index->ArchiveInfo(Version);
879 Desc.Owner = this;
880 Desc.ShortDesc = Version.ParentPkg().Name();
881 QueueURI(Desc);
882
883 Vf++;
884 return true;
885 }
886 return false;
887 }
888 /*}}}*/
889 // AcqArchive::Done - Finished fetching /*{{{*/
890 // ---------------------------------------------------------------------
891 /* */
892 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
893 pkgAcquire::MethodConfig *Cfg)
894 {
895 Item::Done(Message,Size,Md5Hash,Cfg);
896
897 // Check the size
898 if (Size != Version->Size)
899 {
900 Status = StatError;
901 ErrorText = _("Size mismatch");
902 return;
903 }
904
905 // Check the md5
906 if (Md5Hash.empty() == false && MD5.empty() == false)
907 {
908 if (Md5Hash != MD5)
909 {
910 Status = StatError;
911 ErrorText = _("MD5Sum mismatch");
912 if(FileExists(DestFile))
913 Rename(DestFile,DestFile + ".FAILED");
914 return;
915 }
916 }
917
918 // Grab the output filename
919 string FileName = LookupTag(Message,"Filename");
920 if (FileName.empty() == true)
921 {
922 Status = StatError;
923 ErrorText = "Method gave a blank filename";
924 return;
925 }
926
927 Complete = true;
928
929 // Reference filename
930 if (FileName != DestFile)
931 {
932 StoreFilename = DestFile = FileName;
933 Local = true;
934 return;
935 }
936
937 // Done, move it into position
938 string FinalFile = _config->FindDir("Dir::Cache::Archives");
939 FinalFile += flNotDir(StoreFilename);
940 Rename(DestFile,FinalFile);
941
942 StoreFilename = DestFile = FinalFile;
943 Complete = true;
944 }
945 /*}}}*/
946 // AcqArchive::Failed - Failure handler /*{{{*/
947 // ---------------------------------------------------------------------
948 /* Here we try other sources */
949 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
950 {
951 ErrorText = LookupTag(Message,"Message");
952
953 /* We don't really want to retry on failed media swaps, this prevents
954 that. An interesting observation is that permanent failures are not
955 recorded. */
956 if (Cnf->Removable == true &&
957 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
958 {
959 // Vf = Version.FileList();
960 while (Vf.end() == false) Vf++;
961 StoreFilename = string();
962 Item::Failed(Message,Cnf);
963 return;
964 }
965
966 if (QueueNext() == false)
967 {
968 // This is the retry counter
969 if (Retries != 0 &&
970 Cnf->LocalOnly == false &&
971 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
972 {
973 Retries--;
974 Vf = Version.FileList();
975 if (QueueNext() == true)
976 return;
977 }
978
979 StoreFilename = string();
980 Item::Failed(Message,Cnf);
981 }
982 }
983 /*}}}*/
984 // AcqArchive::IsTrusted - Determine whether this archive comes from a
985 // trusted source /*{{{*/
986 // ---------------------------------------------------------------------
987 bool pkgAcqArchive::IsTrusted()
988 {
989 return Trusted;
990 }
991
992 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
993 // ---------------------------------------------------------------------
994 /* */
995 void pkgAcqArchive::Finished()
996 {
997 if (Status == pkgAcquire::Item::StatDone &&
998 Complete == true)
999 return;
1000 StoreFilename = string();
1001 }
1002 /*}}}*/
1003
1004 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1005 // ---------------------------------------------------------------------
1006 /* The file is added to the queue */
1007 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
1008 unsigned long Size,string Dsc,string ShortDesc,
1009 const string &DestDir, const string &DestFilename) :
1010 Item(Owner), Md5Hash(MD5)
1011 {
1012 Retries = _config->FindI("Acquire::Retries",0);
1013
1014 if(!DestFilename.empty())
1015 DestFile = DestFilename;
1016 else if(!DestDir.empty())
1017 DestFile = DestDir + "/" + flNotDir(URI);
1018 else
1019 DestFile = flNotDir(URI);
1020
1021 // Create the item
1022 Desc.URI = URI;
1023 Desc.Description = Dsc;
1024 Desc.Owner = this;
1025
1026 // Set the short description to the archive component
1027 Desc.ShortDesc = ShortDesc;
1028
1029 // Get the transfer sizes
1030 FileSize = Size;
1031 struct stat Buf;
1032 if (stat(DestFile.c_str(),&Buf) == 0)
1033 {
1034 // Hmm, the partial file is too big, erase it
1035 if ((unsigned)Buf.st_size > Size)
1036 unlink(DestFile.c_str());
1037 else
1038 PartialSize = Buf.st_size;
1039 }
1040
1041 QueueURI(Desc);
1042 }
1043 /*}}}*/
1044 // AcqFile::Done - Item downloaded OK /*{{{*/
1045 // ---------------------------------------------------------------------
1046 /* */
1047 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
1048 pkgAcquire::MethodConfig *Cnf)
1049 {
1050 // Check the md5
1051 if (Md5Hash.empty() == false && MD5.empty() == false)
1052 {
1053 if (Md5Hash != MD5)
1054 {
1055 Status = StatError;
1056 ErrorText = "MD5Sum mismatch";
1057 Rename(DestFile,DestFile + ".FAILED");
1058 return;
1059 }
1060 }
1061
1062 Item::Done(Message,Size,MD5,Cnf);
1063
1064 string FileName = LookupTag(Message,"Filename");
1065 if (FileName.empty() == true)
1066 {
1067 Status = StatError;
1068 ErrorText = "Method gave a blank filename";
1069 return;
1070 }
1071
1072 Complete = true;
1073
1074 // The files timestamp matches
1075 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1076 return;
1077
1078 // We have to copy it into place
1079 if (FileName != DestFile)
1080 {
1081 Local = true;
1082 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1083 Cnf->Removable == true)
1084 {
1085 Desc.URI = "copy:" + FileName;
1086 QueueURI(Desc);
1087 return;
1088 }
1089
1090 // Erase the file if it is a symlink so we can overwrite it
1091 struct stat St;
1092 if (lstat(DestFile.c_str(),&St) == 0)
1093 {
1094 if (S_ISLNK(St.st_mode) != 0)
1095 unlink(DestFile.c_str());
1096 }
1097
1098 // Symlink the file
1099 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1100 {
1101 ErrorText = "Link to " + DestFile + " failure ";
1102 Status = StatError;
1103 Complete = false;
1104 }
1105 }
1106 }
1107 /*}}}*/
1108 // AcqFile::Failed - Failure handler /*{{{*/
1109 // ---------------------------------------------------------------------
1110 /* Here we try other sources */
1111 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1112 {
1113 ErrorText = LookupTag(Message,"Message");
1114
1115 // This is the retry counter
1116 if (Retries != 0 &&
1117 Cnf->LocalOnly == false &&
1118 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1119 {
1120 Retries--;
1121 QueueURI(Desc);
1122 return;
1123 }
1124
1125 Item::Failed(Message,Cnf);
1126 }
1127 /*}}}*/