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