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