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