Merge with matt
[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 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 }
344
345 QueueURI(Desc);
346 }
347 /*}}}*/
348 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
349 // ---------------------------------------------------------------------
350 /* The only header we use is the last-modified header. */
351 string pkgAcqMetaSig::Custom600Headers()
352 {
353 struct stat Buf;
354 if (stat(DestFile.c_str(),&Buf) != 0)
355 return "\nIndex-File: true";
356
357 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
358 }
359
360 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
361 pkgAcquire::MethodConfig *Cfg)
362 {
363 Item::Done(Message,Size,MD5,Cfg);
364
365 string FileName = LookupTag(Message,"Filename");
366 if (FileName.empty() == true)
367 {
368 Status = StatError;
369 ErrorText = "Method gave a blank filename";
370 return;
371 }
372
373 if (FileName != DestFile)
374 {
375 // We have to copy it into place
376 Local = true;
377 Desc.URI = "copy:" + FileName;
378 QueueURI(Desc);
379 return;
380 }
381
382 Complete = true;
383
384 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
385 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
386 DestFile, IndexTargets, MetaIndexParser);
387
388 }
389 /*}}}*/
390 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
391 {
392 // Delete any existing sigfile, so that this source isn't
393 // mistakenly trusted
394 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
395 unlink(Final.c_str());
396
397 // queue a pkgAcqMetaIndex with no sigfile
398 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
399 "", IndexTargets, MetaIndexParser);
400
401 if (Cnf->LocalOnly == true ||
402 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
403 {
404 // Ignore this
405 Status = StatDone;
406 Complete = false;
407 Dequeue();
408 return;
409 }
410
411 Item::Failed(Message,Cnf);
412 }
413
414 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,
415 string URI,string URIDesc,string ShortDesc,
416 string SigFile,
417 const vector<struct IndexTarget*>* IndexTargets,
418 indexRecords* MetaIndexParser) :
419 Item(Owner), RealURI(URI), SigFile(SigFile)
420 {
421 this->AuthPass = false;
422 this->MetaIndexParser = MetaIndexParser;
423 this->IndexTargets = IndexTargets;
424 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
425 DestFile += URItoFileName(URI);
426
427 // Create the item
428 Desc.Description = URIDesc;
429 Desc.Owner = this;
430 Desc.ShortDesc = ShortDesc;
431 Desc.URI = URI;
432
433 QueueURI(Desc);
434 }
435
436 /*}}}*/
437 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
438 // ---------------------------------------------------------------------
439 /* The only header we use is the last-modified header. */
440 string pkgAcqMetaIndex::Custom600Headers()
441 {
442 string Final = _config->FindDir("Dir::State::lists");
443 Final += URItoFileName(RealURI);
444
445 struct stat Buf;
446 if (stat(Final.c_str(),&Buf) != 0)
447 return "\nIndex-File: true";
448
449 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
450 }
451
452 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string MD5,
453 pkgAcquire::MethodConfig *Cfg)
454 {
455 Item::Done(Message,Size,MD5,Cfg);
456
457 // MetaIndexes are done in two passes: one to download the
458 // metaindex with an appropriate method, and a second to verify it
459 // with the gpgv method
460
461 if (AuthPass == true)
462 {
463 AuthDone(Message);
464 }
465 else
466 {
467 RetrievalDone(Message);
468 if (!Complete)
469 // Still more retrieving to do
470 return;
471
472 if (SigFile == "")
473 {
474 // There was no signature file, so we are finished. Download
475 // the indexes without verification.
476 QueueIndexes(false);
477 }
478 else
479 {
480 // There was a signature file, so pass it to gpgv for
481 // verification
482
483 if (_config->FindB("Debug::pkgAcquire::Auth", false))
484 std::cerr << "Metaindex acquired, queueing gpg verification ("
485 << SigFile << "," << DestFile << ")\n";
486 AuthPass = true;
487 Desc.URI = "gpgv:" + SigFile;
488 QueueURI(Desc);
489 Mode = "gpgv";
490 }
491 }
492 }
493
494 void pkgAcqMetaIndex::RetrievalDone(string Message)
495 {
496 // We have just finished downloading a Release file (it is not
497 // verified yet)
498
499 string FileName = LookupTag(Message,"Filename");
500 if (FileName.empty() == true)
501 {
502 Status = StatError;
503 ErrorText = "Method gave a blank filename";
504 return;
505 }
506
507 if (FileName != DestFile)
508 {
509 Local = true;
510 Desc.URI = "copy:" + FileName;
511 QueueURI(Desc);
512 return;
513 }
514
515 Complete = true;
516
517 string FinalFile = _config->FindDir("Dir::State::lists");
518 FinalFile += URItoFileName(RealURI);
519
520 // The files timestamp matches
521 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == false)
522 {
523 // Move it into position
524 Rename(DestFile,FinalFile);
525 }
526 DestFile = FinalFile;
527 }
528
529 void pkgAcqMetaIndex::AuthDone(string Message)
530 {
531 // At this point, the gpgv method has succeeded, so there is a
532 // valid signature from a key in the trusted keyring. We
533 // perform additional verification of its contents, and use them
534 // to verify the indexes we are about to download
535
536 if (!MetaIndexParser->Load(DestFile))
537 {
538 Status = StatAuthError;
539 ErrorText = MetaIndexParser->ErrorText;
540 return;
541 }
542
543 if (!VerifyVendor())
544 {
545 return;
546 }
547
548 if (_config->FindB("Debug::pkgAcquire::Auth", false))
549 std::cerr << "Signature verification succeeded: "
550 << DestFile << std::endl;
551
552 // Download further indexes with verification
553 QueueIndexes(true);
554
555 // Done, move signature file into position
556
557 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
558 URItoFileName(RealURI) + ".gpg";
559 Rename(SigFile,VerifiedSigFile);
560 chmod(VerifiedSigFile.c_str(),0644);
561 }
562
563 void pkgAcqMetaIndex::QueueIndexes(bool verify)
564 {
565 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
566 Target != IndexTargets->end();
567 Target++)
568 {
569 string ExpectedIndexMD5;
570 if (verify)
571 {
572 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
573 if (!Record)
574 {
575 Status = StatAuthError;
576 ErrorText = "Unable to find expected entry "
577 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
578 return;
579 }
580 ExpectedIndexMD5 = Record->MD5Hash;
581 if (_config->FindB("Debug::pkgAcquire::Auth", false))
582 {
583 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
584 std::cerr << "Expected MD5: " << ExpectedIndexMD5 << std::endl;
585 }
586 if (ExpectedIndexMD5.empty())
587 {
588 Status = StatAuthError;
589 ErrorText = "Unable to find MD5 sum for "
590 + (*Target)->MetaKey + " in Meta-index file";
591 return;
592 }
593 }
594
595 // Queue Packages file
596 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
597 (*Target)->ShortDesc, ExpectedIndexMD5);
598 }
599 }
600
601 bool pkgAcqMetaIndex::VerifyVendor()
602 {
603 // // Maybe this should be made available from above so we don't have
604 // // to read and parse it every time?
605 // pkgVendorList List;
606 // List.ReadMainList();
607
608 // const Vendor* Vndr = NULL;
609 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
610 // {
611 // string::size_type pos = (*I).find("VALIDSIG ");
612 // if (_config->FindB("Debug::Vendor", false))
613 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
614 // << std::endl;
615 // if (pos != std::string::npos)
616 // {
617 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
618 // if (_config->FindB("Debug::Vendor", false))
619 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
620 // std::endl;
621 // Vndr = List.FindVendor(Fingerprint) != "";
622 // if (Vndr != NULL);
623 // break;
624 // }
625 // }
626
627 string Transformed = MetaIndexParser->GetExpectedDist();
628
629 if (Transformed == "../project/experimental")
630 {
631 Transformed = "experimental";
632 }
633
634 string::size_type pos = Transformed.rfind('/');
635 if (pos != string::npos)
636 {
637 Transformed = Transformed.substr(0, pos);
638 }
639
640 if (Transformed == ".")
641 {
642 Transformed = "";
643 }
644
645 if (_config->FindB("Debug::pkgAcquire::Auth", false))
646 {
647 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
648 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
649 std::cerr << "Transformed Dist: " << Transformed << std::endl;
650 }
651
652 if (MetaIndexParser->CheckDist(Transformed) == false)
653 {
654 // This might become fatal one day
655 // Status = StatAuthError;
656 // ErrorText = "Conflicting distribution; expected "
657 // + MetaIndexParser->GetExpectedDist() + " but got "
658 // + MetaIndexParser->GetDist();
659 // return false;
660 if (!Transformed.empty())
661 {
662 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
663 Desc.Description.c_str(),
664 Transformed.c_str(),
665 MetaIndexParser->GetDist().c_str());
666 }
667 }
668
669 return true;
670 }
671 /*}}}*/
672 // pkgAcqMetaIndex::Failed - no Release file present or no signature
673 // file present /*{{{*/
674 // ---------------------------------------------------------------------
675 /* */
676 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
677 {
678 if (AuthPass == true)
679 {
680 // gpgv method failed
681 _error->Warning("GPG error: %s: %s",
682 Desc.Description.c_str(),
683 LookupTag(Message,"Message").c_str());
684 }
685
686 // No Release file was present, or verification failed, so fall
687 // back to queueing Packages files without verification
688 QueueIndexes(false);
689 }
690
691 /*}}}*/
692
693 // AcqArchive::AcqArchive - Constructor /*{{{*/
694 // ---------------------------------------------------------------------
695 /* This just sets up the initial fetch environment and queues the first
696 possibilitiy */
697 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
698 pkgRecords *Recs,pkgCache::VerIterator const &Version,
699 string &StoreFilename) :
700 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
701 StoreFilename(StoreFilename), Vf(Version.FileList()),
702 Trusted(false)
703 {
704 Retries = _config->FindI("Acquire::Retries",0);
705
706 if (Version.Arch() == 0)
707 {
708 _error->Error(_("I wasn't able to locate a file for the %s package. "
709 "This might mean you need to manually fix this package. "
710 "(due to missing arch)"),
711 Version.ParentPkg().Name());
712 return;
713 }
714
715 /* We need to find a filename to determine the extension. We make the
716 assumption here that all the available sources for this version share
717 the same extension.. */
718 // Skip not source sources, they do not have file fields.
719 for (; Vf.end() == false; Vf++)
720 {
721 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
722 continue;
723 break;
724 }
725
726 // Does not really matter here.. we are going to fail out below
727 if (Vf.end() != true)
728 {
729 // If this fails to get a file name we will bomb out below.
730 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
731 if (_error->PendingError() == true)
732 return;
733
734 // Generate the final file name as: package_version_arch.foo
735 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
736 QuoteString(Version.VerStr(),"_:") + '_' +
737 QuoteString(Version.Arch(),"_:.") +
738 "." + flExtension(Parse.FileName());
739 }
740
741 // check if we have one trusted source for the package. if so, switch
742 // to "TrustedOnly" mode
743 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
744 {
745 pkgIndexFile *Index;
746 if (Sources->FindIndex(i.File(),Index) == false)
747 continue;
748 if (_config->FindB("Debug::pkgAcquire::Auth", false))
749 {
750 std::cerr << "Checking index: " << Index->Describe()
751 << "(Trusted=" << Index->IsTrusted() << ")\n";
752 }
753 if (Index->IsTrusted()) {
754 Trusted = true;
755 break;
756 }
757 }
758
759 // Select a source
760 if (QueueNext() == false && _error->PendingError() == false)
761 _error->Error(_("I wasn't able to locate file for the %s package. "
762 "This might mean you need to manually fix this package."),
763 Version.ParentPkg().Name());
764 }
765 /*}}}*/
766 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
767 // ---------------------------------------------------------------------
768 /* This queues the next available file version for download. It checks if
769 the archive is already available in the cache and stashs the MD5 for
770 checking later. */
771 bool pkgAcqArchive::QueueNext()
772 {
773 for (; Vf.end() == false; Vf++)
774 {
775 // Ignore not source sources
776 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
777 continue;
778
779 // Try to cross match against the source list
780 pkgIndexFile *Index;
781 if (Sources->FindIndex(Vf.File(),Index) == false)
782 continue;
783
784 // only try to get a trusted package from another source if that source
785 // is also trusted
786 if(Trusted && !Index->IsTrusted())
787 continue;
788
789 // Grab the text package record
790 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
791 if (_error->PendingError() == true)
792 return false;
793
794 string PkgFile = Parse.FileName();
795 MD5 = Parse.MD5Hash();
796 if (PkgFile.empty() == true)
797 return _error->Error(_("The package index files are corrupted. No Filename: "
798 "field for package %s."),
799 Version.ParentPkg().Name());
800
801 Desc.URI = Index->ArchiveURI(PkgFile);
802 Desc.Description = Index->ArchiveInfo(Version);
803 Desc.Owner = this;
804 Desc.ShortDesc = Version.ParentPkg().Name();
805
806 // See if we already have the file. (Legacy filenames)
807 FileSize = Version->Size;
808 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
809 struct stat Buf;
810 if (stat(FinalFile.c_str(),&Buf) == 0)
811 {
812 // Make sure the size matches
813 if ((unsigned)Buf.st_size == Version->Size)
814 {
815 Complete = true;
816 Local = true;
817 Status = StatDone;
818 StoreFilename = DestFile = FinalFile;
819 return true;
820 }
821
822 /* Hmm, we have a file and its size does not match, this means it is
823 an old style mismatched arch */
824 unlink(FinalFile.c_str());
825 }
826
827 // Check it again using the new style output filenames
828 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
829 if (stat(FinalFile.c_str(),&Buf) == 0)
830 {
831 // Make sure the size matches
832 if ((unsigned)Buf.st_size == Version->Size)
833 {
834 Complete = true;
835 Local = true;
836 Status = StatDone;
837 StoreFilename = DestFile = FinalFile;
838 return true;
839 }
840
841 /* Hmm, we have a file and its size does not match, this shouldnt
842 happen.. */
843 unlink(FinalFile.c_str());
844 }
845
846 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
847
848 // Check the destination file
849 if (stat(DestFile.c_str(),&Buf) == 0)
850 {
851 // Hmm, the partial file is too big, erase it
852 if ((unsigned)Buf.st_size > Version->Size)
853 unlink(DestFile.c_str());
854 else
855 PartialSize = Buf.st_size;
856 }
857
858 // Create the item
859 Local = false;
860 Desc.URI = Index->ArchiveURI(PkgFile);
861 Desc.Description = Index->ArchiveInfo(Version);
862 Desc.Owner = this;
863 Desc.ShortDesc = Version.ParentPkg().Name();
864 QueueURI(Desc);
865
866 Vf++;
867 return true;
868 }
869 return false;
870 }
871 /*}}}*/
872 // AcqArchive::Done - Finished fetching /*{{{*/
873 // ---------------------------------------------------------------------
874 /* */
875 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
876 pkgAcquire::MethodConfig *Cfg)
877 {
878 Item::Done(Message,Size,Md5Hash,Cfg);
879
880 // Check the size
881 if (Size != Version->Size)
882 {
883 Status = StatError;
884 ErrorText = _("Size mismatch");
885 return;
886 }
887
888 // Check the md5
889 if (Md5Hash.empty() == false && MD5.empty() == false)
890 {
891 if (Md5Hash != MD5)
892 {
893 Status = StatError;
894 ErrorText = _("MD5Sum mismatch");
895 Rename(DestFile,DestFile + ".FAILED");
896 return;
897 }
898 }
899
900 // Grab the output filename
901 string FileName = LookupTag(Message,"Filename");
902 if (FileName.empty() == true)
903 {
904 Status = StatError;
905 ErrorText = "Method gave a blank filename";
906 return;
907 }
908
909 Complete = true;
910
911 // Reference filename
912 if (FileName != DestFile)
913 {
914 StoreFilename = DestFile = FileName;
915 Local = true;
916 return;
917 }
918
919 // Done, move it into position
920 string FinalFile = _config->FindDir("Dir::Cache::Archives");
921 FinalFile += flNotDir(StoreFilename);
922 Rename(DestFile,FinalFile);
923
924 StoreFilename = DestFile = FinalFile;
925 Complete = true;
926 }
927 /*}}}*/
928 // AcqArchive::Failed - Failure handler /*{{{*/
929 // ---------------------------------------------------------------------
930 /* Here we try other sources */
931 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
932 {
933 ErrorText = LookupTag(Message,"Message");
934
935 /* We don't really want to retry on failed media swaps, this prevents
936 that. An interesting observation is that permanent failures are not
937 recorded. */
938 if (Cnf->Removable == true &&
939 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
940 {
941 // Vf = Version.FileList();
942 while (Vf.end() == false) Vf++;
943 StoreFilename = string();
944 Item::Failed(Message,Cnf);
945 return;
946 }
947
948 if (QueueNext() == false)
949 {
950 // This is the retry counter
951 if (Retries != 0 &&
952 Cnf->LocalOnly == false &&
953 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
954 {
955 Retries--;
956 Vf = Version.FileList();
957 if (QueueNext() == true)
958 return;
959 }
960
961 StoreFilename = string();
962 Item::Failed(Message,Cnf);
963 }
964 }
965 /*}}}*/
966 // AcqArchive::IsTrusted - Determine whether this archive comes from a
967 // trusted source /*{{{*/
968 // ---------------------------------------------------------------------
969 bool pkgAcqArchive::IsTrusted()
970 {
971 return Trusted;
972 }
973
974 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
975 // ---------------------------------------------------------------------
976 /* */
977 void pkgAcqArchive::Finished()
978 {
979 if (Status == pkgAcquire::Item::StatDone &&
980 Complete == true)
981 return;
982 StoreFilename = string();
983 }
984 /*}}}*/
985
986 // AcqFile::pkgAcqFile - Constructor /*{{{*/
987 // ---------------------------------------------------------------------
988 /* The file is added to the queue */
989 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
990 unsigned long Size,string Dsc,string ShortDesc) :
991 Item(Owner), Md5Hash(MD5)
992 {
993 Retries = _config->FindI("Acquire::Retries",0);
994
995 DestFile = flNotDir(URI);
996
997 // Create the item
998 Desc.URI = URI;
999 Desc.Description = Dsc;
1000 Desc.Owner = this;
1001
1002 // Set the short description to the archive component
1003 Desc.ShortDesc = ShortDesc;
1004
1005 // Get the transfer sizes
1006 FileSize = Size;
1007 struct stat Buf;
1008 if (stat(DestFile.c_str(),&Buf) == 0)
1009 {
1010 // Hmm, the partial file is too big, erase it
1011 if ((unsigned)Buf.st_size > Size)
1012 unlink(DestFile.c_str());
1013 else
1014 PartialSize = Buf.st_size;
1015 }
1016
1017 QueueURI(Desc);
1018 }
1019 /*}}}*/
1020 // AcqFile::Done - Item downloaded OK /*{{{*/
1021 // ---------------------------------------------------------------------
1022 /* */
1023 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
1024 pkgAcquire::MethodConfig *Cnf)
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 Rename(DestFile,DestFile + ".FAILED");
1034 return;
1035 }
1036 }
1037
1038 Item::Done(Message,Size,MD5,Cnf);
1039
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 // The files timestamp matches
1051 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1052 return;
1053
1054 // We have to copy it into place
1055 if (FileName != DestFile)
1056 {
1057 Local = true;
1058 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1059 Cnf->Removable == true)
1060 {
1061 Desc.URI = "copy:" + FileName;
1062 QueueURI(Desc);
1063 return;
1064 }
1065
1066 // Erase the file if it is a symlink so we can overwrite it
1067 struct stat St;
1068 if (lstat(DestFile.c_str(),&St) == 0)
1069 {
1070 if (S_ISLNK(St.st_mode) != 0)
1071 unlink(DestFile.c_str());
1072 }
1073
1074 // Symlink the file
1075 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1076 {
1077 ErrorText = "Link to " + DestFile + " failure ";
1078 Status = StatError;
1079 Complete = false;
1080 }
1081 }
1082 }
1083 /*}}}*/
1084 // AcqFile::Failed - Failure handler /*{{{*/
1085 // ---------------------------------------------------------------------
1086 /* Here we try other sources */
1087 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1088 {
1089 ErrorText = LookupTag(Message,"Message");
1090
1091 // This is the retry counter
1092 if (Retries != 0 &&
1093 Cnf->LocalOnly == false &&
1094 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1095 {
1096 Retries--;
1097 QueueURI(Desc);
1098 return;
1099 }
1100
1101 Item::Failed(Message,Cnf);
1102 }
1103 /*}}}*/