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