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