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