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