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