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