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