]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
fff55f7b55acda96562ad0f11511e8cb2e9b576d
[apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
4 /* ######################################################################
5
6 Acquire Item - Item to acquire
7
8 Each item can download to exactly one file at a time. This means you
9 cannot create an item that fetches two uri's to two files at the same
10 time. The pkgAcqIndex class creates a second class upon instantiation
11 to fetch the other index files because of this.
12
13 ##################################################################### */
14 /*}}}*/
15 // Include Files /*{{{*/
16 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire-item.h"
18 #endif
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/vendorlist.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/fileutl.h>
26 #include <apt-pkg/md5.h>
27 #include <apt-pkg/sha1.h>
28 #include <apt-pkg/tagfile.h>
29
30 #include <apti18n.h>
31
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <errno.h>
35 #include <string>
36 #include <sstream>
37 #include <stdio.h>
38 /*}}}*/
39
40 using namespace std;
41
42 // Acquire::Item::Item - Constructor /*{{{*/
43 // ---------------------------------------------------------------------
44 /* */
45 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
46 PartialSize(0), Mode(0), ID(0), Complete(false),
47 Local(false), QueueCounter(0)
48 {
49 Owner->Add(this);
50 Status = StatIdle;
51 }
52 /*}}}*/
53 // Acquire::Item::~Item - Destructor /*{{{*/
54 // ---------------------------------------------------------------------
55 /* */
56 pkgAcquire::Item::~Item()
57 {
58 Owner->Remove(this);
59 }
60 /*}}}*/
61 // Acquire::Item::Failed - Item failed to download /*{{{*/
62 // ---------------------------------------------------------------------
63 /* We return to an idle state if there are still other queues that could
64 fetch this object */
65 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
66 {
67 Status = StatIdle;
68 ErrorText = LookupTag(Message,"Message");
69 if (QueueCounter <= 1)
70 {
71 /* This indicates that the file is not available right now but might
72 be sometime later. If we do a retry cycle then this should be
73 retried [CDROMs] */
74 if (Cnf->LocalOnly == true &&
75 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
76 {
77 Status = StatIdle;
78 Dequeue();
79 return;
80 }
81
82 Status = StatError;
83 Dequeue();
84 }
85 }
86 /*}}}*/
87 // Acquire::Item::Start - Item has begun to download /*{{{*/
88 // ---------------------------------------------------------------------
89 /* Stash status and the file size. Note that setting Complete means
90 sub-phases of the acquire process such as decompresion are operating */
91 void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
92 {
93 Status = StatFetching;
94 if (FileSize == 0 && Complete == false)
95 FileSize = Size;
96 }
97 /*}}}*/
98 // Acquire::Item::Done - Item downloaded OK /*{{{*/
99 // ---------------------------------------------------------------------
100 /* */
101 void pkgAcquire::Item::Done(string Message,unsigned long Size,string,
102 pkgAcquire::MethodConfig *Cnf)
103 {
104 // We just downloaded something..
105 string FileName = LookupTag(Message,"Filename");
106 if (Complete == false && FileName == DestFile)
107 {
108 if (Owner->Log != 0)
109 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
110 }
111
112 if (FileSize == 0)
113 FileSize= Size;
114
115 Status = StatDone;
116 ErrorText = string();
117 Owner->Dequeue(this);
118 }
119 /*}}}*/
120 // Acquire::Item::Rename - Rename a file /*{{{*/
121 // ---------------------------------------------------------------------
122 /* This helper function is used by alot of item methods as thier final
123 step */
124 void pkgAcquire::Item::Rename(string From,string To)
125 {
126 if (rename(From.c_str(),To.c_str()) != 0)
127 {
128 char S[300];
129 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
130 From.c_str(),To.c_str());
131 Status = StatError;
132 ErrorText = S;
133 }
134 }
135 /*}}}*/
136
137 // AcqIndexDiffs::AcqIndexDiffs - Constructor
138 // ---------------------------------------------------------------------
139 /* The package diff is added to the queue. one object is constructed
140 * for each diff and the index
141 */
142 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
143 string URI,string URIDesc,string ShortDesc,
144 string ExpectedMD5, vector<DiffInfo> diffs)
145 : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5), available_patches(diffs)
146 {
147
148 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
149 DestFile += URItoFileName(URI);
150
151 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
152
153 Desc.Description = URIDesc;
154 Desc.Owner = this;
155 Desc.ShortDesc = ShortDesc;
156
157 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
158 CurrentPackagesFile += URItoFileName(RealURI);
159
160 if(Debug) {
161 std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
162 << CurrentPackagesFile << std::endl;
163 }
164
165 if(!FileExists(CurrentPackagesFile) ||
166 !_config->FindB("Acquire::Diffs",true)) {
167 // we don't have a pkg file or we don't want to queue
168 if(Debug)
169 std::clog << "No index file or canceld by user" << std::endl;
170 Failed("", NULL);
171 return;
172 }
173
174 if(available_patches.size() == 0)
175 QueueDiffIndex(URI);
176 else
177 QueueNextDiff();
178 }
179
180 void pkgAcqIndexDiffs::QueueDiffIndex(string URI)
181 {
182 Desc.URI = URI + ".diff/Index";
183 Desc.Description = Description + "IndexDiff";
184 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
185 DestFile += URItoFileName(URI) + string(".IndexDiff");
186
187 if(Debug)
188 std::clog << "QueueDiffIndex: " << Desc.URI << std::endl;
189
190 QueueURI(Desc);
191 }
192
193 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
194 // ---------------------------------------------------------------------
195 /* The only header we use is the last-modified header. */
196 string pkgAcqIndexDiffs::Custom600Headers()
197 {
198 if(DestFile.rfind(".IndexDiff") == string::npos)
199 return string("");
200
201 string Final = _config->FindDir("Dir::State::lists");
202 Final += URItoFileName(RealURI) + string(".IndexDiff");
203
204 if(Debug)
205 std::clog << "Custom600Header-IMS: " << Final << std::endl;
206
207 struct stat Buf;
208 if (stat(Final.c_str(),&Buf) != 0)
209 return "\nIndex-File: true";
210
211 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
212 }
213
214 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
215 {
216 if(Debug)
217 std::clog << "Failed(): " << Desc.URI << std::endl
218 << "Falling back to big package file" << std::endl;
219 new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc,
220 ExpectedMD5);
221 Finish();
222 }
223
224
225 // helper that cleans the item out of the fetcher queue
226 void pkgAcqIndexDiffs::Finish(bool allDone)
227 {
228 // we restore the original name, this is required, otherwise
229 // the file will be cleaned
230 if(allDone) {
231 // this is for the "real" finish
232 DestFile = _config->FindDir("Dir::State::lists");
233 DestFile += URItoFileName(RealURI);
234 Complete = true;
235 Dequeue();
236 if(Debug)
237 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
238 return;
239
240 }
241
242 if(Debug)
243 std::clog << "Finishing: " << Desc.URI << std::endl;
244 Complete = false;
245 Status = StatDone;
246 Dequeue();
247 return;
248 }
249
250
251 // this needs to be rewriten to not depend on the external ed
252 bool pkgAcqIndexDiffs::ApplyDiff(string PatchFile)
253 {
254 char *error;
255 int res=0;
256
257 string FinalFile = _config->FindDir("Dir::State::lists");
258 FinalFile += URItoFileName(RealURI);
259
260 int Process = ExecFork();
261 if (Process == 0)
262 {
263 chdir(_config->FindDir("Dir::State::lists").c_str());
264 // for some reason "red" fails with the pdiffs from p.d.o/~aba ?!?
265 string cmd = "(zcat " + PatchFile + "; echo \"wq\" ) | /bin/ed " + FinalFile + " >/dev/null 2>/dev/null";
266 if(Debug)
267 std::clog << "Runing: " << cmd << std::endl;
268 res = system(cmd.c_str());
269 _exit(WEXITSTATUS(res));
270 }
271 if(!ExecWait(Process, error, true)) {
272 //_error->Error("Patch failed: %s ", error);
273 return false;
274 }
275
276 return true;
277 }
278
279 bool pkgAcqIndexDiffs::QueueNextDiff()
280 {
281 // calc sha1 of the just patched file
282 string FinalFile = _config->FindDir("Dir::State::lists");
283 FinalFile += URItoFileName(RealURI);
284
285 FileFd fd(FinalFile, FileFd::ReadOnly);
286 SHA1Summation SHA1;
287 SHA1.AddFD(fd.Fd(), fd.Size());
288 string local_sha1 = string(SHA1.Result());
289
290 // remove all patches until the next matching patch is found
291 // this requires the Index file to be ordered
292 for(vector<DiffInfo>::iterator I=available_patches.begin();
293 (*I).sha1 == local_sha1 || I != available_patches.end();
294 I++)
295 available_patches.erase(I);
296
297
298 // error checking and falling back if no patch was found
299 if(available_patches.size() == 0) {
300 Failed("", NULL);
301 return false;
302 }
303
304 // queue the right diff
305 Desc.URI = string(RealURI) + string(".diff/") + available_patches[0].file + string(".gz");
306 Desc.Description = available_patches[0].file + string(".pdiff");
307
308 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
309 DestFile += URItoFileName(RealURI + string(".diff/") + available_patches[0].file);
310
311 if(Debug)
312 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
313
314 QueueURI(Desc);
315
316 return true;
317 }
318
319 bool pkgAcqIndexDiffs::ParseIndexDiff(string IndexDiffFile)
320 {
321 if(Debug)
322 std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
323 << std::endl;
324
325 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
326 pkgTagFile TF(&Fd);
327 if (_error->PendingError() == true)
328 return false;
329 pkgTagSection Tags;
330 if(TF.Step(Tags) == true)
331 {
332 string local_sha1;
333 string tmp = Tags.FindS("SHA1-Current");
334 std::stringstream ss(tmp);
335 ss >> ServerSha1;
336
337 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
338 SHA1Summation SHA1;
339 SHA1.AddFD(fd.Fd(), fd.Size());
340 local_sha1 = string(SHA1.Result());
341
342 if(local_sha1 == ServerSha1) {
343 if(Debug)
344 std::clog << "Package file is up-to-date" << std::endl;
345 Finish(true);
346 return true;
347 }
348 if(Debug)
349 std::clog << "SHA1-Current: " << ServerSha1 << std::endl;
350
351 // check the historie and see what patches we need
352 string history = Tags.FindS("SHA1-History");
353 std::stringstream hist(history);
354 DiffInfo d;
355 string size;
356 bool found = false;
357 while(hist >> d.sha1 >> size >> d.file) {
358 d.size = atoi(size.c_str());
359 if(d.sha1 == local_sha1)
360 found=true;
361 if(found) {
362 if(Debug)
363 std::clog << "Need to get diff: " << d.file << std::endl;
364 available_patches.push_back(d);
365 }
366 }
367 // no information how to get the patches, bail out
368 if(!found) {
369 if(Debug)
370 std::clog << "Can't find a patch in the index file" << std::endl;
371 // Failed will queue a big package file
372 Failed("", NULL);
373 } else {
374 // queue the diffs
375 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
376 ExpectedMD5, available_patches);
377 Finish();
378 return true;
379 }
380 }
381
382 return false;
383 }
384
385
386 void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash,
387 pkgAcquire::MethodConfig *Cnf)
388 {
389 if(Debug)
390 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
391
392 Item::Done(Message,Size,Md5Hash,Cnf);
393
394 int len = Desc.URI.size();
395 // sucess in downloading the index
396 if(Desc.URI.substr(len-strlen("Index"),len-1) == "Index") {
397
398 // rename
399 string FinalFile = _config->FindDir("Dir::State::lists");
400 FinalFile += URItoFileName(RealURI) + string(".IndexDiff");
401 if(Debug)
402 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
403 << std::endl;
404 Rename(DestFile,FinalFile);
405 chmod(FinalFile.c_str(),0644);
406 DestFile = FinalFile;
407
408 if(!ParseIndexDiff(DestFile))
409 return Failed("", NULL);
410 else
411 return Finish();
412 }
413
414 // sucess in downloading a diff
415 if(Desc.URI.find(".diff") != string::npos) {
416 ApplyDiff(DestFile);
417 available_patches.erase(available_patches.begin());
418
419 if(available_patches.size() > 0) {
420 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
421 ExpectedMD5, available_patches);
422 } else {
423 Finish(true);
424 return;
425 }
426 }
427
428 Finish();
429 }
430
431
432 // AcqIndex::AcqIndex - Constructor /*{{{*/
433 // ---------------------------------------------------------------------
434 /* The package file is added to the queue and a second class is
435 instantiated to fetch the revision file */
436 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
437 string URI,string URIDesc,string ShortDesc,
438 string ExpectedMD5, string comprExt)
439 : Item(Owner), RealURI(URI), ExpectedMD5(ExpectedMD5)
440 {
441 Decompression = false;
442 Erase = false;
443
444 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
445 DestFile += URItoFileName(URI);
446
447 if(comprExt.empty())
448 {
449 // autoselect
450 if(FileExists("/usr/bin/bzip2"))
451 Desc.URI = URI + ".bz2";
452 else
453 Desc.URI = URI + ".gz";
454 } else {
455 Desc.URI = URI + comprExt;
456 }
457
458 Desc.Description = URIDesc;
459 Desc.Owner = this;
460 Desc.ShortDesc = ShortDesc;
461
462 QueueURI(Desc);
463 }
464 /*}}}*/
465 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
466 // ---------------------------------------------------------------------
467 /* The only header we use is the last-modified header. */
468 string pkgAcqIndex::Custom600Headers()
469 {
470 string Final = _config->FindDir("Dir::State::lists");
471 Final += URItoFileName(RealURI);
472
473 struct stat Buf;
474 if (stat(Final.c_str(),&Buf) != 0)
475 return "\nIndex-File: true";
476
477 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
478 }
479 /*}}}*/
480
481 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
482 {
483 // no .bz2 found, retry with .gz
484 if(Desc.URI.substr(Desc.URI.size()-3,Desc.URI.size()-1) == "bz2") {
485 Desc.URI = Desc.URI.substr(0,Desc.URI.size()-3) + "gz";
486
487 // retry with a gzip one
488 new pkgAcqIndex(Owner, RealURI, Desc.Description,Desc.ShortDesc,
489 ExpectedMD5, string(".gz"));
490 Status = StatDone;
491 Complete = false;
492 Dequeue();
493 return;
494 }
495
496
497 Item::Failed(Message,Cnf);
498 }
499
500
501 // AcqIndex::Done - Finished a fetch /*{{{*/
502 // ---------------------------------------------------------------------
503 /* This goes through a number of states.. On the initial fetch the
504 method could possibly return an alternate filename which points
505 to the uncompressed version of the file. If this is so the file
506 is copied into the partial directory. In all other cases the file
507 is decompressed with a gzip uri. */
508 void pkgAcqIndex::Done(string Message,unsigned long Size,string MD5,
509 pkgAcquire::MethodConfig *Cfg)
510 {
511 Item::Done(Message,Size,MD5,Cfg);
512
513 if (Decompression == true)
514 {
515 if (_config->FindB("Debug::pkgAcquire::Auth", false))
516 {
517 std::cerr << std::endl << RealURI << ": Computed MD5: " << MD5;
518 std::cerr << " Expected MD5: " << ExpectedMD5 << std::endl;
519 }
520
521 if (MD5.empty())
522 {
523 MD5Summation sum;
524 FileFd Fd(DestFile, FileFd::ReadOnly);
525 sum.AddFD(Fd.Fd(), Fd.Size());
526 Fd.Close();
527 MD5 = (string)sum.Result();
528 }
529
530 if (!ExpectedMD5.empty() && MD5 != ExpectedMD5)
531 {
532 Status = StatAuthError;
533 ErrorText = _("MD5Sum mismatch");
534 Rename(DestFile,DestFile + ".FAILED");
535 return;
536 }
537 // Done, move it into position
538 string FinalFile = _config->FindDir("Dir::State::lists");
539 FinalFile += URItoFileName(RealURI);
540 Rename(DestFile,FinalFile);
541 chmod(FinalFile.c_str(),0644);
542
543 /* We restore the original name to DestFile so that the clean operation
544 will work OK */
545 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
546 DestFile += URItoFileName(RealURI);
547
548 // Remove the compressed version.
549 if (Erase == true)
550 unlink(DestFile.c_str());
551 return;
552 }
553
554 Erase = false;
555 Complete = true;
556
557 // Handle the unzipd case
558 string FileName = LookupTag(Message,"Alt-Filename");
559 if (FileName.empty() == false)
560 {
561 // The files timestamp matches
562 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
563 return;
564
565 Decompression = true;
566 Local = true;
567 DestFile += ".decomp";
568 Desc.URI = "copy:" + FileName;
569 QueueURI(Desc);
570 Mode = "copy";
571 return;
572 }
573
574 FileName = LookupTag(Message,"Filename");
575 if (FileName.empty() == true)
576 {
577 Status = StatError;
578 ErrorText = "Method gave a blank filename";
579 }
580
581 // The files timestamp matches
582 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
583 return;
584
585 if (FileName == DestFile)
586 Erase = true;
587 else
588 Local = true;
589
590 string compExt = Desc.URI.substr(Desc.URI.size()-3,Desc.URI.size()-1);
591 char *decompProg;
592 if(compExt == "bz2")
593 decompProg = "bzip2";
594 else if(compExt == ".gz")
595 decompProg = "gzip";
596 else {
597 _error->Error("Unsupported extension: %s", compExt.c_str());
598 return;
599 }
600
601 Decompression = true;
602 DestFile += ".decomp";
603 Desc.URI = string(decompProg) + ":" + FileName;
604 QueueURI(Desc);
605 Mode = decompProg;
606 }
607
608 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner,
609 string URI,string URIDesc,string ShortDesc,
610 string MetaIndexURI, string MetaIndexURIDesc,
611 string MetaIndexShortDesc,
612 const vector<IndexTarget*>* IndexTargets,
613 indexRecords* MetaIndexParser) :
614 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
615 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc)
616 {
617 this->MetaIndexParser = MetaIndexParser;
618 this->IndexTargets = IndexTargets;
619 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
620 DestFile += URItoFileName(URI);
621
622 // remove any partial downloaded sig-file. it may confuse proxies
623 // and is too small to warrant a partial download anyway
624 unlink(DestFile.c_str());
625
626 // Create the item
627 Desc.Description = URIDesc;
628 Desc.Owner = this;
629 Desc.ShortDesc = ShortDesc;
630 Desc.URI = URI;
631
632
633 string Final = _config->FindDir("Dir::State::lists");
634 Final += URItoFileName(RealURI);
635 struct stat Buf;
636 if (stat(Final.c_str(),&Buf) == 0)
637 {
638 // File was already in place. It needs to be re-verified
639 // because Release might have changed, so Move it into partial
640 Rename(Final,DestFile);
641 // unlink the file and do not try to use I-M-S and Last-Modified
642 // if the users proxy is broken
643 if(_config->FindB("Acquire::BrokenProxy", false) == true) {
644 std::cerr << "forcing re-get of the signature file as requested" << std::endl;
645 unlink(DestFile.c_str());
646 }
647 }
648
649 QueueURI(Desc);
650 }
651 /*}}}*/
652 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
653 // ---------------------------------------------------------------------
654 /* The only header we use is the last-modified header. */
655 string pkgAcqMetaSig::Custom600Headers()
656 {
657 struct stat Buf;
658 if (stat(DestFile.c_str(),&Buf) != 0)
659 return "\nIndex-File: true";
660
661 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
662 }
663
664 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
665 pkgAcquire::MethodConfig *Cfg)
666 {
667 Item::Done(Message,Size,MD5,Cfg);
668
669 string FileName = LookupTag(Message,"Filename");
670 if (FileName.empty() == true)
671 {
672 Status = StatError;
673 ErrorText = "Method gave a blank filename";
674 return;
675 }
676
677 if (FileName != DestFile)
678 {
679 // We have to copy it into place
680 Local = true;
681 Desc.URI = "copy:" + FileName;
682 QueueURI(Desc);
683 return;
684 }
685
686 Complete = true;
687
688 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
689 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
690 DestFile, IndexTargets, MetaIndexParser);
691
692 }
693 /*}}}*/
694 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
695 {
696 // Delete any existing sigfile, so that this source isn't
697 // mistakenly trusted
698 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
699 unlink(Final.c_str());
700
701 // queue a pkgAcqMetaIndex with no sigfile
702 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
703 "", IndexTargets, MetaIndexParser);
704
705 if (Cnf->LocalOnly == true ||
706 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
707 {
708 // Ignore this
709 Status = StatDone;
710 Complete = false;
711 Dequeue();
712 return;
713 }
714
715 Item::Failed(Message,Cnf);
716 }
717
718 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner,
719 string URI,string URIDesc,string ShortDesc,
720 string SigFile,
721 const vector<struct IndexTarget*>* IndexTargets,
722 indexRecords* MetaIndexParser) :
723 Item(Owner), RealURI(URI), SigFile(SigFile)
724 {
725 this->AuthPass = false;
726 this->MetaIndexParser = MetaIndexParser;
727 this->IndexTargets = IndexTargets;
728 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
729 DestFile += URItoFileName(URI);
730
731 // Create the item
732 Desc.Description = URIDesc;
733 Desc.Owner = this;
734 Desc.ShortDesc = ShortDesc;
735 Desc.URI = URI;
736
737 QueueURI(Desc);
738 }
739
740 /*}}}*/
741 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
742 // ---------------------------------------------------------------------
743 /* The only header we use is the last-modified header. */
744 string pkgAcqMetaIndex::Custom600Headers()
745 {
746 string Final = _config->FindDir("Dir::State::lists");
747 Final += URItoFileName(RealURI);
748
749 struct stat Buf;
750 if (stat(Final.c_str(),&Buf) != 0)
751 return "\nIndex-File: true";
752
753 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
754 }
755
756 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string MD5,
757 pkgAcquire::MethodConfig *Cfg)
758 {
759 Item::Done(Message,Size,MD5,Cfg);
760
761 // MetaIndexes are done in two passes: one to download the
762 // metaindex with an appropriate method, and a second to verify it
763 // with the gpgv method
764
765 if (AuthPass == true)
766 {
767 AuthDone(Message);
768 }
769 else
770 {
771 RetrievalDone(Message);
772 if (!Complete)
773 // Still more retrieving to do
774 return;
775
776 if (SigFile == "")
777 {
778 // There was no signature file, so we are finished. Download
779 // the indexes without verification.
780 QueueIndexes(false);
781 }
782 else
783 {
784 // There was a signature file, so pass it to gpgv for
785 // verification
786
787 if (_config->FindB("Debug::pkgAcquire::Auth", false))
788 std::cerr << "Metaindex acquired, queueing gpg verification ("
789 << SigFile << "," << DestFile << ")\n";
790 AuthPass = true;
791 Desc.URI = "gpgv:" + SigFile;
792 QueueURI(Desc);
793 Mode = "gpgv";
794 }
795 }
796 }
797
798 void pkgAcqMetaIndex::RetrievalDone(string Message)
799 {
800 // We have just finished downloading a Release file (it is not
801 // verified yet)
802
803 string FileName = LookupTag(Message,"Filename");
804 if (FileName.empty() == true)
805 {
806 Status = StatError;
807 ErrorText = "Method gave a blank filename";
808 return;
809 }
810
811 if (FileName != DestFile)
812 {
813 Local = true;
814 Desc.URI = "copy:" + FileName;
815 QueueURI(Desc);
816 return;
817 }
818
819 Complete = true;
820
821 string FinalFile = _config->FindDir("Dir::State::lists");
822 FinalFile += URItoFileName(RealURI);
823
824 // The files timestamp matches
825 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == false)
826 {
827 // Move it into position
828 Rename(DestFile,FinalFile);
829 }
830 DestFile = FinalFile;
831 }
832
833 void pkgAcqMetaIndex::AuthDone(string Message)
834 {
835 // At this point, the gpgv method has succeeded, so there is a
836 // valid signature from a key in the trusted keyring. We
837 // perform additional verification of its contents, and use them
838 // to verify the indexes we are about to download
839
840 if (!MetaIndexParser->Load(DestFile))
841 {
842 Status = StatAuthError;
843 ErrorText = MetaIndexParser->ErrorText;
844 return;
845 }
846
847 if (!VerifyVendor())
848 {
849 return;
850 }
851
852 if (_config->FindB("Debug::pkgAcquire::Auth", false))
853 std::cerr << "Signature verification succeeded: "
854 << DestFile << std::endl;
855
856 // Download further indexes with verification
857 QueueIndexes(true);
858
859 // Done, move signature file into position
860
861 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
862 URItoFileName(RealURI) + ".gpg";
863 Rename(SigFile,VerifiedSigFile);
864 chmod(VerifiedSigFile.c_str(),0644);
865 }
866
867 void pkgAcqMetaIndex::QueueIndexes(bool verify)
868 {
869 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
870 Target != IndexTargets->end();
871 Target++)
872 {
873 string ExpectedIndexMD5;
874 if (verify)
875 {
876 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
877 if (!Record)
878 {
879 Status = StatAuthError;
880 ErrorText = "Unable to find expected entry "
881 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
882 return;
883 }
884 ExpectedIndexMD5 = Record->MD5Hash;
885 if (_config->FindB("Debug::pkgAcquire::Auth", false))
886 {
887 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
888 std::cerr << "Expected MD5: " << ExpectedIndexMD5 << std::endl;
889 }
890 if (ExpectedIndexMD5.empty())
891 {
892 Status = StatAuthError;
893 ErrorText = "Unable to find MD5 sum for "
894 + (*Target)->MetaKey + " in Meta-index file";
895 return;
896 }
897 }
898
899 // Queue Packages file
900 new pkgAcqIndexDiffs(Owner, (*Target)->URI, (*Target)->Description,
901 (*Target)->ShortDesc, ExpectedIndexMD5);
902 }
903 }
904
905 bool pkgAcqMetaIndex::VerifyVendor()
906 {
907 // // Maybe this should be made available from above so we don't have
908 // // to read and parse it every time?
909 // pkgVendorList List;
910 // List.ReadMainList();
911
912 // const Vendor* Vndr = NULL;
913 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
914 // {
915 // string::size_type pos = (*I).find("VALIDSIG ");
916 // if (_config->FindB("Debug::Vendor", false))
917 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
918 // << std::endl;
919 // if (pos != std::string::npos)
920 // {
921 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
922 // if (_config->FindB("Debug::Vendor", false))
923 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
924 // std::endl;
925 // Vndr = List.FindVendor(Fingerprint) != "";
926 // if (Vndr != NULL);
927 // break;
928 // }
929 // }
930
931 string Transformed = MetaIndexParser->GetExpectedDist();
932
933 if (Transformed == "../project/experimental")
934 {
935 Transformed = "experimental";
936 }
937
938 string::size_type pos = Transformed.rfind('/');
939 if (pos != string::npos)
940 {
941 Transformed = Transformed.substr(0, pos);
942 }
943
944 if (Transformed == ".")
945 {
946 Transformed = "";
947 }
948
949 if (_config->FindB("Debug::pkgAcquire::Auth", false))
950 {
951 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
952 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
953 std::cerr << "Transformed Dist: " << Transformed << std::endl;
954 }
955
956 if (MetaIndexParser->CheckDist(Transformed) == false)
957 {
958 // This might become fatal one day
959 // Status = StatAuthError;
960 // ErrorText = "Conflicting distribution; expected "
961 // + MetaIndexParser->GetExpectedDist() + " but got "
962 // + MetaIndexParser->GetDist();
963 // return false;
964 if (!Transformed.empty())
965 {
966 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
967 Desc.Description.c_str(),
968 Transformed.c_str(),
969 MetaIndexParser->GetDist().c_str());
970 }
971 }
972
973 return true;
974 }
975 /*}}}*/
976 // pkgAcqMetaIndex::Failed - no Release file present or no signature
977 // file present /*{{{*/
978 // ---------------------------------------------------------------------
979 /* */
980 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
981 {
982 if (AuthPass == true)
983 {
984 // gpgv method failed
985 _error->Warning("GPG error: %s: %s",
986 Desc.Description.c_str(),
987 LookupTag(Message,"Message").c_str());
988 }
989
990 // No Release file was present, or verification failed, so fall
991 // back to queueing Packages files without verification
992 QueueIndexes(false);
993 }
994
995 /*}}}*/
996
997 // AcqArchive::AcqArchive - Constructor /*{{{*/
998 // ---------------------------------------------------------------------
999 /* This just sets up the initial fetch environment and queues the first
1000 possibilitiy */
1001 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1002 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1003 string &StoreFilename) :
1004 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
1005 StoreFilename(StoreFilename), Vf(Version.FileList()),
1006 Trusted(false)
1007 {
1008 Retries = _config->FindI("Acquire::Retries",0);
1009
1010 if (Version.Arch() == 0)
1011 {
1012 _error->Error(_("I wasn't able to locate a file for the %s package. "
1013 "This might mean you need to manually fix this package. "
1014 "(due to missing arch)"),
1015 Version.ParentPkg().Name());
1016 return;
1017 }
1018
1019 /* We need to find a filename to determine the extension. We make the
1020 assumption here that all the available sources for this version share
1021 the same extension.. */
1022 // Skip not source sources, they do not have file fields.
1023 for (; Vf.end() == false; Vf++)
1024 {
1025 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1026 continue;
1027 break;
1028 }
1029
1030 // Does not really matter here.. we are going to fail out below
1031 if (Vf.end() != true)
1032 {
1033 // If this fails to get a file name we will bomb out below.
1034 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1035 if (_error->PendingError() == true)
1036 return;
1037
1038 // Generate the final file name as: package_version_arch.foo
1039 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1040 QuoteString(Version.VerStr(),"_:") + '_' +
1041 QuoteString(Version.Arch(),"_:.") +
1042 "." + flExtension(Parse.FileName());
1043 }
1044
1045 // check if we have one trusted source for the package. if so, switch
1046 // to "TrustedOnly" mode
1047 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
1048 {
1049 pkgIndexFile *Index;
1050 if (Sources->FindIndex(i.File(),Index) == false)
1051 continue;
1052 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1053 {
1054 std::cerr << "Checking index: " << Index->Describe()
1055 << "(Trusted=" << Index->IsTrusted() << ")\n";
1056 }
1057 if (Index->IsTrusted()) {
1058 Trusted = true;
1059 break;
1060 }
1061 }
1062
1063 // Select a source
1064 if (QueueNext() == false && _error->PendingError() == false)
1065 _error->Error(_("I wasn't able to locate file for the %s package. "
1066 "This might mean you need to manually fix this package."),
1067 Version.ParentPkg().Name());
1068 }
1069 /*}}}*/
1070 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1071 // ---------------------------------------------------------------------
1072 /* This queues the next available file version for download. It checks if
1073 the archive is already available in the cache and stashs the MD5 for
1074 checking later. */
1075 bool pkgAcqArchive::QueueNext()
1076 {
1077 for (; Vf.end() == false; Vf++)
1078 {
1079 // Ignore not source sources
1080 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1081 continue;
1082
1083 // Try to cross match against the source list
1084 pkgIndexFile *Index;
1085 if (Sources->FindIndex(Vf.File(),Index) == false)
1086 continue;
1087
1088 // only try to get a trusted package from another source if that source
1089 // is also trusted
1090 if(Trusted && !Index->IsTrusted())
1091 continue;
1092
1093 // Grab the text package record
1094 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1095 if (_error->PendingError() == true)
1096 return false;
1097
1098 string PkgFile = Parse.FileName();
1099 MD5 = Parse.MD5Hash();
1100 if (PkgFile.empty() == true)
1101 return _error->Error(_("The package index files are corrupted. No Filename: "
1102 "field for package %s."),
1103 Version.ParentPkg().Name());
1104
1105 Desc.URI = Index->ArchiveURI(PkgFile);
1106 Desc.Description = Index->ArchiveInfo(Version);
1107 Desc.Owner = this;
1108 Desc.ShortDesc = Version.ParentPkg().Name();
1109
1110 // See if we already have the file. (Legacy filenames)
1111 FileSize = Version->Size;
1112 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1113 struct stat Buf;
1114 if (stat(FinalFile.c_str(),&Buf) == 0)
1115 {
1116 // Make sure the size matches
1117 if ((unsigned)Buf.st_size == Version->Size)
1118 {
1119 Complete = true;
1120 Local = true;
1121 Status = StatDone;
1122 StoreFilename = DestFile = FinalFile;
1123 return true;
1124 }
1125
1126 /* Hmm, we have a file and its size does not match, this means it is
1127 an old style mismatched arch */
1128 unlink(FinalFile.c_str());
1129 }
1130
1131 // Check it again using the new style output filenames
1132 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1133 if (stat(FinalFile.c_str(),&Buf) == 0)
1134 {
1135 // Make sure the size matches
1136 if ((unsigned)Buf.st_size == Version->Size)
1137 {
1138 Complete = true;
1139 Local = true;
1140 Status = StatDone;
1141 StoreFilename = DestFile = FinalFile;
1142 return true;
1143 }
1144
1145 /* Hmm, we have a file and its size does not match, this shouldnt
1146 happen.. */
1147 unlink(FinalFile.c_str());
1148 }
1149
1150 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
1151
1152 // Check the destination file
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 > Version->Size)
1157 unlink(DestFile.c_str());
1158 else
1159 PartialSize = Buf.st_size;
1160 }
1161
1162 // Create the item
1163 Local = false;
1164 Desc.URI = Index->ArchiveURI(PkgFile);
1165 Desc.Description = Index->ArchiveInfo(Version);
1166 Desc.Owner = this;
1167 Desc.ShortDesc = Version.ParentPkg().Name();
1168 QueueURI(Desc);
1169
1170 Vf++;
1171 return true;
1172 }
1173 return false;
1174 }
1175 /*}}}*/
1176 // AcqArchive::Done - Finished fetching /*{{{*/
1177 // ---------------------------------------------------------------------
1178 /* */
1179 void pkgAcqArchive::Done(string Message,unsigned long Size,string Md5Hash,
1180 pkgAcquire::MethodConfig *Cfg)
1181 {
1182 Item::Done(Message,Size,Md5Hash,Cfg);
1183
1184 // Check the size
1185 if (Size != Version->Size)
1186 {
1187 Status = StatError;
1188 ErrorText = _("Size mismatch");
1189 return;
1190 }
1191
1192 // Check the md5
1193 if (Md5Hash.empty() == false && MD5.empty() == false)
1194 {
1195 if (Md5Hash != MD5)
1196 {
1197 Status = StatError;
1198 ErrorText = _("MD5Sum mismatch");
1199 Rename(DestFile,DestFile + ".FAILED");
1200 return;
1201 }
1202 }
1203
1204 // Grab the output filename
1205 string FileName = LookupTag(Message,"Filename");
1206 if (FileName.empty() == true)
1207 {
1208 Status = StatError;
1209 ErrorText = "Method gave a blank filename";
1210 return;
1211 }
1212
1213 Complete = true;
1214
1215 // Reference filename
1216 if (FileName != DestFile)
1217 {
1218 StoreFilename = DestFile = FileName;
1219 Local = true;
1220 return;
1221 }
1222
1223 // Done, move it into position
1224 string FinalFile = _config->FindDir("Dir::Cache::Archives");
1225 FinalFile += flNotDir(StoreFilename);
1226 Rename(DestFile,FinalFile);
1227
1228 StoreFilename = DestFile = FinalFile;
1229 Complete = true;
1230 }
1231 /*}}}*/
1232 // AcqArchive::Failed - Failure handler /*{{{*/
1233 // ---------------------------------------------------------------------
1234 /* Here we try other sources */
1235 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1236 {
1237 ErrorText = LookupTag(Message,"Message");
1238
1239 /* We don't really want to retry on failed media swaps, this prevents
1240 that. An interesting observation is that permanent failures are not
1241 recorded. */
1242 if (Cnf->Removable == true &&
1243 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1244 {
1245 // Vf = Version.FileList();
1246 while (Vf.end() == false) Vf++;
1247 StoreFilename = string();
1248 Item::Failed(Message,Cnf);
1249 return;
1250 }
1251
1252 if (QueueNext() == false)
1253 {
1254 // This is the retry counter
1255 if (Retries != 0 &&
1256 Cnf->LocalOnly == false &&
1257 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1258 {
1259 Retries--;
1260 Vf = Version.FileList();
1261 if (QueueNext() == true)
1262 return;
1263 }
1264
1265 StoreFilename = string();
1266 Item::Failed(Message,Cnf);
1267 }
1268 }
1269 /*}}}*/
1270 // AcqArchive::IsTrusted - Determine whether this archive comes from a
1271 // trusted source /*{{{*/
1272 // ---------------------------------------------------------------------
1273 bool pkgAcqArchive::IsTrusted()
1274 {
1275 return Trusted;
1276 }
1277
1278 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1279 // ---------------------------------------------------------------------
1280 /* */
1281 void pkgAcqArchive::Finished()
1282 {
1283 if (Status == pkgAcquire::Item::StatDone &&
1284 Complete == true)
1285 return;
1286 StoreFilename = string();
1287 }
1288 /*}}}*/
1289
1290 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1291 // ---------------------------------------------------------------------
1292 /* The file is added to the queue */
1293 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string MD5,
1294 unsigned long Size,string Dsc,string ShortDesc) :
1295 Item(Owner), Md5Hash(MD5)
1296 {
1297 Retries = _config->FindI("Acquire::Retries",0);
1298
1299 DestFile = flNotDir(URI);
1300
1301 // Create the item
1302 Desc.URI = URI;
1303 Desc.Description = Dsc;
1304 Desc.Owner = this;
1305
1306 // Set the short description to the archive component
1307 Desc.ShortDesc = ShortDesc;
1308
1309 // Get the transfer sizes
1310 FileSize = Size;
1311 struct stat Buf;
1312 if (stat(DestFile.c_str(),&Buf) == 0)
1313 {
1314 // Hmm, the partial file is too big, erase it
1315 if ((unsigned)Buf.st_size > Size)
1316 unlink(DestFile.c_str());
1317 else
1318 PartialSize = Buf.st_size;
1319 }
1320
1321 QueueURI(Desc);
1322 }
1323 /*}}}*/
1324 // AcqFile::Done - Item downloaded OK /*{{{*/
1325 // ---------------------------------------------------------------------
1326 /* */
1327 void pkgAcqFile::Done(string Message,unsigned long Size,string MD5,
1328 pkgAcquire::MethodConfig *Cnf)
1329 {
1330 // Check the md5
1331 if (Md5Hash.empty() == false && MD5.empty() == false)
1332 {
1333 if (Md5Hash != MD5)
1334 {
1335 Status = StatError;
1336 ErrorText = "MD5Sum mismatch";
1337 Rename(DestFile,DestFile + ".FAILED");
1338 return;
1339 }
1340 }
1341
1342 Item::Done(Message,Size,MD5,Cnf);
1343
1344 string FileName = LookupTag(Message,"Filename");
1345 if (FileName.empty() == true)
1346 {
1347 Status = StatError;
1348 ErrorText = "Method gave a blank filename";
1349 return;
1350 }
1351
1352 Complete = true;
1353
1354 // The files timestamp matches
1355 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1356 return;
1357
1358 // We have to copy it into place
1359 if (FileName != DestFile)
1360 {
1361 Local = true;
1362 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1363 Cnf->Removable == true)
1364 {
1365 Desc.URI = "copy:" + FileName;
1366 QueueURI(Desc);
1367 return;
1368 }
1369
1370 // Erase the file if it is a symlink so we can overwrite it
1371 struct stat St;
1372 if (lstat(DestFile.c_str(),&St) == 0)
1373 {
1374 if (S_ISLNK(St.st_mode) != 0)
1375 unlink(DestFile.c_str());
1376 }
1377
1378 // Symlink the file
1379 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1380 {
1381 ErrorText = "Link to " + DestFile + " failure ";
1382 Status = StatError;
1383 Complete = false;
1384 }
1385 }
1386 }
1387 /*}}}*/
1388 // AcqFile::Failed - Failure handler /*{{{*/
1389 // ---------------------------------------------------------------------
1390 /* Here we try other sources */
1391 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1392 {
1393 ErrorText = LookupTag(Message,"Message");
1394
1395 // This is the retry counter
1396 if (Retries != 0 &&
1397 Cnf->LocalOnly == false &&
1398 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1399 {
1400 Retries--;
1401 QueueURI(Desc);
1402 return;
1403 }
1404
1405 Item::Failed(Message,Cnf);
1406 }
1407 /*}}}*/