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