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