Add a Acquire::CompressionTypes config variable from there the
[ntk/apt.git] / apt-pkg / acquire-item.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-item.cc,v 1.46.2.9 2004/01/16 18:51:11 mdz Exp $
4 /* ######################################################################
5
6 Acquire Item - Item to acquire
7
8 Each item can download to exactly one file at a time. This means you
9 cannot create an item that fetches two uri's to two files at the same
10 time. The pkgAcqIndex class creates a second class upon instantiation
11 to fetch the other index files because of this.
12
13 ##################################################################### */
14 /*}}}*/
15 // Include Files /*{{{*/
16 #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 Configuration::Item const *Opts = _config->Tree("Acquire::CompressionTypes");
555 if (Opts != 0)
556 Opts = Opts->Child;
557
558 const char dirBin[] = "Dir::Bin::";
559 for (; Opts != 0; Opts = Opts->Next)
560 {
561 if (Opts->Tag.empty() == true || Opts->Value.empty() == true)
562 continue;
563 const string bin = _config->FindFile(string(dirBin).append(Opts->Value).c_str(),"");
564 if (bin != "" && !FileExists(bin))
565 continue;
566 comprExt = '.' + Opts->Tag;
567 break;
568 }
569 }
570 CompressionExtension = ((comprExt == "plain" || comprExt == ".") ? "" : comprExt);
571
572 Desc.URI = URI + CompressionExtension;
573
574 Desc.Description = URIDesc;
575 Desc.Owner = this;
576 Desc.ShortDesc = ShortDesc;
577
578 QueueURI(Desc);
579 }
580 /*}}}*/
581 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
582 // ---------------------------------------------------------------------
583 /* The only header we use is the last-modified header. */
584 string pkgAcqIndex::Custom600Headers()
585 {
586 string Final = _config->FindDir("Dir::State::lists");
587 Final += URItoFileName(RealURI);
588
589 struct stat Buf;
590 if (stat(Final.c_str(),&Buf) != 0)
591 return "\nIndex-File: true";
592
593 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
594 }
595 /*}}}*/
596 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
597 {
598 Configuration::Item const *Opts = _config->Tree("Acquire::CompressionTypes");
599 if (Opts != 0)
600 Opts = Opts->Child;
601
602 const char dirBin[] = "Dir::Bin::";
603 for (; Opts != 0; Opts = Opts->Next)
604 {
605 if (Opts->Tag.empty() == true || Opts->Value.empty() == true)
606 continue;
607
608 // jump over all already checked compression types
609 const unsigned int nameLen = Desc.URI.size() - Opts->Tag.size();
610 if(Desc.URI.substr(nameLen) != Opts->Tag || Opts->Next == 0)
611 continue;
612
613 // check if we need an external binary for this compression type
614 const string bin = _config->FindFile(string(dirBin).append(Opts->Next->Value).c_str(),"");
615 if (bin != "" && !FileExists(bin))
616 continue;
617
618 // retry with the next extension
619 Desc.URI = Desc.URI.substr(0, nameLen) + Opts->Next->Tag;
620
621 new pkgAcqIndex(Owner, RealURI, Desc.Description, Desc.ShortDesc,
622 ExpectedHash, string(".").append(Opts->Next->Tag));
623
624 Status = StatDone;
625 Complete = false;
626 Dequeue();
627 return;
628 }
629
630 // on decompression failure, remove bad versions in partial/
631 if(Decompression && Erase) {
632 string s = _config->FindDir("Dir::State::lists") + "partial/";
633 s += URItoFileName(RealURI);
634 unlink(s.c_str());
635 }
636
637 Item::Failed(Message,Cnf);
638 }
639 /*}}}*/
640 // AcqIndex::Done - Finished a fetch /*{{{*/
641 // ---------------------------------------------------------------------
642 /* This goes through a number of states.. On the initial fetch the
643 method could possibly return an alternate filename which points
644 to the uncompressed version of the file. If this is so the file
645 is copied into the partial directory. In all other cases the file
646 is decompressed with a gzip uri. */
647 void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash,
648 pkgAcquire::MethodConfig *Cfg)
649 {
650 Item::Done(Message,Size,Hash,Cfg);
651
652 if (Decompression == true)
653 {
654 if (_config->FindB("Debug::pkgAcquire::Auth", false))
655 {
656 std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
657 std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
658 }
659
660 if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
661 {
662 Status = StatAuthError;
663 ErrorText = _("Hash Sum mismatch");
664 Rename(DestFile,DestFile + ".FAILED");
665 return;
666 }
667 // Done, move it into position
668 string FinalFile = _config->FindDir("Dir::State::lists");
669 FinalFile += URItoFileName(RealURI);
670 Rename(DestFile,FinalFile);
671 chmod(FinalFile.c_str(),0644);
672
673 /* We restore the original name to DestFile so that the clean operation
674 will work OK */
675 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
676 DestFile += URItoFileName(RealURI);
677
678 // Remove the compressed version.
679 if (Erase == true)
680 unlink(DestFile.c_str());
681 return;
682 }
683
684 Erase = false;
685 Complete = true;
686
687 // Handle the unzipd case
688 string FileName = LookupTag(Message,"Alt-Filename");
689 if (FileName.empty() == false)
690 {
691 // The files timestamp matches
692 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
693 return;
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 = flExtension(flNotDir(URI(Desc.URI).Path));
720 string decompProg;
721
722 // get the binary name for your used compression type
723 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
724 if(decompProg.empty() == false);
725 // flExtensions returns the full name if no extension is found
726 // this is why we have this complicated compare operation here
727 // FIMXE: add a new flJustExtension() that return "" if no
728 // extension is found and use that above so that it can
729 // be tested against ""
730 else if(compExt == flNotDir(URI(Desc.URI).Path))
731 decompProg = "copy";
732 else {
733 _error->Error("Unsupported extension: %s", compExt.c_str());
734 return;
735 }
736
737 Decompression = true;
738 DestFile += ".decomp";
739 Desc.URI = decompProg + ":" + FileName;
740 QueueURI(Desc);
741 Mode = decompProg.c_str();
742 }
743 /*}}}*/
744 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
745 // ---------------------------------------------------------------------
746 /* The Translation file is added to the queue */
747 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
748 string URI,string URIDesc,string ShortDesc)
749 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
750 {
751 }
752 /*}}}*/
753 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
754 // ---------------------------------------------------------------------
755 /* */
756 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
757 {
758 if (Cnf->LocalOnly == true ||
759 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
760 {
761 // Ignore this
762 Status = StatDone;
763 Complete = false;
764 Dequeue();
765 return;
766 }
767
768 Item::Failed(Message,Cnf);
769 }
770 /*}}}*/
771 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
772 string URI,string URIDesc,string ShortDesc,
773 string MetaIndexURI, string MetaIndexURIDesc,
774 string MetaIndexShortDesc,
775 const vector<IndexTarget*>* IndexTargets,
776 indexRecords* MetaIndexParser) :
777 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
778 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
779 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
780 {
781 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
782 DestFile += URItoFileName(URI);
783
784 // remove any partial downloaded sig-file in partial/.
785 // it may confuse proxies and is too small to warrant a
786 // partial download anyway
787 unlink(DestFile.c_str());
788
789 // Create the item
790 Desc.Description = URIDesc;
791 Desc.Owner = this;
792 Desc.ShortDesc = ShortDesc;
793 Desc.URI = URI;
794
795 string Final = _config->FindDir("Dir::State::lists");
796 Final += URItoFileName(RealURI);
797 struct stat Buf;
798 if (stat(Final.c_str(),&Buf) == 0)
799 {
800 // File was already in place. It needs to be re-downloaded/verified
801 // because Release might have changed, we do give it a differnt
802 // name than DestFile because otherwise the http method will
803 // send If-Range requests and there are too many broken servers
804 // out there that do not understand them
805 LastGoodSig = DestFile+".reverify";
806 Rename(Final,LastGoodSig);
807 }
808
809 QueueURI(Desc);
810 }
811 /*}}}*/
812 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
813 // ---------------------------------------------------------------------
814 /* The only header we use is the last-modified header. */
815 string pkgAcqMetaSig::Custom600Headers()
816 {
817 struct stat Buf;
818 if (stat(LastGoodSig.c_str(),&Buf) != 0)
819 return "\nIndex-File: true";
820
821 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
822 }
823
824 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
825 pkgAcquire::MethodConfig *Cfg)
826 {
827 Item::Done(Message,Size,MD5,Cfg);
828
829 string FileName = LookupTag(Message,"Filename");
830 if (FileName.empty() == true)
831 {
832 Status = StatError;
833 ErrorText = "Method gave a blank filename";
834 return;
835 }
836
837 if (FileName != DestFile)
838 {
839 // We have to copy it into place
840 Local = true;
841 Desc.URI = "copy:" + FileName;
842 QueueURI(Desc);
843 return;
844 }
845
846 Complete = true;
847
848 // put the last known good file back on i-m-s hit (it will
849 // be re-verified again)
850 // Else do nothing, we have the new file in DestFile then
851 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
852 Rename(LastGoodSig, DestFile);
853
854 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
855 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
856 DestFile, IndexTargets, MetaIndexParser);
857
858 }
859 /*}}}*/
860 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
861 {
862 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
863
864 // if we get a network error we fail gracefully
865 if(Status == StatTransientNetworkError)
866 {
867 Item::Failed(Message,Cnf);
868 // move the sigfile back on transient network failures
869 if(FileExists(DestFile))
870 Rename(LastGoodSig,Final);
871
872 // set the status back to , Item::Failed likes to reset it
873 Status = pkgAcquire::Item::StatTransientNetworkError;
874 return;
875 }
876
877 // Delete any existing sigfile when the acquire failed
878 unlink(Final.c_str());
879
880 // queue a pkgAcqMetaIndex with no sigfile
881 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
882 "", IndexTargets, MetaIndexParser);
883
884 if (Cnf->LocalOnly == true ||
885 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
886 {
887 // Ignore this
888 Status = StatDone;
889 Complete = false;
890 Dequeue();
891 return;
892 }
893
894 Item::Failed(Message,Cnf);
895 }
896 /*}}}*/
897 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
898 string URI,string URIDesc,string ShortDesc,
899 string SigFile,
900 const vector<struct IndexTarget*>* IndexTargets,
901 indexRecords* MetaIndexParser) :
902 Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
903 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
904 {
905 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
906 DestFile += URItoFileName(URI);
907
908 // Create the item
909 Desc.Description = URIDesc;
910 Desc.Owner = this;
911 Desc.ShortDesc = ShortDesc;
912 Desc.URI = URI;
913
914 QueueURI(Desc);
915 }
916 /*}}}*/
917 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
918 // ---------------------------------------------------------------------
919 /* The only header we use is the last-modified header. */
920 string pkgAcqMetaIndex::Custom600Headers()
921 {
922 string Final = _config->FindDir("Dir::State::lists");
923 Final += URItoFileName(RealURI);
924
925 struct stat Buf;
926 if (stat(Final.c_str(),&Buf) != 0)
927 return "\nIndex-File: true";
928
929 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
930 }
931 /*}}}*/
932 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, /*{{{*/
933 pkgAcquire::MethodConfig *Cfg)
934 {
935 Item::Done(Message,Size,Hash,Cfg);
936
937 // MetaIndexes are done in two passes: one to download the
938 // metaindex with an appropriate method, and a second to verify it
939 // with the gpgv method
940
941 if (AuthPass == true)
942 {
943 AuthDone(Message);
944 }
945 else
946 {
947 RetrievalDone(Message);
948 if (!Complete)
949 // Still more retrieving to do
950 return;
951
952 if (SigFile == "")
953 {
954 // There was no signature file, so we are finished. Download
955 // the indexes without verification.
956 QueueIndexes(false);
957 }
958 else
959 {
960 // There was a signature file, so pass it to gpgv for
961 // verification
962
963 if (_config->FindB("Debug::pkgAcquire::Auth", false))
964 std::cerr << "Metaindex acquired, queueing gpg verification ("
965 << SigFile << "," << DestFile << ")\n";
966 AuthPass = true;
967 Desc.URI = "gpgv:" + SigFile;
968 QueueURI(Desc);
969 Mode = "gpgv";
970 }
971 }
972 }
973 /*}}}*/
974 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
975 {
976 // We have just finished downloading a Release file (it is not
977 // verified yet)
978
979 string FileName = LookupTag(Message,"Filename");
980 if (FileName.empty() == true)
981 {
982 Status = StatError;
983 ErrorText = "Method gave a blank filename";
984 return;
985 }
986
987 if (FileName != DestFile)
988 {
989 Local = true;
990 Desc.URI = "copy:" + FileName;
991 QueueURI(Desc);
992 return;
993 }
994
995 // see if the download was a IMSHit
996 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
997 Complete = true;
998
999 string FinalFile = _config->FindDir("Dir::State::lists");
1000 FinalFile += URItoFileName(RealURI);
1001
1002 // If we get a IMS hit we can remove the empty file in partial
1003 // othersie we move the file in place
1004 if (IMSHit)
1005 unlink(DestFile.c_str());
1006 else
1007 Rename(DestFile,FinalFile);
1008
1009 chmod(FinalFile.c_str(),0644);
1010 DestFile = FinalFile;
1011 }
1012 /*}}}*/
1013 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1014 {
1015 // At this point, the gpgv method has succeeded, so there is a
1016 // valid signature from a key in the trusted keyring. We
1017 // perform additional verification of its contents, and use them
1018 // to verify the indexes we are about to download
1019
1020 if (!MetaIndexParser->Load(DestFile))
1021 {
1022 Status = StatAuthError;
1023 ErrorText = MetaIndexParser->ErrorText;
1024 return;
1025 }
1026
1027 if (!VerifyVendor(Message))
1028 {
1029 return;
1030 }
1031
1032 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1033 std::cerr << "Signature verification succeeded: "
1034 << DestFile << std::endl;
1035
1036 // Download further indexes with verification
1037 QueueIndexes(true);
1038
1039 // Done, move signature file into position
1040
1041 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1042 URItoFileName(RealURI) + ".gpg";
1043 Rename(SigFile,VerifiedSigFile);
1044 chmod(VerifiedSigFile.c_str(),0644);
1045 }
1046 /*}}}*/
1047 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1048 {
1049 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1050 Target != IndexTargets->end();
1051 Target++)
1052 {
1053 HashString ExpectedIndexHash;
1054 if (verify)
1055 {
1056 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1057 if (!Record)
1058 {
1059 Status = StatAuthError;
1060 ErrorText = "Unable to find expected entry "
1061 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
1062 return;
1063 }
1064 ExpectedIndexHash = Record->Hash;
1065 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1066 {
1067 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
1068 std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
1069 }
1070 if (ExpectedIndexHash.empty())
1071 {
1072 Status = StatAuthError;
1073 ErrorText = "Unable to find hash sum for "
1074 + (*Target)->MetaKey + " in Meta-index file";
1075 return;
1076 }
1077 }
1078
1079 // Queue Packages file (either diff or full packages files, depending
1080 // on the users option)
1081 if(_config->FindB("Acquire::PDiffs",true) == true)
1082 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
1083 (*Target)->ShortDesc, ExpectedIndexHash);
1084 else
1085 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
1086 (*Target)->ShortDesc, ExpectedIndexHash);
1087 }
1088 }
1089 /*}}}*/
1090 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1091 {
1092 // // Maybe this should be made available from above so we don't have
1093 // // to read and parse it every time?
1094 // pkgVendorList List;
1095 // List.ReadMainList();
1096
1097 // const Vendor* Vndr = NULL;
1098 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
1099 // {
1100 // string::size_type pos = (*I).find("VALIDSIG ");
1101 // if (_config->FindB("Debug::Vendor", false))
1102 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
1103 // << std::endl;
1104 // if (pos != std::string::npos)
1105 // {
1106 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
1107 // if (_config->FindB("Debug::Vendor", false))
1108 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
1109 // std::endl;
1110 // Vndr = List.FindVendor(Fingerprint) != "";
1111 // if (Vndr != NULL);
1112 // break;
1113 // }
1114 // }
1115 string::size_type pos;
1116
1117 // check for missing sigs (that where not fatal because otherwise we had
1118 // bombed earlier)
1119 string missingkeys;
1120 string msg = _("There is no public key available for the "
1121 "following key IDs:\n");
1122 pos = Message.find("NO_PUBKEY ");
1123 if (pos != std::string::npos)
1124 {
1125 string::size_type start = pos+strlen("NO_PUBKEY ");
1126 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1127 missingkeys += (Fingerprint);
1128 }
1129 if(!missingkeys.empty())
1130 _error->Warning("%s", string(msg+missingkeys).c_str());
1131
1132 string Transformed = MetaIndexParser->GetExpectedDist();
1133
1134 if (Transformed == "../project/experimental")
1135 {
1136 Transformed = "experimental";
1137 }
1138
1139 pos = Transformed.rfind('/');
1140 if (pos != string::npos)
1141 {
1142 Transformed = Transformed.substr(0, pos);
1143 }
1144
1145 if (Transformed == ".")
1146 {
1147 Transformed = "";
1148 }
1149
1150 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1151 {
1152 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1153 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1154 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1155 }
1156
1157 if (MetaIndexParser->CheckDist(Transformed) == false)
1158 {
1159 // This might become fatal one day
1160 // Status = StatAuthError;
1161 // ErrorText = "Conflicting distribution; expected "
1162 // + MetaIndexParser->GetExpectedDist() + " but got "
1163 // + MetaIndexParser->GetDist();
1164 // return false;
1165 if (!Transformed.empty())
1166 {
1167 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
1168 Desc.Description.c_str(),
1169 Transformed.c_str(),
1170 MetaIndexParser->GetDist().c_str());
1171 }
1172 }
1173
1174 return true;
1175 }
1176 /*}}}*/
1177 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1178 // ---------------------------------------------------------------------
1179 /* */
1180 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1181 {
1182 if (AuthPass == true)
1183 {
1184 // if we fail the authentication but got the file via a IMS-Hit
1185 // this means that the file wasn't downloaded and that it might be
1186 // just stale (server problem, proxy etc). we delete what we have
1187 // queue it again without i-m-s
1188 // alternatively we could just unlink the file and let the user try again
1189 if (IMSHit)
1190 {
1191 Complete = false;
1192 Local = false;
1193 AuthPass = false;
1194 unlink(DestFile.c_str());
1195
1196 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1197 DestFile += URItoFileName(RealURI);
1198 Desc.URI = RealURI;
1199 QueueURI(Desc);
1200 return;
1201 }
1202
1203 // gpgv method failed
1204 _error->Warning("GPG error: %s: %s",
1205 Desc.Description.c_str(),
1206 LookupTag(Message,"Message").c_str());
1207
1208 }
1209
1210 // No Release file was present, or verification failed, so fall
1211 // back to queueing Packages files without verification
1212 QueueIndexes(false);
1213 }
1214 /*}}}*/
1215 // AcqArchive::AcqArchive - Constructor /*{{{*/
1216 // ---------------------------------------------------------------------
1217 /* This just sets up the initial fetch environment and queues the first
1218 possibilitiy */
1219 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1220 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1221 string &StoreFilename) :
1222 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
1223 StoreFilename(StoreFilename), Vf(Version.FileList()),
1224 Trusted(false)
1225 {
1226 Retries = _config->FindI("Acquire::Retries",0);
1227
1228 if (Version.Arch() == 0)
1229 {
1230 _error->Error(_("I wasn't able to locate a file for the %s package. "
1231 "This might mean you need to manually fix this package. "
1232 "(due to missing arch)"),
1233 Version.ParentPkg().Name());
1234 return;
1235 }
1236
1237 /* We need to find a filename to determine the extension. We make the
1238 assumption here that all the available sources for this version share
1239 the same extension.. */
1240 // Skip not source sources, they do not have file fields.
1241 for (; Vf.end() == false; Vf++)
1242 {
1243 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1244 continue;
1245 break;
1246 }
1247
1248 // Does not really matter here.. we are going to fail out below
1249 if (Vf.end() != true)
1250 {
1251 // If this fails to get a file name we will bomb out below.
1252 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1253 if (_error->PendingError() == true)
1254 return;
1255
1256 // Generate the final file name as: package_version_arch.foo
1257 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1258 QuoteString(Version.VerStr(),"_:") + '_' +
1259 QuoteString(Version.Arch(),"_:.") +
1260 "." + flExtension(Parse.FileName());
1261 }
1262
1263 // check if we have one trusted source for the package. if so, switch
1264 // to "TrustedOnly" mode
1265 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
1266 {
1267 pkgIndexFile *Index;
1268 if (Sources->FindIndex(i.File(),Index) == false)
1269 continue;
1270 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1271 {
1272 std::cerr << "Checking index: " << Index->Describe()
1273 << "(Trusted=" << Index->IsTrusted() << ")\n";
1274 }
1275 if (Index->IsTrusted()) {
1276 Trusted = true;
1277 break;
1278 }
1279 }
1280
1281 // "allow-unauthenticated" restores apts old fetching behaviour
1282 // that means that e.g. unauthenticated file:// uris are higher
1283 // priority than authenticated http:// uris
1284 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
1285 Trusted = false;
1286
1287 // Select a source
1288 if (QueueNext() == false && _error->PendingError() == false)
1289 _error->Error(_("I wasn't able to locate file for the %s package. "
1290 "This might mean you need to manually fix this package."),
1291 Version.ParentPkg().Name());
1292 }
1293 /*}}}*/
1294 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1295 // ---------------------------------------------------------------------
1296 /* This queues the next available file version for download. It checks if
1297 the archive is already available in the cache and stashs the MD5 for
1298 checking later. */
1299 bool pkgAcqArchive::QueueNext()
1300 {
1301 for (; Vf.end() == false; Vf++)
1302 {
1303 // Ignore not source sources
1304 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1305 continue;
1306
1307 // Try to cross match against the source list
1308 pkgIndexFile *Index;
1309 if (Sources->FindIndex(Vf.File(),Index) == false)
1310 continue;
1311
1312 // only try to get a trusted package from another source if that source
1313 // is also trusted
1314 if(Trusted && !Index->IsTrusted())
1315 continue;
1316
1317 // Grab the text package record
1318 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1319 if (_error->PendingError() == true)
1320 return false;
1321
1322 string PkgFile = Parse.FileName();
1323 if(Parse.SHA256Hash() != "")
1324 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
1325 else if (Parse.SHA1Hash() != "")
1326 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
1327 else
1328 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1329 if (PkgFile.empty() == true)
1330 return _error->Error(_("The package index files are corrupted. No Filename: "
1331 "field for package %s."),
1332 Version.ParentPkg().Name());
1333
1334 Desc.URI = Index->ArchiveURI(PkgFile);
1335 Desc.Description = Index->ArchiveInfo(Version);
1336 Desc.Owner = this;
1337 Desc.ShortDesc = Version.ParentPkg().Name();
1338
1339 // See if we already have the file. (Legacy filenames)
1340 FileSize = Version->Size;
1341 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1342 struct stat Buf;
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 means it is
1356 an old style mismatched arch */
1357 unlink(FinalFile.c_str());
1358 }
1359
1360 // Check it again using the new style output filenames
1361 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1362 if (stat(FinalFile.c_str(),&Buf) == 0)
1363 {
1364 // Make sure the size matches
1365 if ((unsigned)Buf.st_size == Version->Size)
1366 {
1367 Complete = true;
1368 Local = true;
1369 Status = StatDone;
1370 StoreFilename = DestFile = FinalFile;
1371 return true;
1372 }
1373
1374 /* Hmm, we have a file and its size does not match, this shouldnt
1375 happen.. */
1376 unlink(FinalFile.c_str());
1377 }
1378
1379 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
1380
1381 // Check the destination file
1382 if (stat(DestFile.c_str(),&Buf) == 0)
1383 {
1384 // Hmm, the partial file is too big, erase it
1385 if ((unsigned)Buf.st_size > Version->Size)
1386 unlink(DestFile.c_str());
1387 else
1388 PartialSize = Buf.st_size;
1389 }
1390
1391 // Create the item
1392 Local = false;
1393 Desc.URI = Index->ArchiveURI(PkgFile);
1394 Desc.Description = Index->ArchiveInfo(Version);
1395 Desc.Owner = this;
1396 Desc.ShortDesc = Version.ParentPkg().Name();
1397 QueueURI(Desc);
1398
1399 Vf++;
1400 return true;
1401 }
1402 return false;
1403 }
1404 /*}}}*/
1405 // AcqArchive::Done - Finished fetching /*{{{*/
1406 // ---------------------------------------------------------------------
1407 /* */
1408 void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash,
1409 pkgAcquire::MethodConfig *Cfg)
1410 {
1411 Item::Done(Message,Size,CalcHash,Cfg);
1412
1413 // Check the size
1414 if (Size != Version->Size)
1415 {
1416 Status = StatError;
1417 ErrorText = _("Size mismatch");
1418 return;
1419 }
1420
1421 // Check the hash
1422 if(ExpectedHash.toStr() != CalcHash)
1423 {
1424 Status = StatError;
1425 ErrorText = _("Hash Sum mismatch");
1426 if(FileExists(DestFile))
1427 Rename(DestFile,DestFile + ".FAILED");
1428 return;
1429 }
1430
1431 // Grab the output filename
1432 string FileName = LookupTag(Message,"Filename");
1433 if (FileName.empty() == true)
1434 {
1435 Status = StatError;
1436 ErrorText = "Method gave a blank filename";
1437 return;
1438 }
1439
1440 Complete = true;
1441
1442 // Reference filename
1443 if (FileName != DestFile)
1444 {
1445 StoreFilename = DestFile = FileName;
1446 Local = true;
1447 return;
1448 }
1449
1450 // Done, move it into position
1451 string FinalFile = _config->FindDir("Dir::Cache::Archives");
1452 FinalFile += flNotDir(StoreFilename);
1453 Rename(DestFile,FinalFile);
1454
1455 StoreFilename = DestFile = FinalFile;
1456 Complete = true;
1457 }
1458 /*}}}*/
1459 // AcqArchive::Failed - Failure handler /*{{{*/
1460 // ---------------------------------------------------------------------
1461 /* Here we try other sources */
1462 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1463 {
1464 ErrorText = LookupTag(Message,"Message");
1465
1466 /* We don't really want to retry on failed media swaps, this prevents
1467 that. An interesting observation is that permanent failures are not
1468 recorded. */
1469 if (Cnf->Removable == true &&
1470 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1471 {
1472 // Vf = Version.FileList();
1473 while (Vf.end() == false) Vf++;
1474 StoreFilename = string();
1475 Item::Failed(Message,Cnf);
1476 return;
1477 }
1478
1479 if (QueueNext() == false)
1480 {
1481 // This is the retry counter
1482 if (Retries != 0 &&
1483 Cnf->LocalOnly == false &&
1484 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1485 {
1486 Retries--;
1487 Vf = Version.FileList();
1488 if (QueueNext() == true)
1489 return;
1490 }
1491
1492 StoreFilename = string();
1493 Item::Failed(Message,Cnf);
1494 }
1495 }
1496 /*}}}*/
1497 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
1498 // ---------------------------------------------------------------------
1499 bool pkgAcqArchive::IsTrusted()
1500 {
1501 return Trusted;
1502 }
1503 /*}}}*/
1504 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1505 // ---------------------------------------------------------------------
1506 /* */
1507 void pkgAcqArchive::Finished()
1508 {
1509 if (Status == pkgAcquire::Item::StatDone &&
1510 Complete == true)
1511 return;
1512 StoreFilename = string();
1513 }
1514 /*}}}*/
1515 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1516 // ---------------------------------------------------------------------
1517 /* The file is added to the queue */
1518 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
1519 unsigned long Size,string Dsc,string ShortDesc,
1520 const string &DestDir, const string &DestFilename) :
1521 Item(Owner), ExpectedHash(Hash)
1522 {
1523 Retries = _config->FindI("Acquire::Retries",0);
1524
1525 if(!DestFilename.empty())
1526 DestFile = DestFilename;
1527 else if(!DestDir.empty())
1528 DestFile = DestDir + "/" + flNotDir(URI);
1529 else
1530 DestFile = flNotDir(URI);
1531
1532 // Create the item
1533 Desc.URI = URI;
1534 Desc.Description = Dsc;
1535 Desc.Owner = this;
1536
1537 // Set the short description to the archive component
1538 Desc.ShortDesc = ShortDesc;
1539
1540 // Get the transfer sizes
1541 FileSize = Size;
1542 struct stat Buf;
1543 if (stat(DestFile.c_str(),&Buf) == 0)
1544 {
1545 // Hmm, the partial file is too big, erase it
1546 if ((unsigned)Buf.st_size > Size)
1547 unlink(DestFile.c_str());
1548 else
1549 PartialSize = Buf.st_size;
1550 }
1551
1552 QueueURI(Desc);
1553 }
1554 /*}}}*/
1555 // AcqFile::Done - Item downloaded OK /*{{{*/
1556 // ---------------------------------------------------------------------
1557 /* */
1558 void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash,
1559 pkgAcquire::MethodConfig *Cnf)
1560 {
1561 Item::Done(Message,Size,CalcHash,Cnf);
1562
1563 // Check the hash
1564 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
1565 {
1566 Status = StatError;
1567 ErrorText = "Hash Sum mismatch";
1568 Rename(DestFile,DestFile + ".FAILED");
1569 return;
1570 }
1571
1572 string FileName = LookupTag(Message,"Filename");
1573 if (FileName.empty() == true)
1574 {
1575 Status = StatError;
1576 ErrorText = "Method gave a blank filename";
1577 return;
1578 }
1579
1580 Complete = true;
1581
1582 // The files timestamp matches
1583 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1584 return;
1585
1586 // We have to copy it into place
1587 if (FileName != DestFile)
1588 {
1589 Local = true;
1590 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1591 Cnf->Removable == true)
1592 {
1593 Desc.URI = "copy:" + FileName;
1594 QueueURI(Desc);
1595 return;
1596 }
1597
1598 // Erase the file if it is a symlink so we can overwrite it
1599 struct stat St;
1600 if (lstat(DestFile.c_str(),&St) == 0)
1601 {
1602 if (S_ISLNK(St.st_mode) != 0)
1603 unlink(DestFile.c_str());
1604 }
1605
1606 // Symlink the file
1607 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1608 {
1609 ErrorText = "Link to " + DestFile + " failure ";
1610 Status = StatError;
1611 Complete = false;
1612 }
1613 }
1614 }
1615 /*}}}*/
1616 // AcqFile::Failed - Failure handler /*{{{*/
1617 // ---------------------------------------------------------------------
1618 /* Here we try other sources */
1619 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1620 {
1621 ErrorText = LookupTag(Message,"Message");
1622
1623 // This is the retry counter
1624 if (Retries != 0 &&
1625 Cnf->LocalOnly == false &&
1626 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1627 {
1628 Retries--;
1629 QueueURI(Desc);
1630 return;
1631 }
1632
1633 Item::Failed(Message,Cnf);
1634 }
1635 /*}}}*/