merge lp:~mvo/apt/sha512-template to add support for sha512
[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/aptconfiguration.h>
19 #include <apt-pkg/sourcelist.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 #include <ctime>
36 /*}}}*/
37
38 using namespace std;
39
40 // Acquire::Item::Item - Constructor /*{{{*/
41 // ---------------------------------------------------------------------
42 /* */
43 pkgAcquire::Item::Item(pkgAcquire *Owner) : Owner(Owner), FileSize(0),
44 PartialSize(0), Mode(0), ID(0), Complete(false),
45 Local(false), QueueCounter(0)
46 {
47 Owner->Add(this);
48 Status = StatIdle;
49 }
50 /*}}}*/
51 // Acquire::Item::~Item - Destructor /*{{{*/
52 // ---------------------------------------------------------------------
53 /* */
54 pkgAcquire::Item::~Item()
55 {
56 Owner->Remove(this);
57 }
58 /*}}}*/
59 // Acquire::Item::Failed - Item failed to download /*{{{*/
60 // ---------------------------------------------------------------------
61 /* We return to an idle state if there are still other queues that could
62 fetch this object */
63 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
64 {
65 Status = StatIdle;
66 ErrorText = LookupTag(Message,"Message");
67 UsedMirror = LookupTag(Message,"UsedMirror");
68 if (QueueCounter <= 1)
69 {
70 /* This indicates that the file is not available right now but might
71 be sometime later. If we do a retry cycle then this should be
72 retried [CDROMs] */
73 if (Cnf->LocalOnly == true &&
74 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
75 {
76 Status = StatIdle;
77 Dequeue();
78 return;
79 }
80
81 Status = StatError;
82 Dequeue();
83 }
84
85 // report mirror failure back to LP if we actually use a mirror
86 string FailReason = LookupTag(Message, "FailReason");
87 if(FailReason.size() != 0)
88 ReportMirrorFailure(FailReason);
89 else
90 ReportMirrorFailure(ErrorText);
91 }
92 /*}}}*/
93 // Acquire::Item::Start - Item has begun to download /*{{{*/
94 // ---------------------------------------------------------------------
95 /* Stash status and the file size. Note that setting Complete means
96 sub-phases of the acquire process such as decompresion are operating */
97 void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
98 {
99 Status = StatFetching;
100 if (FileSize == 0 && Complete == false)
101 FileSize = Size;
102 }
103 /*}}}*/
104 // Acquire::Item::Done - Item downloaded OK /*{{{*/
105 // ---------------------------------------------------------------------
106 /* */
107 void pkgAcquire::Item::Done(string Message,unsigned long Size,string Hash,
108 pkgAcquire::MethodConfig *Cnf)
109 {
110 // We just downloaded something..
111 string FileName = LookupTag(Message,"Filename");
112 UsedMirror = LookupTag(Message,"UsedMirror");
113 if (Complete == false && !Local && FileName == DestFile)
114 {
115 if (Owner->Log != 0)
116 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
117 }
118
119 if (FileSize == 0)
120 FileSize= Size;
121 Status = StatDone;
122 ErrorText = string();
123 Owner->Dequeue(this);
124 }
125 /*}}}*/
126 // Acquire::Item::Rename - Rename a file /*{{{*/
127 // ---------------------------------------------------------------------
128 /* This helper function is used by alot of item methods as thier final
129 step */
130 void pkgAcquire::Item::Rename(string From,string To)
131 {
132 if (rename(From.c_str(),To.c_str()) != 0)
133 {
134 char S[300];
135 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
136 From.c_str(),To.c_str());
137 Status = StatError;
138 ErrorText = S;
139 }
140 }
141 /*}}}*/
142 // Acquire::Item::ReportMirrorFailure /*{{{*/
143 // ---------------------------------------------------------------------
144 void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
145 {
146 // we only act if a mirror was used at all
147 if(UsedMirror.empty())
148 return;
149 #if 0
150 std::cerr << "\nReportMirrorFailure: "
151 << UsedMirror
152 << " Uri: " << DescURI()
153 << " FailCode: "
154 << FailCode << std::endl;
155 #endif
156 const char *Args[40];
157 unsigned int i = 0;
158 string report = _config->Find("Methods::Mirror::ProblemReporting",
159 "/usr/lib/apt/apt-report-mirror-failure");
160 if(!FileExists(report))
161 return;
162 Args[i++] = report.c_str();
163 Args[i++] = UsedMirror.c_str();
164 Args[i++] = DescURI().c_str();
165 Args[i++] = FailCode.c_str();
166 Args[i++] = NULL;
167 pid_t pid = ExecFork();
168 if(pid < 0)
169 {
170 _error->Error("ReportMirrorFailure Fork failed");
171 return;
172 }
173 else if(pid == 0)
174 {
175 execvp(Args[0], (char**)Args);
176 std::cerr << "Could not exec " << Args[0] << std::endl;
177 _exit(100);
178 }
179 if(!ExecWait(pid, "report-mirror-failure"))
180 {
181 _error->Warning("Couldn't report problem to '%s'",
182 _config->Find("Methods::Mirror::ProblemReporting").c_str());
183 }
184 }
185 /*}}}*/
186 // AcqSubIndex::AcqSubIndex - Constructor /*{{{*/
187 // ---------------------------------------------------------------------
188 /* Get the Index file first and see if there are languages available
189 * If so, create a pkgAcqIndexTrans for the found language(s).
190 */
191 pkgAcqSubIndex::pkgAcqSubIndex(pkgAcquire *Owner, string const &URI,
192 string const &URIDesc, string const &ShortDesc,
193 HashString const &ExpectedHash)
194 : Item(Owner), ExpectedHash(ExpectedHash)
195 {
196 Debug = _config->FindB("Debug::pkgAcquire::SubIndex",false);
197
198 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
199 DestFile += URItoFileName(URI);
200
201 Desc.URI = URI;
202 Desc.Description = URIDesc;
203 Desc.Owner = this;
204 Desc.ShortDesc = ShortDesc;
205
206 QueueURI(Desc);
207
208 if(Debug)
209 std::clog << "pkgAcqSubIndex: " << Desc.URI << std::endl;
210 }
211 /*}}}*/
212 // AcqSubIndex::Custom600Headers - Insert custom request headers /*{{{*/
213 // ---------------------------------------------------------------------
214 /* The only header we use is the last-modified header. */
215 string pkgAcqSubIndex::Custom600Headers()
216 {
217 string Final = _config->FindDir("Dir::State::lists");
218 Final += URItoFileName(Desc.URI);
219
220 struct stat Buf;
221 if (stat(Final.c_str(),&Buf) != 0)
222 return "\nIndex-File: true\nFail-Ignore: true\n";
223 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
224 }
225 /*}}}*/
226 void pkgAcqSubIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
227 {
228 if(Debug)
229 std::clog << "pkgAcqSubIndex failed: " << Desc.URI << std::endl;
230
231 Complete = false;
232 Status = StatDone;
233 Dequeue();
234
235 // No good Index is provided, so try guessing
236 std::vector<std::string> langs = APT::Configuration::getLanguages(true);
237 for (std::vector<std::string>::const_iterator l = langs.begin();
238 l != langs.end(); ++l)
239 {
240 if (*l == "none") continue;
241 string const file = "Translation-" + *l;
242 new pkgAcqIndexTrans(Owner, Desc.URI.substr(0, Desc.URI.rfind('/')+1).append(file),
243 Desc.Description.erase(Desc.Description.rfind(' ')+1).append(file),
244 file);
245 }
246 }
247 /*}}}*/
248 void pkgAcqSubIndex::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
249 pkgAcquire::MethodConfig *Cnf)
250 {
251 if(Debug)
252 std::clog << "pkgAcqSubIndex::Done(): " << Desc.URI << std::endl;
253
254 string FileName = LookupTag(Message,"Filename");
255 if (FileName.empty() == true)
256 {
257 Status = StatError;
258 ErrorText = "Method gave a blank filename";
259 return;
260 }
261
262 if (FileName != DestFile)
263 {
264 Local = true;
265 Desc.URI = "copy:" + FileName;
266 QueueURI(Desc);
267 return;
268 }
269
270 Item::Done(Message,Size,Md5Hash,Cnf);
271
272 string FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(Desc.URI);
273
274 // sucess in downloading the index
275 // rename the index
276 if(Debug)
277 std::clog << "Renaming: " << DestFile << " -> " << FinalFile << std::endl;
278 Rename(DestFile,FinalFile);
279 chmod(FinalFile.c_str(),0644);
280 DestFile = FinalFile;
281
282 if(ParseIndex(DestFile) == false)
283 return Failed("", NULL);
284
285 Complete = true;
286 Status = StatDone;
287 Dequeue();
288 return;
289 }
290 /*}}}*/
291 bool pkgAcqSubIndex::ParseIndex(string const &IndexFile) /*{{{*/
292 {
293 indexRecords SubIndexParser;
294 if (FileExists(IndexFile) == false || SubIndexParser.Load(IndexFile) == false)
295 return false;
296
297 std::vector<std::string> lang = APT::Configuration::getLanguages(true);
298 for (std::vector<std::string>::const_iterator l = lang.begin();
299 l != lang.end(); ++l)
300 {
301 if (*l == "none")
302 continue;
303
304 string file = "Translation-" + *l;
305 indexRecords::checkSum const *Record = SubIndexParser.Lookup(file);
306 HashString expected;
307 if (Record == NULL)
308 {
309 // FIXME: the Index file provided by debian currently only includes bz2 records
310 Record = SubIndexParser.Lookup(file + ".bz2");
311 if (Record == NULL)
312 continue;
313 }
314 else
315 {
316 expected = Record->Hash;
317 if (expected.empty() == true)
318 continue;
319 }
320
321 IndexTarget target;
322 target.Description = Desc.Description.erase(Desc.Description.rfind(' ')+1).append(file);
323 target.MetaKey = file;
324 target.ShortDesc = file;
325 target.URI = Desc.URI.substr(0, Desc.URI.rfind('/')+1).append(file);
326 new pkgAcqIndexTrans(Owner, &target, expected, &SubIndexParser);
327 }
328 return true;
329 }
330 /*}}}*/
331 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
332 // ---------------------------------------------------------------------
333 /* Get the DiffIndex file first and see if there are patches availabe
334 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
335 * patches. If anything goes wrong in that process, it will fall back to
336 * the original packages file
337 */
338 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
339 string URI,string URIDesc,string ShortDesc,
340 HashString ExpectedHash)
341 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
342 Description(URIDesc)
343 {
344
345 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
346
347 Desc.Description = URIDesc + "/DiffIndex";
348 Desc.Owner = this;
349 Desc.ShortDesc = ShortDesc;
350 Desc.URI = URI + ".diff/Index";
351
352 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
353 DestFile += URItoFileName(URI) + string(".DiffIndex");
354
355 if(Debug)
356 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
357
358 // look for the current package file
359 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
360 CurrentPackagesFile += URItoFileName(RealURI);
361
362 // FIXME: this file:/ check is a hack to prevent fetching
363 // from local sources. this is really silly, and
364 // should be fixed cleanly as soon as possible
365 if(!FileExists(CurrentPackagesFile) ||
366 Desc.URI.substr(0,strlen("file:/")) == "file:/")
367 {
368 // we don't have a pkg file or we don't want to queue
369 if(Debug)
370 std::clog << "No index file, local or canceld by user" << std::endl;
371 Failed("", NULL);
372 return;
373 }
374
375 if(Debug)
376 std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
377 << CurrentPackagesFile << std::endl;
378
379 QueueURI(Desc);
380
381 }
382 /*}}}*/
383 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
384 // ---------------------------------------------------------------------
385 /* The only header we use is the last-modified header. */
386 string pkgAcqDiffIndex::Custom600Headers()
387 {
388 string Final = _config->FindDir("Dir::State::lists");
389 Final += URItoFileName(RealURI) + string(".IndexDiff");
390
391 if(Debug)
392 std::clog << "Custom600Header-IMS: " << Final << std::endl;
393
394 struct stat Buf;
395 if (stat(Final.c_str(),&Buf) != 0)
396 return "\nIndex-File: true";
397
398 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
399 }
400 /*}}}*/
401 bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
402 {
403 if(Debug)
404 std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
405 << std::endl;
406
407 pkgTagSection Tags;
408 string ServerSha1;
409 vector<DiffInfo> available_patches;
410
411 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
412 pkgTagFile TF(&Fd);
413 if (_error->PendingError() == true)
414 return false;
415
416 if(TF.Step(Tags) == true)
417 {
418 bool found = false;
419 DiffInfo d;
420 string size;
421
422 string const tmp = Tags.FindS("SHA1-Current");
423 std::stringstream ss(tmp);
424 ss >> ServerSha1 >> size;
425 unsigned long const ServerSize = atol(size.c_str());
426
427 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
428 SHA1Summation SHA1;
429 SHA1.AddFD(fd.Fd(), fd.Size());
430 string const local_sha1 = SHA1.Result();
431
432 if(local_sha1 == ServerSha1)
433 {
434 // we have the same sha1 as the server
435 if(Debug)
436 std::clog << "Package file is up-to-date" << std::endl;
437 // set found to true, this will queue a pkgAcqIndexDiffs with
438 // a empty availabe_patches
439 found = true;
440 }
441 else
442 {
443 if(Debug)
444 std::clog << "SHA1-Current: " << ServerSha1 << " and we start at "<< fd.Name() << " " << fd.Size() << " " << local_sha1 << std::endl;
445
446 // check the historie and see what patches we need
447 string const history = Tags.FindS("SHA1-History");
448 std::stringstream hist(history);
449 while(hist >> d.sha1 >> size >> d.file)
450 {
451 // read until the first match is found
452 // from that point on, we probably need all diffs
453 if(d.sha1 == local_sha1)
454 found=true;
455 else if (found == false)
456 continue;
457
458 if(Debug)
459 std::clog << "Need to get diff: " << d.file << std::endl;
460 available_patches.push_back(d);
461 }
462
463 if (available_patches.empty() == false)
464 {
465 // patching with too many files is rather slow compared to a fast download
466 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
467 if (fileLimit != 0 && fileLimit < available_patches.size())
468 {
469 if (Debug)
470 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
471 << ") so fallback to complete download" << std::endl;
472 return false;
473 }
474
475 // see if the patches are too big
476 found = false; // it was true and it will be true again at the end
477 d = *available_patches.begin();
478 string const firstPatch = d.file;
479 unsigned long patchesSize = 0;
480 std::stringstream patches(Tags.FindS("SHA1-Patches"));
481 while(patches >> d.sha1 >> size >> d.file)
482 {
483 if (firstPatch == d.file)
484 found = true;
485 else if (found == false)
486 continue;
487
488 patchesSize += atol(size.c_str());
489 }
490 unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
491 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
492 {
493 if (Debug)
494 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
495 << ") so fallback to complete download" << std::endl;
496 return false;
497 }
498 }
499 }
500
501 // we have something, queue the next diff
502 if(found)
503 {
504 // queue the diffs
505 string::size_type const last_space = Description.rfind(" ");
506 if(last_space != string::npos)
507 Description.erase(last_space, Description.size()-last_space);
508 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
509 ExpectedHash, ServerSha1, available_patches);
510 Complete = false;
511 Status = StatDone;
512 Dequeue();
513 return true;
514 }
515 }
516
517 // Nothing found, report and return false
518 // Failing here is ok, if we return false later, the full
519 // IndexFile is queued
520 if(Debug)
521 std::clog << "Can't find a patch in the index file" << std::endl;
522 return false;
523 }
524 /*}}}*/
525 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
526 {
527 if(Debug)
528 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl
529 << "Falling back to normal index file aquire" << std::endl;
530
531 new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc,
532 ExpectedHash);
533
534 Complete = false;
535 Status = StatDone;
536 Dequeue();
537 }
538 /*}}}*/
539 void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
540 pkgAcquire::MethodConfig *Cnf)
541 {
542 if(Debug)
543 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
544
545 Item::Done(Message,Size,Md5Hash,Cnf);
546
547 string FinalFile;
548 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
549
550 // sucess in downloading the index
551 // rename the index
552 FinalFile += string(".IndexDiff");
553 if(Debug)
554 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
555 << std::endl;
556 Rename(DestFile,FinalFile);
557 chmod(FinalFile.c_str(),0644);
558 DestFile = FinalFile;
559
560 if(!ParseDiffIndex(DestFile))
561 return Failed("", NULL);
562
563 Complete = true;
564 Status = StatDone;
565 Dequeue();
566 return;
567 }
568 /*}}}*/
569 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
570 // ---------------------------------------------------------------------
571 /* The package diff is added to the queue. one object is constructed
572 * for each diff and the index
573 */
574 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
575 string URI,string URIDesc,string ShortDesc,
576 HashString ExpectedHash,
577 string ServerSha1,
578 vector<DiffInfo> diffs)
579 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
580 available_patches(diffs), ServerSha1(ServerSha1)
581 {
582
583 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
584 DestFile += URItoFileName(URI);
585
586 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
587
588 Description = URIDesc;
589 Desc.Owner = this;
590 Desc.ShortDesc = ShortDesc;
591
592 if(available_patches.size() == 0)
593 {
594 // we are done (yeah!)
595 Finish(true);
596 }
597 else
598 {
599 // get the next diff
600 State = StateFetchDiff;
601 QueueNextDiff();
602 }
603 }
604 /*}}}*/
605 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
606 {
607 if(Debug)
608 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl
609 << "Falling back to normal index file aquire" << std::endl;
610 new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
611 ExpectedHash);
612 Finish();
613 }
614 /*}}}*/
615 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
616 void pkgAcqIndexDiffs::Finish(bool allDone)
617 {
618 // we restore the original name, this is required, otherwise
619 // the file will be cleaned
620 if(allDone)
621 {
622 DestFile = _config->FindDir("Dir::State::lists");
623 DestFile += URItoFileName(RealURI);
624
625 if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
626 {
627 Status = StatAuthError;
628 ErrorText = _("MD5Sum mismatch");
629 Rename(DestFile,DestFile + ".FAILED");
630 Dequeue();
631 return;
632 }
633
634 // this is for the "real" finish
635 Complete = true;
636 Status = StatDone;
637 Dequeue();
638 if(Debug)
639 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
640 return;
641 }
642
643 if(Debug)
644 std::clog << "Finishing: " << Desc.URI << std::endl;
645 Complete = false;
646 Status = StatDone;
647 Dequeue();
648 return;
649 }
650 /*}}}*/
651 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
652 {
653
654 // calc sha1 of the just patched file
655 string FinalFile = _config->FindDir("Dir::State::lists");
656 FinalFile += URItoFileName(RealURI);
657
658 FileFd fd(FinalFile, FileFd::ReadOnly);
659 SHA1Summation SHA1;
660 SHA1.AddFD(fd.Fd(), fd.Size());
661 string local_sha1 = string(SHA1.Result());
662 if(Debug)
663 std::clog << "QueueNextDiff: "
664 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
665
666 // final file reached before all patches are applied
667 if(local_sha1 == ServerSha1)
668 {
669 Finish(true);
670 return true;
671 }
672
673 // remove all patches until the next matching patch is found
674 // this requires the Index file to be ordered
675 for(vector<DiffInfo>::iterator I=available_patches.begin();
676 available_patches.size() > 0 &&
677 I != available_patches.end() &&
678 (*I).sha1 != local_sha1;
679 I++)
680 {
681 available_patches.erase(I);
682 }
683
684 // error checking and falling back if no patch was found
685 if(available_patches.size() == 0)
686 {
687 Failed("", NULL);
688 return false;
689 }
690
691 // queue the right diff
692 Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz";
693 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
694 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
695 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
696
697 if(Debug)
698 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
699
700 QueueURI(Desc);
701
702 return true;
703 }
704 /*}}}*/
705 void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
706 pkgAcquire::MethodConfig *Cnf)
707 {
708 if(Debug)
709 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
710
711 Item::Done(Message,Size,Md5Hash,Cnf);
712
713 string FinalFile;
714 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
715
716 // sucess in downloading a diff, enter ApplyDiff state
717 if(State == StateFetchDiff)
718 {
719
720 // rred excepts the patch as $FinalFile.ed
721 Rename(DestFile,FinalFile+".ed");
722
723 if(Debug)
724 std::clog << "Sending to rred method: " << FinalFile << std::endl;
725
726 State = StateApplyDiff;
727 Local = true;
728 Desc.URI = "rred:" + FinalFile;
729 QueueURI(Desc);
730 Mode = "rred";
731 return;
732 }
733
734
735 // success in download/apply a diff, queue next (if needed)
736 if(State == StateApplyDiff)
737 {
738 // remove the just applied patch
739 available_patches.erase(available_patches.begin());
740
741 // move into place
742 if(Debug)
743 {
744 std::clog << "Moving patched file in place: " << std::endl
745 << DestFile << " -> " << FinalFile << std::endl;
746 }
747 Rename(DestFile,FinalFile);
748 chmod(FinalFile.c_str(),0644);
749
750 // see if there is more to download
751 if(available_patches.size() > 0) {
752 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
753 ExpectedHash, ServerSha1, available_patches);
754 return Finish();
755 } else
756 return Finish(true);
757 }
758 }
759 /*}}}*/
760 // AcqIndex::AcqIndex - Constructor /*{{{*/
761 // ---------------------------------------------------------------------
762 /* The package file is added to the queue and a second class is
763 instantiated to fetch the revision file */
764 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
765 string URI,string URIDesc,string ShortDesc,
766 HashString ExpectedHash, string comprExt)
767 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash)
768 {
769 if(comprExt.empty() == true)
770 {
771 // autoselect the compression method
772 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
773 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
774 comprExt.append(*t).append(" ");
775 if (comprExt.empty() == false)
776 comprExt.erase(comprExt.end()-1);
777 }
778 CompressionExtension = comprExt;
779
780 Init(URI, URIDesc, ShortDesc);
781 }
782 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner, IndexTarget const *Target,
783 HashString const &ExpectedHash, indexRecords const *MetaIndexParser)
784 : Item(Owner), RealURI(Target->URI), ExpectedHash(ExpectedHash)
785 {
786 // autoselect the compression method
787 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
788 CompressionExtension = "";
789 if (ExpectedHash.empty() == false)
790 {
791 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
792 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
793 CompressionExtension.append(*t).append(" ");
794 }
795 else
796 {
797 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
798 CompressionExtension.append(*t).append(" ");
799 }
800 if (CompressionExtension.empty() == false)
801 CompressionExtension.erase(CompressionExtension.end()-1);
802
803 Init(Target->URI, Target->Description, Target->ShortDesc);
804 }
805 /*}}}*/
806 // AcqIndex::Init - defered Constructor /*{{{*/
807 void pkgAcqIndex::Init(string const &URI, string const &URIDesc, string const &ShortDesc) {
808 Decompression = false;
809 Erase = false;
810
811 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
812 DestFile += URItoFileName(URI);
813
814 std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
815 if (comprExt == "uncompressed")
816 Desc.URI = URI;
817 else
818 Desc.URI = URI + '.' + comprExt;
819
820 Desc.Description = URIDesc;
821 Desc.Owner = this;
822 Desc.ShortDesc = ShortDesc;
823
824 QueueURI(Desc);
825 }
826 /*}}}*/
827 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
828 // ---------------------------------------------------------------------
829 /* The only header we use is the last-modified header. */
830 string pkgAcqIndex::Custom600Headers()
831 {
832 string Final = _config->FindDir("Dir::State::lists");
833 Final += URItoFileName(RealURI);
834 if (_config->FindB("Acquire::GzipIndexes",false))
835 Final += ".gz";
836
837 string msg = "\nIndex-File: true";
838 // FIXME: this really should use "IndexTarget::IsOptional()" but that
839 // seems to be difficult without breaking ABI
840 if (ShortDesc().find("Translation") != 0)
841 msg += "\nFail-Ignore: true";
842 struct stat Buf;
843 if (stat(Final.c_str(),&Buf) == 0)
844 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
845
846 return msg;
847 }
848 /*}}}*/
849 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
850 {
851 size_t const nextExt = CompressionExtension.find(' ');
852 if (nextExt != std::string::npos)
853 {
854 CompressionExtension = CompressionExtension.substr(nextExt+1);
855 Init(RealURI, Desc.Description, Desc.ShortDesc);
856 return;
857 }
858
859 // on decompression failure, remove bad versions in partial/
860 if (Decompression && Erase) {
861 string s = _config->FindDir("Dir::State::lists") + "partial/";
862 s.append(URItoFileName(RealURI));
863 unlink(s.c_str());
864 }
865
866 Item::Failed(Message,Cnf);
867 }
868 /*}}}*/
869 // AcqIndex::Done - Finished a fetch /*{{{*/
870 // ---------------------------------------------------------------------
871 /* This goes through a number of states.. On the initial fetch the
872 method could possibly return an alternate filename which points
873 to the uncompressed version of the file. If this is so the file
874 is copied into the partial directory. In all other cases the file
875 is decompressed with a gzip uri. */
876 void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash,
877 pkgAcquire::MethodConfig *Cfg)
878 {
879 Item::Done(Message,Size,Hash,Cfg);
880
881 if (Decompression == true)
882 {
883 if (_config->FindB("Debug::pkgAcquire::Auth", false))
884 {
885 std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
886 std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
887 }
888
889 if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
890 {
891 Status = StatAuthError;
892 ErrorText = _("Hash Sum mismatch");
893 Rename(DestFile,DestFile + ".FAILED");
894 ReportMirrorFailure("HashChecksumFailure");
895 return;
896 }
897 // Done, move it into position
898 string FinalFile = _config->FindDir("Dir::State::lists");
899 FinalFile += URItoFileName(RealURI);
900 Rename(DestFile,FinalFile);
901 chmod(FinalFile.c_str(),0644);
902
903 /* We restore the original name to DestFile so that the clean operation
904 will work OK */
905 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
906 DestFile += URItoFileName(RealURI);
907
908 // Remove the compressed version.
909 if (Erase == true)
910 unlink(DestFile.c_str());
911 return;
912 }
913
914 Erase = false;
915 Complete = true;
916
917 // Handle the unzipd case
918 string FileName = LookupTag(Message,"Alt-Filename");
919 if (FileName.empty() == false)
920 {
921 // The files timestamp matches
922 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
923 return;
924 Decompression = true;
925 Local = true;
926 DestFile += ".decomp";
927 Desc.URI = "copy:" + FileName;
928 QueueURI(Desc);
929 Mode = "copy";
930 return;
931 }
932
933 FileName = LookupTag(Message,"Filename");
934 if (FileName.empty() == true)
935 {
936 Status = StatError;
937 ErrorText = "Method gave a blank filename";
938 }
939
940 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
941
942 // The files timestamp matches
943 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
944 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
945 // Update DestFile for .gz suffix so that the clean operation keeps it
946 DestFile += ".gz";
947 return;
948 }
949
950 if (FileName == DestFile)
951 Erase = true;
952 else
953 Local = true;
954
955 string decompProg;
956
957 // If we enable compressed indexes and already have gzip, keep it
958 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
959 string FinalFile = _config->FindDir("Dir::State::lists");
960 FinalFile += URItoFileName(RealURI) + ".gz";
961 Rename(DestFile,FinalFile);
962 chmod(FinalFile.c_str(),0644);
963
964 // Update DestFile for .gz suffix so that the clean operation keeps it
965 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
966 DestFile += URItoFileName(RealURI) + ".gz";
967 return;
968 }
969
970 // get the binary name for your used compression type
971 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
972 if(decompProg.empty() == false);
973 else if(compExt == "uncompressed")
974 decompProg = "copy";
975 else {
976 _error->Error("Unsupported extension: %s", compExt.c_str());
977 return;
978 }
979
980 Decompression = true;
981 DestFile += ".decomp";
982 Desc.URI = decompProg + ":" + FileName;
983 QueueURI(Desc);
984 Mode = decompProg.c_str();
985 }
986 /*}}}*/
987 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
988 // ---------------------------------------------------------------------
989 /* The Translation file is added to the queue */
990 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
991 string URI,string URIDesc,string ShortDesc)
992 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
993 {
994 }
995 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner, IndexTarget const *Target,
996 HashString const &ExpectedHash, indexRecords const *MetaIndexParser)
997 : pkgAcqIndex(Owner, Target, ExpectedHash, MetaIndexParser)
998 {
999 }
1000 /*}}}*/
1001 // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1002 // ---------------------------------------------------------------------
1003 string pkgAcqIndexTrans::Custom600Headers()
1004 {
1005 string Final = _config->FindDir("Dir::State::lists");
1006 Final += URItoFileName(RealURI);
1007
1008 struct stat Buf;
1009 if (stat(Final.c_str(),&Buf) != 0)
1010 return "\nFail-Ignore: true\nIndex-File: true";
1011 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1012 }
1013 /*}}}*/
1014 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1015 // ---------------------------------------------------------------------
1016 /* */
1017 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1018 {
1019 size_t const nextExt = CompressionExtension.find(' ');
1020 if (nextExt != std::string::npos)
1021 {
1022 CompressionExtension = CompressionExtension.substr(nextExt+1);
1023 Init(RealURI, Desc.Description, Desc.ShortDesc);
1024 Status = StatIdle;
1025 return;
1026 }
1027
1028 if (Cnf->LocalOnly == true ||
1029 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1030 {
1031 // Ignore this
1032 Status = StatDone;
1033 Complete = false;
1034 Dequeue();
1035 return;
1036 }
1037
1038 Item::Failed(Message,Cnf);
1039 }
1040 /*}}}*/
1041 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
1042 string URI,string URIDesc,string ShortDesc,
1043 string MetaIndexURI, string MetaIndexURIDesc,
1044 string MetaIndexShortDesc,
1045 const vector<IndexTarget*>* IndexTargets,
1046 indexRecords* MetaIndexParser) :
1047 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
1048 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1049 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
1050 {
1051 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1052 DestFile += URItoFileName(URI);
1053
1054 // remove any partial downloaded sig-file in partial/.
1055 // it may confuse proxies and is too small to warrant a
1056 // partial download anyway
1057 unlink(DestFile.c_str());
1058
1059 // Create the item
1060 Desc.Description = URIDesc;
1061 Desc.Owner = this;
1062 Desc.ShortDesc = ShortDesc;
1063 Desc.URI = URI;
1064
1065 string Final = _config->FindDir("Dir::State::lists");
1066 Final += URItoFileName(RealURI);
1067 struct stat Buf;
1068 if (stat(Final.c_str(),&Buf) == 0)
1069 {
1070 // File was already in place. It needs to be re-downloaded/verified
1071 // because Release might have changed, we do give it a differnt
1072 // name than DestFile because otherwise the http method will
1073 // send If-Range requests and there are too many broken servers
1074 // out there that do not understand them
1075 LastGoodSig = DestFile+".reverify";
1076 Rename(Final,LastGoodSig);
1077 }
1078
1079 QueueURI(Desc);
1080 }
1081 /*}}}*/
1082 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1083 // ---------------------------------------------------------------------
1084 /* The only header we use is the last-modified header. */
1085 string pkgAcqMetaSig::Custom600Headers()
1086 {
1087 struct stat Buf;
1088 if (stat(LastGoodSig.c_str(),&Buf) != 0)
1089 return "\nIndex-File: true";
1090
1091 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1092 }
1093
1094 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
1095 pkgAcquire::MethodConfig *Cfg)
1096 {
1097 Item::Done(Message,Size,MD5,Cfg);
1098
1099 string FileName = LookupTag(Message,"Filename");
1100 if (FileName.empty() == true)
1101 {
1102 Status = StatError;
1103 ErrorText = "Method gave a blank filename";
1104 return;
1105 }
1106
1107 if (FileName != DestFile)
1108 {
1109 // We have to copy it into place
1110 Local = true;
1111 Desc.URI = "copy:" + FileName;
1112 QueueURI(Desc);
1113 return;
1114 }
1115
1116 Complete = true;
1117
1118 // put the last known good file back on i-m-s hit (it will
1119 // be re-verified again)
1120 // Else do nothing, we have the new file in DestFile then
1121 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1122 Rename(LastGoodSig, DestFile);
1123
1124 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
1125 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc,
1126 MetaIndexShortDesc, DestFile, IndexTargets,
1127 MetaIndexParser);
1128
1129 }
1130 /*}}}*/
1131 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1132 {
1133 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1134
1135 // if we get a network error we fail gracefully
1136 if(Status == StatTransientNetworkError)
1137 {
1138 Item::Failed(Message,Cnf);
1139 // move the sigfile back on transient network failures
1140 if(FileExists(LastGoodSig))
1141 Rename(LastGoodSig,Final);
1142
1143 // set the status back to , Item::Failed likes to reset it
1144 Status = pkgAcquire::Item::StatTransientNetworkError;
1145 return;
1146 }
1147
1148 // Delete any existing sigfile when the acquire failed
1149 unlink(Final.c_str());
1150
1151 // queue a pkgAcqMetaIndex with no sigfile
1152 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1153 "", IndexTargets, MetaIndexParser);
1154
1155 if (Cnf->LocalOnly == true ||
1156 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1157 {
1158 // Ignore this
1159 Status = StatDone;
1160 Complete = false;
1161 Dequeue();
1162 return;
1163 }
1164
1165 Item::Failed(Message,Cnf);
1166 }
1167 /*}}}*/
1168 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1169 string URI,string URIDesc,string ShortDesc,
1170 string SigFile,
1171 const vector<struct IndexTarget*>* IndexTargets,
1172 indexRecords* MetaIndexParser) :
1173 Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
1174 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
1175 {
1176 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1177 DestFile += URItoFileName(URI);
1178
1179 // Create the item
1180 Desc.Description = URIDesc;
1181 Desc.Owner = this;
1182 Desc.ShortDesc = ShortDesc;
1183 Desc.URI = URI;
1184
1185 QueueURI(Desc);
1186 }
1187 /*}}}*/
1188 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1189 // ---------------------------------------------------------------------
1190 /* The only header we use is the last-modified header. */
1191 string pkgAcqMetaIndex::Custom600Headers()
1192 {
1193 string Final = _config->FindDir("Dir::State::lists");
1194 Final += URItoFileName(RealURI);
1195
1196 struct stat Buf;
1197 if (stat(Final.c_str(),&Buf) != 0)
1198 return "\nIndex-File: true";
1199
1200 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1201 }
1202 /*}}}*/
1203 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, /*{{{*/
1204 pkgAcquire::MethodConfig *Cfg)
1205 {
1206 Item::Done(Message,Size,Hash,Cfg);
1207
1208 // MetaIndexes are done in two passes: one to download the
1209 // metaindex with an appropriate method, and a second to verify it
1210 // with the gpgv method
1211
1212 if (AuthPass == true)
1213 {
1214 AuthDone(Message);
1215
1216 // all cool, move Release file into place
1217 Complete = true;
1218 }
1219 else
1220 {
1221 RetrievalDone(Message);
1222 if (!Complete)
1223 // Still more retrieving to do
1224 return;
1225
1226 if (SigFile == "")
1227 {
1228 // There was no signature file, so we are finished. Download
1229 // the indexes without verification.
1230 QueueIndexes(false);
1231 }
1232 else
1233 {
1234 // There was a signature file, so pass it to gpgv for
1235 // verification
1236
1237 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1238 std::cerr << "Metaindex acquired, queueing gpg verification ("
1239 << SigFile << "," << DestFile << ")\n";
1240 AuthPass = true;
1241 Desc.URI = "gpgv:" + SigFile;
1242 QueueURI(Desc);
1243 Mode = "gpgv";
1244 return;
1245 }
1246 }
1247
1248 if (Complete == true)
1249 {
1250 string FinalFile = _config->FindDir("Dir::State::lists");
1251 FinalFile += URItoFileName(RealURI);
1252 if (SigFile == DestFile)
1253 SigFile = FinalFile;
1254 Rename(DestFile,FinalFile);
1255 chmod(FinalFile.c_str(),0644);
1256 DestFile = FinalFile;
1257 }
1258 }
1259 /*}}}*/
1260 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1261 {
1262 // We have just finished downloading a Release file (it is not
1263 // verified yet)
1264
1265 string FileName = LookupTag(Message,"Filename");
1266 if (FileName.empty() == true)
1267 {
1268 Status = StatError;
1269 ErrorText = "Method gave a blank filename";
1270 return;
1271 }
1272
1273 if (FileName != DestFile)
1274 {
1275 Local = true;
1276 Desc.URI = "copy:" + FileName;
1277 QueueURI(Desc);
1278 return;
1279 }
1280
1281 // make sure to verify against the right file on I-M-S hit
1282 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1283 if(IMSHit)
1284 {
1285 string FinalFile = _config->FindDir("Dir::State::lists");
1286 FinalFile += URItoFileName(RealURI);
1287 if (SigFile == DestFile)
1288 SigFile = FinalFile;
1289 DestFile = FinalFile;
1290 }
1291 Complete = true;
1292 }
1293 /*}}}*/
1294 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1295 {
1296 // At this point, the gpgv method has succeeded, so there is a
1297 // valid signature from a key in the trusted keyring. We
1298 // perform additional verification of its contents, and use them
1299 // to verify the indexes we are about to download
1300
1301 if (!MetaIndexParser->Load(DestFile))
1302 {
1303 Status = StatAuthError;
1304 ErrorText = MetaIndexParser->ErrorText;
1305 return;
1306 }
1307
1308 if (!VerifyVendor(Message))
1309 {
1310 return;
1311 }
1312
1313 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1314 std::cerr << "Signature verification succeeded: "
1315 << DestFile << std::endl;
1316
1317 // Download further indexes with verification
1318 QueueIndexes(true);
1319
1320 // is it a clearsigned MetaIndex file?
1321 if (DestFile == SigFile)
1322 return;
1323
1324 // Done, move signature file into position
1325 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1326 URItoFileName(RealURI) + ".gpg";
1327 Rename(SigFile,VerifiedSigFile);
1328 chmod(VerifiedSigFile.c_str(),0644);
1329 }
1330 /*}}}*/
1331 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1332 {
1333 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1334 Target != IndexTargets->end();
1335 Target++)
1336 {
1337 HashString ExpectedIndexHash;
1338 if (verify)
1339 {
1340 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1341 if (Record == NULL)
1342 {
1343 if ((*Target)->IsOptional() == false)
1344 {
1345 Status = StatAuthError;
1346 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1347 return;
1348 }
1349 }
1350 else
1351 {
1352 ExpectedIndexHash = Record->Hash;
1353 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1354 {
1355 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
1356 std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
1357 }
1358 if (ExpectedIndexHash.empty() == true && (*Target)->IsOptional() == false)
1359 {
1360 Status = StatAuthError;
1361 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1362 return;
1363 }
1364 }
1365 }
1366
1367 if ((*Target)->IsOptional() == true)
1368 {
1369 if ((*Target)->IsSubIndex() == true)
1370 new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
1371 (*Target)->ShortDesc, ExpectedIndexHash);
1372 else
1373 new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
1374 continue;
1375 }
1376
1377 /* Queue Packages file (either diff or full packages files, depending
1378 on the users option) - we also check if the PDiff Index file is listed
1379 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1380 instead, but passing the required info to it is to much hassle */
1381 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1382 MetaIndexParser->Exists(string((*Target)->MetaKey).append(".diff/Index")) == true))
1383 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
1384 (*Target)->ShortDesc, ExpectedIndexHash);
1385 else
1386 new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
1387 }
1388 }
1389 /*}}}*/
1390 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1391 {
1392 string::size_type pos;
1393
1394 // check for missing sigs (that where not fatal because otherwise we had
1395 // bombed earlier)
1396 string missingkeys;
1397 string msg = _("There is no public key available for the "
1398 "following key IDs:\n");
1399 pos = Message.find("NO_PUBKEY ");
1400 if (pos != std::string::npos)
1401 {
1402 string::size_type start = pos+strlen("NO_PUBKEY ");
1403 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1404 missingkeys += (Fingerprint);
1405 }
1406 if(!missingkeys.empty())
1407 _error->Warning("%s", string(msg+missingkeys).c_str());
1408
1409 string Transformed = MetaIndexParser->GetExpectedDist();
1410
1411 if (Transformed == "../project/experimental")
1412 {
1413 Transformed = "experimental";
1414 }
1415
1416 pos = Transformed.rfind('/');
1417 if (pos != string::npos)
1418 {
1419 Transformed = Transformed.substr(0, pos);
1420 }
1421
1422 if (Transformed == ".")
1423 {
1424 Transformed = "";
1425 }
1426
1427 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1428 MetaIndexParser->GetValidUntil() > 0) {
1429 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1430 if (invalid_since > 0)
1431 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1432 // the time since then the file is invalid - formated in the same way as in
1433 // the download progress display (e.g. 7d 3h 42min 1s)
1434 return _error->Error(_("Release file expired, ignoring %s (invalid since %s)"),
1435 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1436 }
1437
1438 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1439 {
1440 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1441 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1442 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1443 }
1444
1445 if (MetaIndexParser->CheckDist(Transformed) == false)
1446 {
1447 // This might become fatal one day
1448 // Status = StatAuthError;
1449 // ErrorText = "Conflicting distribution; expected "
1450 // + MetaIndexParser->GetExpectedDist() + " but got "
1451 // + MetaIndexParser->GetDist();
1452 // return false;
1453 if (!Transformed.empty())
1454 {
1455 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1456 Desc.Description.c_str(),
1457 Transformed.c_str(),
1458 MetaIndexParser->GetDist().c_str());
1459 }
1460 }
1461
1462 return true;
1463 }
1464 /*}}}*/
1465 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1466 // ---------------------------------------------------------------------
1467 /* */
1468 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1469 {
1470 if (AuthPass == true)
1471 {
1472 // gpgv method failed, if we have a good signature
1473 string LastGoodSigFile = _config->FindDir("Dir::State::lists");
1474 if (DestFile == SigFile)
1475 LastGoodSigFile.append(URItoFileName(RealURI));
1476 else
1477 LastGoodSigFile.append("partial/").append(URItoFileName(RealURI)).append(".gpg.reverify");
1478
1479 if(FileExists(LastGoodSigFile))
1480 {
1481 if (DestFile != SigFile)
1482 {
1483 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1484 URItoFileName(RealURI) + ".gpg";
1485 Rename(LastGoodSigFile,VerifiedSigFile);
1486 }
1487 Status = StatTransientNetworkError;
1488 _error->Warning(_("A error occurred during the signature "
1489 "verification. The repository is not updated "
1490 "and the previous index files will be used. "
1491 "GPG error: %s: %s\n"),
1492 Desc.Description.c_str(),
1493 LookupTag(Message,"Message").c_str());
1494 RunScripts("APT::Update::Auth-Failure");
1495 return;
1496 } else {
1497 _error->Warning(_("GPG error: %s: %s"),
1498 Desc.Description.c_str(),
1499 LookupTag(Message,"Message").c_str());
1500 }
1501 // gpgv method failed
1502 ReportMirrorFailure("GPGFailure");
1503 }
1504
1505 /* Always move the meta index, even if gpgv failed. This ensures
1506 * that PackageFile objects are correctly filled in */
1507 if (FileExists(DestFile)) {
1508 string FinalFile = _config->FindDir("Dir::State::lists");
1509 FinalFile += URItoFileName(RealURI);
1510 /* InRelease files become Release files, otherwise
1511 * they would be considered as trusted later on */
1512 if (SigFile == DestFile) {
1513 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
1514 "Release");
1515 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
1516 "Release");
1517 SigFile = FinalFile;
1518 }
1519 Rename(DestFile,FinalFile);
1520 chmod(FinalFile.c_str(),0644);
1521
1522 DestFile = FinalFile;
1523 }
1524
1525 // No Release file was present, or verification failed, so fall
1526 // back to queueing Packages files without verification
1527 QueueIndexes(false);
1528 }
1529 /*}}}*/
1530 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
1531 string const &URI, string const &URIDesc, string const &ShortDesc,
1532 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
1533 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
1534 const vector<struct IndexTarget*>* IndexTargets,
1535 indexRecords* MetaIndexParser) :
1536 pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
1537 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1538 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
1539 {
1540 SigFile = DestFile;
1541 }
1542 /*}}}*/
1543 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1544 // ---------------------------------------------------------------------
1545 // FIXME: this can go away once the InRelease file is used widely
1546 string pkgAcqMetaClearSig::Custom600Headers()
1547 {
1548 string Final = _config->FindDir("Dir::State::lists");
1549 Final += URItoFileName(RealURI);
1550
1551 struct stat Buf;
1552 if (stat(Final.c_str(),&Buf) != 0)
1553 return "\nIndex-File: true\nFail-Ignore: true\n";
1554
1555 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1556 }
1557 /*}}}*/
1558 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1559 {
1560 if (AuthPass == false)
1561 {
1562 new pkgAcqMetaSig(Owner,
1563 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
1564 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1565 IndexTargets, MetaIndexParser);
1566 if (Cnf->LocalOnly == true ||
1567 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
1568 Dequeue();
1569 }
1570 else
1571 pkgAcqMetaIndex::Failed(Message, Cnf);
1572 }
1573 /*}}}*/
1574 // AcqArchive::AcqArchive - Constructor /*{{{*/
1575 // ---------------------------------------------------------------------
1576 /* This just sets up the initial fetch environment and queues the first
1577 possibilitiy */
1578 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1579 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1580 string &StoreFilename) :
1581 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
1582 StoreFilename(StoreFilename), Vf(Version.FileList()),
1583 Trusted(false)
1584 {
1585 Retries = _config->FindI("Acquire::Retries",0);
1586
1587 if (Version.Arch() == 0)
1588 {
1589 _error->Error(_("I wasn't able to locate a file for the %s package. "
1590 "This might mean you need to manually fix this package. "
1591 "(due to missing arch)"),
1592 Version.ParentPkg().Name());
1593 return;
1594 }
1595
1596 /* We need to find a filename to determine the extension. We make the
1597 assumption here that all the available sources for this version share
1598 the same extension.. */
1599 // Skip not source sources, they do not have file fields.
1600 for (; Vf.end() == false; Vf++)
1601 {
1602 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1603 continue;
1604 break;
1605 }
1606
1607 // Does not really matter here.. we are going to fail out below
1608 if (Vf.end() != true)
1609 {
1610 // If this fails to get a file name we will bomb out below.
1611 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1612 if (_error->PendingError() == true)
1613 return;
1614
1615 // Generate the final file name as: package_version_arch.foo
1616 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1617 QuoteString(Version.VerStr(),"_:") + '_' +
1618 QuoteString(Version.Arch(),"_:.") +
1619 "." + flExtension(Parse.FileName());
1620 }
1621
1622 // check if we have one trusted source for the package. if so, switch
1623 // to "TrustedOnly" mode
1624 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
1625 {
1626 pkgIndexFile *Index;
1627 if (Sources->FindIndex(i.File(),Index) == false)
1628 continue;
1629 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1630 {
1631 std::cerr << "Checking index: " << Index->Describe()
1632 << "(Trusted=" << Index->IsTrusted() << ")\n";
1633 }
1634 if (Index->IsTrusted()) {
1635 Trusted = true;
1636 break;
1637 }
1638 }
1639
1640 // "allow-unauthenticated" restores apts old fetching behaviour
1641 // that means that e.g. unauthenticated file:// uris are higher
1642 // priority than authenticated http:// uris
1643 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
1644 Trusted = false;
1645
1646 // Select a source
1647 if (QueueNext() == false && _error->PendingError() == false)
1648 _error->Error(_("I wasn't able to locate a file for the %s package. "
1649 "This might mean you need to manually fix this package."),
1650 Version.ParentPkg().Name());
1651 }
1652 /*}}}*/
1653 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1654 // ---------------------------------------------------------------------
1655 /* This queues the next available file version for download. It checks if
1656 the archive is already available in the cache and stashs the MD5 for
1657 checking later. */
1658 bool pkgAcqArchive::QueueNext()
1659 {
1660 string const ForceHash = _config->Find("Acquire::ForceHash");
1661 for (; Vf.end() == false; Vf++)
1662 {
1663 // Ignore not source sources
1664 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1665 continue;
1666
1667 // Try to cross match against the source list
1668 pkgIndexFile *Index;
1669 if (Sources->FindIndex(Vf.File(),Index) == false)
1670 continue;
1671
1672 // only try to get a trusted package from another source if that source
1673 // is also trusted
1674 if(Trusted && !Index->IsTrusted())
1675 continue;
1676
1677 // Grab the text package record
1678 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1679 if (_error->PendingError() == true)
1680 return false;
1681
1682 string PkgFile = Parse.FileName();
1683 if (ForceHash.empty() == false)
1684 {
1685 if(stringcasecmp(ForceHash, "sha512") == 0)
1686 ExpectedHash = HashString("SHA512", Parse.SHA512Hash());
1687 if(stringcasecmp(ForceHash, "sha256") == 0)
1688 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
1689 else if (stringcasecmp(ForceHash, "sha1") == 0)
1690 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
1691 else
1692 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1693 }
1694 else
1695 {
1696 string Hash;
1697 if ((Hash = Parse.SHA512Hash()).empty() == false)
1698 ExpectedHash = HashString("SHA512", Hash);
1699 else if ((Hash = Parse.SHA256Hash()).empty() == false)
1700 ExpectedHash = HashString("SHA256", Hash);
1701 else if ((Hash = Parse.SHA1Hash()).empty() == false)
1702 ExpectedHash = HashString("SHA1", Hash);
1703 else
1704 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1705 }
1706 if (PkgFile.empty() == true)
1707 return _error->Error(_("The package index files are corrupted. No Filename: "
1708 "field for package %s."),
1709 Version.ParentPkg().Name());
1710
1711 Desc.URI = Index->ArchiveURI(PkgFile);
1712 Desc.Description = Index->ArchiveInfo(Version);
1713 Desc.Owner = this;
1714 Desc.ShortDesc = Version.ParentPkg().Name();
1715
1716 // See if we already have the file. (Legacy filenames)
1717 FileSize = Version->Size;
1718 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1719 struct stat Buf;
1720 if (stat(FinalFile.c_str(),&Buf) == 0)
1721 {
1722 // Make sure the size matches
1723 if ((unsigned)Buf.st_size == Version->Size)
1724 {
1725 Complete = true;
1726 Local = true;
1727 Status = StatDone;
1728 StoreFilename = DestFile = FinalFile;
1729 return true;
1730 }
1731
1732 /* Hmm, we have a file and its size does not match, this means it is
1733 an old style mismatched arch */
1734 unlink(FinalFile.c_str());
1735 }
1736
1737 // Check it again using the new style output filenames
1738 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1739 if (stat(FinalFile.c_str(),&Buf) == 0)
1740 {
1741 // Make sure the size matches
1742 if ((unsigned)Buf.st_size == Version->Size)
1743 {
1744 Complete = true;
1745 Local = true;
1746 Status = StatDone;
1747 StoreFilename = DestFile = FinalFile;
1748 return true;
1749 }
1750
1751 /* Hmm, we have a file and its size does not match, this shouldnt
1752 happen.. */
1753 unlink(FinalFile.c_str());
1754 }
1755
1756 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
1757
1758 // Check the destination file
1759 if (stat(DestFile.c_str(),&Buf) == 0)
1760 {
1761 // Hmm, the partial file is too big, erase it
1762 if ((unsigned)Buf.st_size > Version->Size)
1763 unlink(DestFile.c_str());
1764 else
1765 PartialSize = Buf.st_size;
1766 }
1767
1768 // Create the item
1769 Local = false;
1770 Desc.URI = Index->ArchiveURI(PkgFile);
1771 Desc.Description = Index->ArchiveInfo(Version);
1772 Desc.Owner = this;
1773 Desc.ShortDesc = Version.ParentPkg().Name();
1774 QueueURI(Desc);
1775
1776 Vf++;
1777 return true;
1778 }
1779 return false;
1780 }
1781 /*}}}*/
1782 // AcqArchive::Done - Finished fetching /*{{{*/
1783 // ---------------------------------------------------------------------
1784 /* */
1785 void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash,
1786 pkgAcquire::MethodConfig *Cfg)
1787 {
1788 Item::Done(Message,Size,CalcHash,Cfg);
1789
1790 // Check the size
1791 if (Size != Version->Size)
1792 {
1793 Status = StatError;
1794 ErrorText = _("Size mismatch");
1795 return;
1796 }
1797
1798 // Check the hash
1799 if(ExpectedHash.toStr() != CalcHash)
1800 {
1801 Status = StatError;
1802 ErrorText = _("Hash Sum mismatch");
1803 if(FileExists(DestFile))
1804 Rename(DestFile,DestFile + ".FAILED");
1805 return;
1806 }
1807
1808 // Grab the output filename
1809 string FileName = LookupTag(Message,"Filename");
1810 if (FileName.empty() == true)
1811 {
1812 Status = StatError;
1813 ErrorText = "Method gave a blank filename";
1814 return;
1815 }
1816
1817 Complete = true;
1818
1819 // Reference filename
1820 if (FileName != DestFile)
1821 {
1822 StoreFilename = DestFile = FileName;
1823 Local = true;
1824 return;
1825 }
1826
1827 // Done, move it into position
1828 string FinalFile = _config->FindDir("Dir::Cache::Archives");
1829 FinalFile += flNotDir(StoreFilename);
1830 Rename(DestFile,FinalFile);
1831
1832 StoreFilename = DestFile = FinalFile;
1833 Complete = true;
1834 }
1835 /*}}}*/
1836 // AcqArchive::Failed - Failure handler /*{{{*/
1837 // ---------------------------------------------------------------------
1838 /* Here we try other sources */
1839 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1840 {
1841 ErrorText = LookupTag(Message,"Message");
1842
1843 /* We don't really want to retry on failed media swaps, this prevents
1844 that. An interesting observation is that permanent failures are not
1845 recorded. */
1846 if (Cnf->Removable == true &&
1847 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1848 {
1849 // Vf = Version.FileList();
1850 while (Vf.end() == false) Vf++;
1851 StoreFilename = string();
1852 Item::Failed(Message,Cnf);
1853 return;
1854 }
1855
1856 if (QueueNext() == false)
1857 {
1858 // This is the retry counter
1859 if (Retries != 0 &&
1860 Cnf->LocalOnly == false &&
1861 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1862 {
1863 Retries--;
1864 Vf = Version.FileList();
1865 if (QueueNext() == true)
1866 return;
1867 }
1868
1869 StoreFilename = string();
1870 Item::Failed(Message,Cnf);
1871 }
1872 }
1873 /*}}}*/
1874 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
1875 // ---------------------------------------------------------------------
1876 bool pkgAcqArchive::IsTrusted()
1877 {
1878 return Trusted;
1879 }
1880 /*}}}*/
1881 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1882 // ---------------------------------------------------------------------
1883 /* */
1884 void pkgAcqArchive::Finished()
1885 {
1886 if (Status == pkgAcquire::Item::StatDone &&
1887 Complete == true)
1888 return;
1889 StoreFilename = string();
1890 }
1891 /*}}}*/
1892 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1893 // ---------------------------------------------------------------------
1894 /* The file is added to the queue */
1895 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
1896 unsigned long Size,string Dsc,string ShortDesc,
1897 const string &DestDir, const string &DestFilename,
1898 bool IsIndexFile) :
1899 Item(Owner), ExpectedHash(Hash), IsIndexFile(IsIndexFile)
1900 {
1901 Retries = _config->FindI("Acquire::Retries",0);
1902
1903 if(!DestFilename.empty())
1904 DestFile = DestFilename;
1905 else if(!DestDir.empty())
1906 DestFile = DestDir + "/" + flNotDir(URI);
1907 else
1908 DestFile = flNotDir(URI);
1909
1910 // Create the item
1911 Desc.URI = URI;
1912 Desc.Description = Dsc;
1913 Desc.Owner = this;
1914
1915 // Set the short description to the archive component
1916 Desc.ShortDesc = ShortDesc;
1917
1918 // Get the transfer sizes
1919 FileSize = Size;
1920 struct stat Buf;
1921 if (stat(DestFile.c_str(),&Buf) == 0)
1922 {
1923 // Hmm, the partial file is too big, erase it
1924 if ((unsigned)Buf.st_size > Size)
1925 unlink(DestFile.c_str());
1926 else
1927 PartialSize = Buf.st_size;
1928 }
1929
1930 QueueURI(Desc);
1931 }
1932 /*}}}*/
1933 // AcqFile::Done - Item downloaded OK /*{{{*/
1934 // ---------------------------------------------------------------------
1935 /* */
1936 void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash,
1937 pkgAcquire::MethodConfig *Cnf)
1938 {
1939 Item::Done(Message,Size,CalcHash,Cnf);
1940
1941 // Check the hash
1942 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
1943 {
1944 Status = StatError;
1945 ErrorText = _("Hash Sum mismatch");
1946 Rename(DestFile,DestFile + ".FAILED");
1947 return;
1948 }
1949
1950 string FileName = LookupTag(Message,"Filename");
1951 if (FileName.empty() == true)
1952 {
1953 Status = StatError;
1954 ErrorText = "Method gave a blank filename";
1955 return;
1956 }
1957
1958 Complete = true;
1959
1960 // The files timestamp matches
1961 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1962 return;
1963
1964 // We have to copy it into place
1965 if (FileName != DestFile)
1966 {
1967 Local = true;
1968 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1969 Cnf->Removable == true)
1970 {
1971 Desc.URI = "copy:" + FileName;
1972 QueueURI(Desc);
1973 return;
1974 }
1975
1976 // Erase the file if it is a symlink so we can overwrite it
1977 struct stat St;
1978 if (lstat(DestFile.c_str(),&St) == 0)
1979 {
1980 if (S_ISLNK(St.st_mode) != 0)
1981 unlink(DestFile.c_str());
1982 }
1983
1984 // Symlink the file
1985 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1986 {
1987 ErrorText = "Link to " + DestFile + " failure ";
1988 Status = StatError;
1989 Complete = false;
1990 }
1991 }
1992 }
1993 /*}}}*/
1994 // AcqFile::Failed - Failure handler /*{{{*/
1995 // ---------------------------------------------------------------------
1996 /* Here we try other sources */
1997 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1998 {
1999 ErrorText = LookupTag(Message,"Message");
2000
2001 // This is the retry counter
2002 if (Retries != 0 &&
2003 Cnf->LocalOnly == false &&
2004 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2005 {
2006 Retries--;
2007 QueueURI(Desc);
2008 return;
2009 }
2010
2011 Item::Failed(Message,Cnf);
2012 }
2013 /*}}}*/
2014 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2015 // ---------------------------------------------------------------------
2016 /* The only header we use is the last-modified header. */
2017 string pkgAcqFile::Custom600Headers()
2018 {
2019 if (IsIndexFile)
2020 return "\nIndex-File: true";
2021 return "";
2022 }
2023 /*}}}*/
2024 bool IndexTarget::IsOptional() const {
2025 if (strncmp(ShortDesc.c_str(), "Translation", 11) != 0)
2026 return false;
2027 return true;
2028 }
2029 bool IndexTarget::IsSubIndex() const {
2030 if (ShortDesc != "TranslationIndex")
2031 return false;
2032 return true;
2033 }