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