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