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