]> git.saurik.com Git - apt.git/blame - apt-pkg/acquire-item.cc
* add --dsc-only option, thanks to K. Richard Pixley
[apt.git] / apt-pkg / acquire-item.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
b3d44315 3// $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
0118833a
AL
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.
b185acc2 12
0118833a
AL
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>
b2e465d6 21#include <apt-pkg/sourcelist.h>
b3d44315 22#include <apt-pkg/vendorlist.h>
03e39e59 23#include <apt-pkg/error.h>
cdcc6d34 24#include <apt-pkg/strutl.h>
36375005 25#include <apt-pkg/fileutl.h>
b3d44315 26#include <apt-pkg/md5.h>
0a8a80e5 27
b2e465d6
AL
28#include <apti18n.h>
29
0a8a80e5
AL
30#include <sys/stat.h>
31#include <unistd.h>
c88edf1d 32#include <errno.h>
5819a761 33#include <string>
c88edf1d 34#include <stdio.h>
0118833a
AL
35 /*}}}*/
36
b3d44315 37using namespace std;
5819a761 38
0118833a
AL
39// Acquire::Item::Item - Constructor /*{{{*/
40// ---------------------------------------------------------------------
41/* */
8267fe24 42pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
6b1ff003
AL
43 PartialSize(0), Mode(0), ID(0), Complete(false),
44 Local(false), QueueCounter(0)
0118833a
AL
45{
46 Owner->Add(this);
c88edf1d 47 Status = StatIdle;
0118833a
AL
48}
49 /*}}}*/
50// Acquire::Item::~Item - Destructor /*{{{*/
51// ---------------------------------------------------------------------
52/* */
53pkgAcquire::Item::~Item()
54{
55 Owner->Remove(this);
56}
57 /*}}}*/
c88edf1d
AL
58// Acquire::Item::Failed - Item failed to download /*{{{*/
59// ---------------------------------------------------------------------
93bf083d
AL
60/* We return to an idle state if there are still other queues that could
61 fetch this object */
7d8afa39 62void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
c88edf1d 63{
93bf083d 64 Status = StatIdle;
db890fdb 65 ErrorText = LookupTag(Message,"Message");
c88edf1d 66 if (QueueCounter <= 1)
93bf083d 67 {
a72ace20 68 /* This indicates that the file is not available right now but might
7d8afa39 69 be sometime later. If we do a retry cycle then this should be
17caf1b1 70 retried [CDROMs] */
7d8afa39
AL
71 if (Cnf->LocalOnly == true &&
72 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
a72ace20
AL
73 {
74 Status = StatIdle;
681d76d0 75 Dequeue();
a72ace20
AL
76 return;
77 }
78
93bf083d 79 Status = StatError;
681d76d0 80 Dequeue();
93bf083d 81 }
c88edf1d
AL
82}
83 /*}}}*/
8267fe24
AL
84// Acquire::Item::Start - Item has begun to download /*{{{*/
85// ---------------------------------------------------------------------
17caf1b1
AL
86/* Stash status and the file size. Note that setting Complete means
87 sub-phases of the acquire process such as decompresion are operating */
727f18af 88void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
8267fe24
AL
89{
90 Status = StatFetching;
91 if (FileSize == 0 && Complete == false)
92 FileSize = Size;
93}
94 /*}}}*/
c88edf1d
AL
95// Acquire::Item::Done - Item downloaded OK /*{{{*/
96// ---------------------------------------------------------------------
97/* */
459681d3
AL
98void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
99 pkgAcquire::MethodConfig *Cnf)
c88edf1d 100{
b98f2859
AL
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 }
aa0e1101
AL
108
109 if (FileSize == 0)
110 FileSize= Size;
b98f2859 111
c88edf1d
AL
112 Status = StatDone;
113 ErrorText = string();
114 Owner->Dequeue(this);
115}
116 /*}}}*/
8b89e57f
AL
117// Acquire::Item::Rename - Rename a file /*{{{*/
118// ---------------------------------------------------------------------
119/* This helper function is used by alot of item methods as thier final
120 step */
121void pkgAcquire::Item::Rename(string From,string To)
122{
123 if (rename(From.c_str(),To.c_str()) != 0)
124 {
125 char S[300];
0fcd01de 126 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
8b89e57f
AL
127 From.c_str(),To.c_str());
128 Status = StatError;
129 ErrorText = S;
7a3c2ab0 130 }
8b89e57f
AL
131}
132 /*}}}*/
0118833a
AL
133
134// AcqIndex::AcqIndex - Constructor /*{{{*/
135// ---------------------------------------------------------------------
136/* The package file is added to the queue and a second class is
b2e465d6
AL
137 instantiated to fetch the revision file */
138pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
b3d44315
MV
139 string URI,string URIDesc,string ShortDesc,
140 string ExpectedMD5, string comprExt) :
141 Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
0118833a 142{
8b89e57f 143 Decompression = false;
bfd22fc0 144 Erase = false;
13e8426f 145
0a8a80e5 146 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 147 DestFile += URItoFileName(URI);
8267fe24 148
b3d44315
MV
149 if(comprExt.empty())
150 {
13e8426f 151 // autoselect the compression method
4577fda2 152 if(FileExists("/bin/bzip2"))
13e8426f
MV
153 CompressionExtension = ".bz2";
154 else
155 CompressionExtension = ".gz";
b3d44315 156 } else {
13e8426f 157 CompressionExtension = comprExt;
b3d44315 158 }
13e8426f 159 Desc.URI = URI + CompressionExtension;
b3d44315 160
b2e465d6 161 Desc.Description = URIDesc;
8267fe24 162 Desc.Owner = this;
b2e465d6 163 Desc.ShortDesc = ShortDesc;
8267fe24
AL
164
165 QueueURI(Desc);
0118833a
AL
166}
167 /*}}}*/
0a8a80e5 168// AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 169// ---------------------------------------------------------------------
0a8a80e5
AL
170/* The only header we use is the last-modified header. */
171string pkgAcqIndex::Custom600Headers()
0118833a 172{
0a8a80e5 173 string Final = _config->FindDir("Dir::State::lists");
b2e465d6 174 Final += URItoFileName(RealURI);
0a8a80e5
AL
175
176 struct stat Buf;
177 if (stat(Final.c_str(),&Buf) != 0)
a72ace20 178 return "\nIndex-File: true";
0118833a 179
a72ace20 180 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a
AL
181}
182 /*}}}*/
debc84b2
MZ
183
184void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
185{
17ff0930 186
debc84b2 187 // no .bz2 found, retry with .gz
46e00f9d 188 if(Desc.URI.substr(Desc.URI.size()-3) == "bz2") {
debc84b2 189 Desc.URI = Desc.URI.substr(0,Desc.URI.size()-3) + "gz";
b3d44315
MV
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();
debc84b2 197 return;
17ff0930
MV
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());
debc84b2
MZ
205 }
206
debc84b2
MZ
207 Item::Failed(Message,Cnf);
208}
209
210
8b89e57f
AL
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. */
459681d3
AL
218void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
219 pkgAcquire::MethodConfig *Cfg)
8b89e57f 220{
459681d3 221 Item::Done(Message,Size,MD5,Cfg);
8b89e57f
AL
222
223 if (Decompression == true)
224 {
b3d44315
MV
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 }
8b89e57f
AL
247 // Done, move it into position
248 string FinalFile = _config->FindDir("Dir::State::lists");
b2e465d6 249 FinalFile += URItoFileName(RealURI);
8b89e57f 250 Rename(DestFile,FinalFile);
7a3c2ab0 251 chmod(FinalFile.c_str(),0644);
bfd22fc0 252
7a7fa5f0
AL
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/";
b2e465d6 256 DestFile += URItoFileName(RealURI);
7a7fa5f0 257
bfd22fc0
AL
258 // Remove the compressed version.
259 if (Erase == true)
bfd22fc0 260 unlink(DestFile.c_str());
8b89e57f
AL
261 return;
262 }
bfd22fc0
AL
263
264 Erase = false;
8267fe24 265 Complete = true;
bfd22fc0 266
8b89e57f
AL
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;
b3d44315 274
8b89e57f 275 Decompression = true;
a6568219 276 Local = true;
8b89e57f 277 DestFile += ".decomp";
8267fe24
AL
278 Desc.URI = "copy:" + FileName;
279 QueueURI(Desc);
b98f2859 280 Mode = "copy";
8b89e57f
AL
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;
bfd22fc0
AL
294
295 if (FileName == DestFile)
296 Erase = true;
8267fe24 297 else
a6568219 298 Local = true;
8b89e57f 299
46e00f9d 300 string compExt = Desc.URI.substr(Desc.URI.size()-3);
debc84b2
MZ
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
8b89e57f
AL
311 Decompression = true;
312 DestFile += ".decomp";
debc84b2 313 Desc.URI = string(decompProg) + ":" + FileName;
8267fe24 314 QueueURI(Desc);
debc84b2 315 Mode = decompProg;
8b89e57f 316}
8b89e57f 317
b3d44315
MV
318pkgAcqMetaSig::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),
46e00f9d
MV
325 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
326 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
0118833a 327{
0a8a80e5 328 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
b2e465d6 329 DestFile += URItoFileName(URI);
b3d44315 330
f6237efd
MV
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
8267fe24 335 // Create the item
b2e465d6 336 Desc.Description = URIDesc;
8267fe24 337 Desc.Owner = this;
b3d44315
MV
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 }
8267fe24 351
8267fe24 352 QueueURI(Desc);
0118833a
AL
353}
354 /*}}}*/
b3d44315 355// pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
0118833a 356// ---------------------------------------------------------------------
0a8a80e5 357/* The only header we use is the last-modified header. */
b3d44315 358string pkgAcqMetaSig::Custom600Headers()
0118833a 359{
0a8a80e5 360 struct stat Buf;
2aab5956 361 if (stat(DestFile.c_str(),&Buf) != 0)
a72ace20 362 return "\nIndex-File: true";
a789b983 363
a72ace20 364 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
0118833a 365}
b3d44315
MV
366
367void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
368 pkgAcquire::MethodConfig *Cfg)
c88edf1d 369{
459681d3 370 Item::Done(Message,Size,MD5,Cfg);
c88edf1d
AL
371
372 string FileName = LookupTag(Message,"Filename");
373 if (FileName.empty() == true)
374 {
375 Status = StatError;
376 ErrorText = "Method gave a blank filename";
8b89e57f 377 return;
c88edf1d 378 }
8b89e57f 379
c88edf1d
AL
380 if (FileName != DestFile)
381 {
b3d44315 382 // We have to copy it into place
a6568219 383 Local = true;
8267fe24
AL
384 Desc.URI = "copy:" + FileName;
385 QueueURI(Desc);
c88edf1d
AL
386 return;
387 }
b3d44315
MV
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
c88edf1d
AL
395}
396 /*}}}*/
b3d44315 397void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
681d76d0 398{
a789b983 399
75dd8af1 400 // if we get a network error we fail gracefully
25182152 401 if(LookupTag(Message,"FailReason") == "Timeout" ||
0c1b7be9 402 LookupTag(Message,"FailReason") == "TmpResolveFailure" ||
75dd8af1 403 LookupTag(Message,"FailReason") == "ConnectionRefused") {
24057ad6
MV
404 Item::Failed(Message,Cnf);
405 return;
406 }
407
75dd8af1
MV
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
b3d44315
MV
412 // queue a pkgAcqMetaIndex with no sigfile
413 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
414 "", IndexTargets, MetaIndexParser);
415
681d76d0
AL
416 if (Cnf->LocalOnly == true ||
417 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
418 {
2b154e53
AL
419 // Ignore this
420 Status = StatDone;
421 Complete = false;
681d76d0
AL
422 Dequeue();
423 return;
424 }
425
426 Item::Failed(Message,Cnf);
427}
b3d44315
MV
428
429pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,
430 string URI,string URIDesc,string ShortDesc,
431 string SigFile,
432 const vector<struct IndexTarget*>* IndexTargets,
433 indexRecords* MetaIndexParser) :
46e00f9d 434 Item(Owner), RealURI(URI), SigFile(SigFile), AuthPass(false),
f381d68d 435 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets), IMSHit(false)
b3d44315 436{
b3d44315
MV
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. */
453string 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
465void 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
507void 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
f381d68d
MV
528 // see if the download was a IMSHit
529 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
530
b3d44315
MV
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
545void 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
ce424cd4 559 if (!VerifyVendor(Message))
b3d44315
MV
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
579void 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
ce424cd4 617bool pkgAcqMetaIndex::VerifyVendor(string Message)
b3d44315
MV
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// }
ce424cd4
MV
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;
400ad7a4 647 string msg = _("There is no public key available for the "
ce424cd4
MV
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());
b3d44315
MV
658
659 string Transformed = MetaIndexParser->GetExpectedDist();
660
661 if (Transformed == "../project/experimental")
662 {
663 Transformed = "experimental";
664 }
665
ce424cd4 666 pos = Transformed.rfind('/');
b3d44315
MV
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/* */
708void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
709{
710 if (AuthPass == true)
711 {
f381d68d
MV
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
b3d44315
MV
732 _error->Warning("GPG error: %s: %s",
733 Desc.Description.c_str(),
734 LookupTag(Message,"Message").c_str());
f381d68d 735
b3d44315
MV
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
681d76d0 743 /*}}}*/
03e39e59
AL
744
745// AcqArchive::AcqArchive - Constructor /*{{{*/
746// ---------------------------------------------------------------------
17caf1b1
AL
747/* This just sets up the initial fetch environment and queues the first
748 possibilitiy */
03e39e59 749pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
30e1eab5
AL
750 pkgRecords *Recs,pkgCache::VerIterator const &Version,
751 string &StoreFilename) :
752 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
b3d44315
MV
753 StoreFilename(StoreFilename), Vf(Version.FileList()),
754 Trusted(false)
03e39e59 755{
7d8afa39 756 Retries = _config->FindI("Acquire::Retries",0);
813c8eea
AL
757
758 if (Version.Arch() == 0)
bdae53f1 759 {
d1f1f6a8 760 _error->Error(_("I wasn't able to locate a file for the %s package. "
7a3c2ab0
AL
761 "This might mean you need to manually fix this package. "
762 "(due to missing arch)"),
813c8eea 763 Version.ParentPkg().Name());
bdae53f1
AL
764 return;
765 }
813c8eea 766
b2e465d6
AL
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 }
b3d44315
MV
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
a3371852
MV
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
03e39e59 817 // Select a source
b185acc2 818 if (QueueNext() == false && _error->PendingError() == false)
b2e465d6
AL
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."),
b185acc2
AL
821 Version.ParentPkg().Name());
822}
823 /*}}}*/
824// AcqArchive::QueueNext - Queue the next file source /*{{{*/
825// ---------------------------------------------------------------------
17caf1b1
AL
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. */
b185acc2 829bool pkgAcqArchive::QueueNext()
b2e465d6 830{
03e39e59
AL
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
b2e465d6
AL
838 pkgIndexFile *Index;
839 if (Sources->FindIndex(Vf.File(),Index) == false)
840 continue;
03e39e59 841
b3d44315
MV
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
03e39e59
AL
847 // Grab the text package record
848 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
849 if (_error->PendingError() == true)
b185acc2 850 return false;
03e39e59 851
b2e465d6 852 string PkgFile = Parse.FileName();
03e39e59
AL
853 MD5 = Parse.MD5Hash();
854 if (PkgFile.empty() == true)
b2e465d6
AL
855 return _error->Error(_("The package index files are corrupted. No Filename: "
856 "field for package %s."),
857 Version.ParentPkg().Name());
a6568219 858
b3d44315
MV
859 Desc.URI = Index->ArchiveURI(PkgFile);
860 Desc.Description = Index->ArchiveInfo(Version);
861 Desc.Owner = this;
862 Desc.ShortDesc = Version.ParentPkg().Name();
863
17caf1b1 864 // See if we already have the file. (Legacy filenames)
a6568219
AL
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;
30e1eab5 876 StoreFilename = DestFile = FinalFile;
b185acc2 877 return true;
a6568219
AL
878 }
879
6b1ff003
AL
880 /* Hmm, we have a file and its size does not match, this means it is
881 an old style mismatched arch */
a6568219
AL
882 unlink(FinalFile.c_str());
883 }
17caf1b1
AL
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);
6b1ff003
AL
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
03e39e59 916 // Create the item
b2e465d6
AL
917 Local = false;
918 Desc.URI = Index->ArchiveURI(PkgFile);
919 Desc.Description = Index->ArchiveInfo(Version);
03e39e59
AL
920 Desc.Owner = this;
921 Desc.ShortDesc = Version.ParentPkg().Name();
922 QueueURI(Desc);
b185acc2
AL
923
924 Vf++;
925 return true;
03e39e59 926 }
b185acc2
AL
927 return false;
928}
03e39e59
AL
929 /*}}}*/
930// AcqArchive::Done - Finished fetching /*{{{*/
931// ---------------------------------------------------------------------
932/* */
459681d3
AL
933void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
934 pkgAcquire::MethodConfig *Cfg)
03e39e59 935{
459681d3 936 Item::Done(Message,Size,Md5Hash,Cfg);
03e39e59
AL
937
938 // Check the size
939 if (Size != Version->Size)
940 {
bdae53f1 941 Status = StatError;
b2e465d6 942 ErrorText = _("Size mismatch");
03e39e59
AL
943 return;
944 }
945
946 // Check the md5
947 if (Md5Hash.empty() == false && MD5.empty() == false)
948 {
949 if (Md5Hash != MD5)
950 {
bdae53f1 951 Status = StatError;
b2e465d6 952 ErrorText = _("MD5Sum mismatch");
13e8426f
MV
953 if(FileExists(DestFile))
954 Rename(DestFile,DestFile + ".FAILED");
03e39e59
AL
955 return;
956 }
957 }
a6568219
AL
958
959 // Grab the output filename
03e39e59
AL
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 }
a6568219
AL
967
968 Complete = true;
30e1eab5
AL
969
970 // Reference filename
a6568219
AL
971 if (FileName != DestFile)
972 {
30e1eab5 973 StoreFilename = DestFile = FileName;
a6568219
AL
974 Local = true;
975 return;
976 }
977
978 // Done, move it into position
979 string FinalFile = _config->FindDir("Dir::Cache::Archives");
17caf1b1 980 FinalFile += flNotDir(StoreFilename);
a6568219 981 Rename(DestFile,FinalFile);
03e39e59 982
30e1eab5 983 StoreFilename = DestFile = FinalFile;
03e39e59
AL
984 Complete = true;
985}
986 /*}}}*/
db890fdb
AL
987// AcqArchive::Failed - Failure handler /*{{{*/
988// ---------------------------------------------------------------------
989/* Here we try other sources */
7d8afa39 990void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
db890fdb
AL
991{
992 ErrorText = LookupTag(Message,"Message");
b2e465d6
AL
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
db890fdb 1007 if (QueueNext() == false)
7d8afa39
AL
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
9dbb421f 1020 StoreFilename = string();
7d8afa39
AL
1021 Item::Failed(Message,Cnf);
1022 }
db890fdb
AL
1023}
1024 /*}}}*/
b3d44315
MV
1025// AcqArchive::IsTrusted - Determine whether this archive comes from a
1026// trusted source /*{{{*/
1027// ---------------------------------------------------------------------
1028bool pkgAcqArchive::IsTrusted()
1029{
1030 return Trusted;
1031}
1032
ab559b35
AL
1033// AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1034// ---------------------------------------------------------------------
1035/* */
1036void pkgAcqArchive::Finished()
1037{
1038 if (Status == pkgAcquire::Item::StatDone &&
1039 Complete == true)
1040 return;
1041 StoreFilename = string();
1042}
1043 /*}}}*/
36375005
AL
1044
1045// AcqFile::pkgAcqFile - Constructor /*{{{*/
1046// ---------------------------------------------------------------------
1047/* The file is added to the queue */
1048pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
46e00f9d
MV
1049 unsigned long Size,string Dsc,string ShortDesc,
1050 const string &DestDir, const string &DestFilename) :
b3c39978 1051 Item(Owner), Md5Hash(MD5)
36375005 1052{
08cfc005
AL
1053 Retries = _config->FindI("Acquire::Retries",0);
1054
46e00f9d
MV
1055 if(!DestFilename.empty())
1056 DestFile = DestFilename;
1057 else if(!DestDir.empty())
1058 DestFile = DestDir + "/" + flNotDir(URI);
1059 else
1060 DestFile = flNotDir(URI);
1061
36375005
AL
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 }
092ae175 1081
36375005
AL
1082 QueueURI(Desc);
1083}
1084 /*}}}*/
1085// AcqFile::Done - Item downloaded OK /*{{{*/
1086// ---------------------------------------------------------------------
1087/* */
459681d3
AL
1088void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
1089 pkgAcquire::MethodConfig *Cnf)
36375005 1090{
b3c39978
AL
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
459681d3 1103 Item::Done(Message,Size,MD5,Cnf);
36375005
AL
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;
459681d3
AL
1123 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1124 Cnf->Removable == true)
917ae805
AL
1125 {
1126 Desc.URI = "copy:" + FileName;
1127 QueueURI(Desc);
1128 return;
1129 }
1130
83ab33fc
AL
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
917ae805
AL
1140 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1141 {
83ab33fc 1142 ErrorText = "Link to " + DestFile + " failure ";
917ae805
AL
1143 Status = StatError;
1144 Complete = false;
1145 }
36375005
AL
1146 }
1147}
1148 /*}}}*/
08cfc005
AL
1149// AcqFile::Failed - Failure handler /*{{{*/
1150// ---------------------------------------------------------------------
1151/* Here we try other sources */
1152void 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 /*}}}*/