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