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