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