]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
Do not allow going from authenticated to unauthenticated repo
[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 <config.h>
17
18 #include <apt-pkg/acquire-item.h>
19 #include <apt-pkg/configuration.h>
20 #include <apt-pkg/aptconfiguration.h>
21 #include <apt-pkg/sourcelist.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/sha1.h>
26 #include <apt-pkg/tagfile.h>
27 #include <apt-pkg/indexrecords.h>
28 #include <apt-pkg/acquire.h>
29 #include <apt-pkg/hashes.h>
30 #include <apt-pkg/indexfile.h>
31 #include <apt-pkg/pkgcache.h>
32 #include <apt-pkg/cacheiterators.h>
33 #include <apt-pkg/pkgrecords.h>
34
35 #include <stddef.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <iostream>
39 #include <vector>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <errno.h>
43 #include <string>
44 #include <sstream>
45 #include <stdio.h>
46 #include <ctime>
47
48 #include <apti18n.h>
49 /*}}}*/
50
51 using namespace std;
52
53 static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
54 {
55 if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
56 return;
57 std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
58 for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
59 std::cerr << "\t- " << hs->toStr() << std::endl;
60 std::cerr << " Actual Hash: " << std::endl;
61 for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
62 std::cerr << "\t- " << hs->toStr() << std::endl;
63 }
64 /*}}}*/
65
66 // Acquire::Item::Item - Constructor /*{{{*/
67 pkgAcquire::Item::Item(pkgAcquire *Owner,
68 HashStringList const &ExpectedHashes,
69 pkgAcqMetaBase *TransactionManager)
70 : Owner(Owner), FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false),
71 Local(false), QueueCounter(0), TransactionManager(TransactionManager),
72 ExpectedAdditionalItems(0), ExpectedHashes(ExpectedHashes)
73 {
74 Owner->Add(this);
75 Status = StatIdle;
76 if(TransactionManager != NULL)
77 TransactionManager->Add(this);
78 }
79 /*}}}*/
80 // Acquire::Item::~Item - Destructor /*{{{*/
81 // ---------------------------------------------------------------------
82 /* */
83 pkgAcquire::Item::~Item()
84 {
85 Owner->Remove(this);
86 }
87 /*}}}*/
88 // Acquire::Item::Failed - Item failed to download /*{{{*/
89 // ---------------------------------------------------------------------
90 /* We return to an idle state if there are still other queues that could
91 fetch this object */
92 void pkgAcquire::Item::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
93 {
94 Status = StatIdle;
95 if(ErrorText == "")
96 ErrorText = LookupTag(Message,"Message");
97 UsedMirror = LookupTag(Message,"UsedMirror");
98 if (QueueCounter <= 1)
99 {
100 /* This indicates that the file is not available right now but might
101 be sometime later. If we do a retry cycle then this should be
102 retried [CDROMs] */
103 if (Cnf->LocalOnly == true &&
104 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
105 {
106 Status = StatIdle;
107 Dequeue();
108 return;
109 }
110
111 Status = StatError;
112 Dequeue();
113 }
114
115 // report mirror failure back to LP if we actually use a mirror
116 string FailReason = LookupTag(Message, "FailReason");
117 if(FailReason.size() != 0)
118 ReportMirrorFailure(FailReason);
119 else
120 ReportMirrorFailure(ErrorText);
121 }
122 /*}}}*/
123 // Acquire::Item::Start - Item has begun to download /*{{{*/
124 // ---------------------------------------------------------------------
125 /* Stash status and the file size. Note that setting Complete means
126 sub-phases of the acquire process such as decompresion are operating */
127 void pkgAcquire::Item::Start(string /*Message*/,unsigned long long Size)
128 {
129 Status = StatFetching;
130 if (FileSize == 0 && Complete == false)
131 FileSize = Size;
132 }
133 /*}}}*/
134 // Acquire::Item::Done - Item downloaded OK /*{{{*/
135 // ---------------------------------------------------------------------
136 /* */
137 void pkgAcquire::Item::Done(string Message,unsigned long long Size,HashStringList const &/*Hash*/,
138 pkgAcquire::MethodConfig * /*Cnf*/)
139 {
140 // We just downloaded something..
141 string FileName = LookupTag(Message,"Filename");
142 UsedMirror = LookupTag(Message,"UsedMirror");
143 if (Complete == false && !Local && FileName == DestFile)
144 {
145 if (Owner->Log != 0)
146 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
147 }
148
149 if (FileSize == 0)
150 FileSize= Size;
151 Status = StatDone;
152 ErrorText = string();
153 Owner->Dequeue(this);
154 }
155 /*}}}*/
156 // Acquire::Item::Rename - Rename a file /*{{{*/
157 // ---------------------------------------------------------------------
158 /* This helper function is used by a lot of item methods as their final
159 step */
160 bool pkgAcquire::Item::Rename(string From,string To)
161 {
162 if (rename(From.c_str(),To.c_str()) != 0)
163 {
164 char S[300];
165 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
166 From.c_str(),To.c_str());
167 Status = StatError;
168 ErrorText += S;
169 return false;
170 }
171 return true;
172 }
173 /*}}}*/
174 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
175 {
176 if(FileExists(DestFile))
177 Rename(DestFile, DestFile + ".FAILED");
178
179 switch (error)
180 {
181 case HashSumMismatch:
182 ErrorText = _("Hash Sum mismatch");
183 Status = StatAuthError;
184 ReportMirrorFailure("HashChecksumFailure");
185 break;
186 case SizeMismatch:
187 ErrorText = _("Size mismatch");
188 Status = StatAuthError;
189 ReportMirrorFailure("SizeFailure");
190 break;
191 case InvalidFormat:
192 ErrorText = _("Invalid file format");
193 Status = StatError;
194 // do not report as usually its not the mirrors fault, but Portal/Proxy
195 break;
196 case SignatureError:
197 ErrorText = _("Signature error");
198 Status = StatError;
199 break;
200 case NotClearsigned:
201 ErrorText = _("Does not start with a cleartext signature");
202 Status = StatError;
203 break;
204 }
205 return false;
206 }
207 /*}}}*/
208 // Acquire::Item::ReportMirrorFailure /*{{{*/
209 // ---------------------------------------------------------------------
210 void pkgAcquire::Item::ReportMirrorFailure(string FailCode)
211 {
212 // we only act if a mirror was used at all
213 if(UsedMirror.empty())
214 return;
215 #if 0
216 std::cerr << "\nReportMirrorFailure: "
217 << UsedMirror
218 << " Uri: " << DescURI()
219 << " FailCode: "
220 << FailCode << std::endl;
221 #endif
222 const char *Args[40];
223 unsigned int i = 0;
224 string report = _config->Find("Methods::Mirror::ProblemReporting",
225 "/usr/lib/apt/apt-report-mirror-failure");
226 if(!FileExists(report))
227 return;
228 Args[i++] = report.c_str();
229 Args[i++] = UsedMirror.c_str();
230 Args[i++] = DescURI().c_str();
231 Args[i++] = FailCode.c_str();
232 Args[i++] = NULL;
233 pid_t pid = ExecFork();
234 if(pid < 0)
235 {
236 _error->Error("ReportMirrorFailure Fork failed");
237 return;
238 }
239 else if(pid == 0)
240 {
241 execvp(Args[0], (char**)Args);
242 std::cerr << "Could not exec " << Args[0] << std::endl;
243 _exit(100);
244 }
245 if(!ExecWait(pid, "report-mirror-failure"))
246 {
247 _error->Warning("Couldn't report problem to '%s'",
248 _config->Find("Methods::Mirror::ProblemReporting").c_str());
249 }
250 }
251 /*}}}*/
252 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
253 // ---------------------------------------------------------------------
254 /* Get the DiffIndex file first and see if there are patches available
255 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
256 * patches. If anything goes wrong in that process, it will fall back to
257 * the original packages file
258 */
259 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
260 pkgAcqMetaBase *TransactionManager,
261 IndexTarget const * const Target,
262 HashStringList const &ExpectedHashes,
263 indexRecords *MetaIndexParser)
264 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes,
265 MetaIndexParser), PackagesFileReadyInPartial(false)
266 {
267
268 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
269
270 RealURI = Target->URI;
271 Desc.Owner = this;
272 Desc.Description = Target->Description + "/DiffIndex";
273 Desc.ShortDesc = Target->ShortDesc;
274 Desc.URI = Target->URI + ".diff/Index";
275
276 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
277 DestFile += URItoFileName(Desc.URI);
278
279 if(Debug)
280 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
281
282 // look for the current package file
283 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
284 CurrentPackagesFile += URItoFileName(RealURI);
285
286 // FIXME: this file:/ check is a hack to prevent fetching
287 // from local sources. this is really silly, and
288 // should be fixed cleanly as soon as possible
289 if(!FileExists(CurrentPackagesFile) ||
290 Desc.URI.substr(0,strlen("file:/")) == "file:/")
291 {
292 // we don't have a pkg file or we don't want to queue
293 if(Debug)
294 std::clog << "No index file, local or canceld by user" << std::endl;
295 Failed("", NULL);
296 return;
297 }
298
299 if(Debug)
300 std::clog << "pkgAcqDiffIndex::pkgAcqDiffIndex(): "
301 << CurrentPackagesFile << std::endl;
302
303 QueueURI(Desc);
304
305 }
306 /*}}}*/
307 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
308 // ---------------------------------------------------------------------
309 /* The only header we use is the last-modified header. */
310 string pkgAcqDiffIndex::Custom600Headers() const
311 {
312 string Final = _config->FindDir("Dir::State::lists");
313 Final += URItoFileName(Desc.URI);
314
315 if(Debug)
316 std::clog << "Custom600Header-IMS: " << Final << std::endl;
317
318 struct stat Buf;
319 if (stat(Final.c_str(),&Buf) != 0)
320 return "\nIndex-File: true";
321
322 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
323 }
324 /*}}}*/
325 bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
326 {
327 if(Debug)
328 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
329 << std::endl;
330
331 pkgTagSection Tags;
332 string ServerSha1;
333 vector<DiffInfo> available_patches;
334
335 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
336 pkgTagFile TF(&Fd);
337 if (_error->PendingError() == true)
338 return false;
339
340 if(TF.Step(Tags) == true)
341 {
342 bool found = false;
343 DiffInfo d;
344 string size;
345
346 string const tmp = Tags.FindS("SHA1-Current");
347 std::stringstream ss(tmp);
348 ss >> ServerSha1 >> size;
349 unsigned long const ServerSize = atol(size.c_str());
350
351 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
352 SHA1Summation SHA1;
353 SHA1.AddFD(fd);
354 string const local_sha1 = SHA1.Result();
355
356 if(local_sha1 == ServerSha1)
357 {
358 // we have the same sha1 as the server so we are done here
359 if(Debug)
360 std::clog << "Package file is up-to-date" << std::endl;
361 // ensure we have no leftovers from previous runs
362 std::string Partial = _config->FindDir("Dir::State::lists");
363 Partial += "partial/" + URItoFileName(RealURI);
364 unlink(Partial.c_str());
365 // list cleanup needs to know that this file as well as the already
366 // present index is ours, so we create an empty diff to save it for us
367 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
368 ExpectedHashes, MetaIndexParser,
369 ServerSha1, available_patches);
370 return true;
371 }
372 else
373 {
374 if(Debug)
375 std::clog << "SHA1-Current: " << ServerSha1 << " and we start at "<< fd.Name() << " " << fd.Size() << " " << local_sha1 << std::endl;
376
377 // check the historie and see what patches we need
378 string const history = Tags.FindS("SHA1-History");
379 std::stringstream hist(history);
380 while(hist >> d.sha1 >> size >> d.file)
381 {
382 // read until the first match is found
383 // from that point on, we probably need all diffs
384 if(d.sha1 == local_sha1)
385 found=true;
386 else if (found == false)
387 continue;
388
389 if(Debug)
390 std::clog << "Need to get diff: " << d.file << std::endl;
391 available_patches.push_back(d);
392 }
393
394 if (available_patches.empty() == false)
395 {
396 // patching with too many files is rather slow compared to a fast download
397 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
398 if (fileLimit != 0 && fileLimit < available_patches.size())
399 {
400 if (Debug)
401 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
402 << ") so fallback to complete download" << std::endl;
403 return false;
404 }
405
406 // see if the patches are too big
407 found = false; // it was true and it will be true again at the end
408 d = *available_patches.begin();
409 string const firstPatch = d.file;
410 unsigned long patchesSize = 0;
411 std::stringstream patches(Tags.FindS("SHA1-Patches"));
412 while(patches >> d.sha1 >> size >> d.file)
413 {
414 if (firstPatch == d.file)
415 found = true;
416 else if (found == false)
417 continue;
418
419 patchesSize += atol(size.c_str());
420 }
421 unsigned long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
422 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
423 {
424 if (Debug)
425 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
426 << ") so fallback to complete download" << std::endl;
427 return false;
428 }
429 }
430 }
431
432 // we have something, queue the next diff
433 if(found)
434 {
435 // FIXME: make this use the method
436 PackagesFileReadyInPartial = true;
437 std::string Partial = _config->FindDir("Dir::State::lists");
438 Partial += "partial/" + URItoFileName(RealURI);
439
440 FileFd From(CurrentPackagesFile, FileFd::ReadOnly);
441 FileFd To(Partial, FileFd::WriteEmpty);
442 if(CopyFile(From, To) == false)
443 return _error->Errno("CopyFile", "failed to copy");
444
445 if(Debug)
446 std::cerr << "Done copying " << CurrentPackagesFile
447 << " -> " << Partial
448 << std::endl;
449
450 // queue the diffs
451 string::size_type const last_space = Description.rfind(" ");
452 if(last_space != string::npos)
453 Description.erase(last_space, Description.size()-last_space);
454
455 /* decide if we should download patches one by one or in one go:
456 The first is good if the server merges patches, but many don't so client
457 based merging can be attempt in which case the second is better.
458 "bad things" will happen if patches are merged on the server,
459 but client side merging is attempt as well */
460 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
461 if (pdiff_merge == true)
462 {
463 // reprepro adds this flag if it has merged patches on the server
464 std::string const precedence = Tags.FindS("X-Patch-Precedence");
465 pdiff_merge = (precedence != "merged");
466 }
467
468 if (pdiff_merge == false)
469 {
470 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, ExpectedHashes,
471 MetaIndexParser,
472 ServerSha1, available_patches);
473 }
474 else
475 {
476 std::vector<pkgAcqIndexMergeDiffs*> *diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
477 for(size_t i = 0; i < available_patches.size(); ++i)
478 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner,
479 TransactionManager,
480 Target,
481 ExpectedHashes,
482 MetaIndexParser,
483 available_patches[i],
484 diffs);
485 }
486
487 Complete = false;
488 Status = StatDone;
489 Dequeue();
490 return true;
491 }
492 }
493
494 // Nothing found, report and return false
495 // Failing here is ok, if we return false later, the full
496 // IndexFile is queued
497 if(Debug)
498 std::clog << "Can't find a patch in the index file" << std::endl;
499 return false;
500 }
501 /*}}}*/
502 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
503 {
504 if(Debug)
505 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
506 << "Falling back to normal index file acquire" << std::endl;
507
508 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
509
510 Complete = false;
511 Status = StatDone;
512 Dequeue();
513 }
514 /*}}}*/
515 void pkgAcqDiffIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
516 pkgAcquire::MethodConfig *Cnf)
517 {
518 if(Debug)
519 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
520
521 Item::Done(Message, Size, Hashes, Cnf);
522
523 string FinalFile;
524 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
525
526 // success in downloading the index
527 // rename the index
528 FinalFile += string(".IndexDiff");
529 if(Debug)
530 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
531 << std::endl;
532 Rename(DestFile,FinalFile);
533 chmod(FinalFile.c_str(),0644);
534 DestFile = FinalFile;
535
536 if(!ParseDiffIndex(DestFile))
537 return Failed("", NULL);
538
539 Complete = true;
540 Status = StatDone;
541 Dequeue();
542 return;
543 }
544 /*}}}*/
545 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
546 // ---------------------------------------------------------------------
547 /* The package diff is added to the queue. one object is constructed
548 * for each diff and the index
549 */
550 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
551 pkgAcqMetaBase *TransactionManager,
552 struct IndexTarget const * const Target,
553 HashStringList const &ExpectedHashes,
554 indexRecords *MetaIndexParser,
555 string ServerSha1,
556 vector<DiffInfo> diffs)
557 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser),
558 available_patches(diffs), ServerSha1(ServerSha1)
559 {
560
561 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
562 DestFile += URItoFileName(Target->URI);
563
564 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
565
566 RealURI = Target->URI;
567 Desc.Owner = this;
568 Description = Target->Description;
569 Desc.ShortDesc = Target->ShortDesc;
570
571 if(available_patches.empty() == true)
572 {
573 // we are done (yeah!), check hashes against the final file
574 DestFile = _config->FindDir("Dir::State::lists");
575 DestFile += URItoFileName(Target->URI);
576 Finish(true);
577 }
578 else
579 {
580 // get the next diff
581 State = StateFetchDiff;
582 QueueNextDiff();
583 }
584 }
585 /*}}}*/
586 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
587 {
588 if(Debug)
589 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
590 << "Falling back to normal index file acquire" << std::endl;
591 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
592 Finish();
593 }
594 /*}}}*/
595 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
596 void pkgAcqIndexDiffs::Finish(bool allDone)
597 {
598 if(Debug)
599 std::clog << "pkgAcqIndexDiffs::Finish(): "
600 << allDone << " "
601 << Desc.URI << std::endl;
602
603 // we restore the original name, this is required, otherwise
604 // the file will be cleaned
605 if(allDone)
606 {
607 if(HashSums().usable() && !HashSums().VerifyFile(DestFile))
608 {
609 RenameOnError(HashSumMismatch);
610 Dequeue();
611 return;
612 }
613
614 // queue for copy
615 PartialFile = _config->FindDir("Dir::State::lists")+"partial/"+URItoFileName(RealURI);
616
617 DestFile = _config->FindDir("Dir::State::lists");
618 DestFile += URItoFileName(RealURI);
619
620 // this happens if we have a up-to-date indexfile
621 if(!FileExists(PartialFile))
622 PartialFile = DestFile;
623
624 // this is for the "real" finish
625 Complete = true;
626 Status = StatDone;
627 Dequeue();
628 if(Debug)
629 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
630 return;
631 }
632
633 if(Debug)
634 std::clog << "Finishing: " << Desc.URI << std::endl;
635 Complete = false;
636 Status = StatDone;
637 Dequeue();
638 return;
639 }
640 /*}}}*/
641 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
642 {
643 // calc sha1 of the just patched file
644 string FinalFile = _config->FindDir("Dir::State::lists");
645 FinalFile += "partial/" + URItoFileName(RealURI);
646
647 if(!FileExists(FinalFile))
648 {
649 Failed("No FinalFile " + FinalFile + " available", NULL);
650 return false;
651 }
652
653 FileFd fd(FinalFile, FileFd::ReadOnly);
654 SHA1Summation SHA1;
655 SHA1.AddFD(fd);
656 string local_sha1 = string(SHA1.Result());
657 if(Debug)
658 std::clog << "QueueNextDiff: "
659 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
660
661
662 // final file reached before all patches are applied
663 if(local_sha1 == ServerSha1)
664 {
665 Finish(true);
666 return true;
667 }
668
669 // remove all patches until the next matching patch is found
670 // this requires the Index file to be ordered
671 for(vector<DiffInfo>::iterator I=available_patches.begin();
672 available_patches.empty() == false &&
673 I != available_patches.end() &&
674 I->sha1 != local_sha1;
675 ++I)
676 {
677 available_patches.erase(I);
678 }
679
680 // error checking and falling back if no patch was found
681 if(available_patches.empty() == true)
682 {
683 Failed("No patches available", NULL);
684 return false;
685 }
686
687 // queue the right diff
688 Desc.URI = RealURI + ".diff/" + available_patches[0].file + ".gz";
689 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
690 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
691 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
692
693 if(Debug)
694 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
695
696 QueueURI(Desc);
697
698 return true;
699 }
700 /*}}}*/
701 void pkgAcqIndexDiffs::Done(string Message,unsigned long long Size, HashStringList const &Hashes, /*{{{*/
702 pkgAcquire::MethodConfig *Cnf)
703 {
704 if(Debug)
705 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
706
707 Item::Done(Message, Size, Hashes, Cnf);
708
709 string FinalFile;
710 FinalFile = _config->FindDir("Dir::State::lists")+"partial/"+URItoFileName(RealURI);
711
712 // success in downloading a diff, enter ApplyDiff state
713 if(State == StateFetchDiff)
714 {
715
716 // rred excepts the patch as $FinalFile.ed
717 Rename(DestFile,FinalFile+".ed");
718
719 if(Debug)
720 std::clog << "Sending to rred method: " << FinalFile << std::endl;
721
722 State = StateApplyDiff;
723 Local = true;
724 Desc.URI = "rred:" + FinalFile;
725 QueueURI(Desc);
726 Mode = "rred";
727 return;
728 }
729
730
731 // success in download/apply a diff, queue next (if needed)
732 if(State == StateApplyDiff)
733 {
734 // remove the just applied patch
735 available_patches.erase(available_patches.begin());
736 unlink((FinalFile + ".ed").c_str());
737
738 // move into place
739 if(Debug)
740 {
741 std::clog << "Moving patched file in place: " << std::endl
742 << DestFile << " -> " << FinalFile << std::endl;
743 }
744 Rename(DestFile,FinalFile);
745 chmod(FinalFile.c_str(),0644);
746
747 // see if there is more to download
748 if(available_patches.empty() == false) {
749 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
750 ExpectedHashes, MetaIndexParser,
751 ServerSha1, available_patches);
752 return Finish();
753 } else
754 // update
755 DestFile = FinalFile;
756 return Finish(true);
757 }
758 }
759 /*}}}*/
760 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
761 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire *Owner,
762 pkgAcqMetaBase *TransactionManager,
763 struct IndexTarget const * const Target,
764 HashStringList const &ExpectedHashes,
765 indexRecords *MetaIndexParser,
766 DiffInfo const &patch,
767 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
768 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser),
769 patch(patch), allPatches(allPatches), State(StateFetchDiff)
770 {
771
772 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
773 DestFile += URItoFileName(Target->URI);
774
775 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
776
777 RealURI = Target->URI;
778 Desc.Owner = this;
779 Description = Target->Description;
780 Desc.ShortDesc = Target->ShortDesc;
781
782 Desc.URI = RealURI + ".diff/" + patch.file + ".gz";
783 Desc.Description = Description + " " + patch.file + string(".pdiff");
784 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
785 DestFile += URItoFileName(RealURI + ".diff/" + patch.file);
786
787 if(Debug)
788 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
789
790 QueueURI(Desc);
791 }
792 /*}}}*/
793 void pkgAcqIndexMergeDiffs::Failed(string Message,pkgAcquire::MethodConfig * /*Cnf*/)/*{{{*/
794 {
795 if(Debug)
796 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
797 Complete = false;
798 Status = StatDone;
799 Dequeue();
800
801 // check if we are the first to fail, otherwise we are done here
802 State = StateDoneDiff;
803 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
804 I != allPatches->end(); ++I)
805 if ((*I)->State == StateErrorDiff)
806 return;
807
808 // first failure means we should fallback
809 State = StateErrorDiff;
810 std::clog << "Falling back to normal index file acquire" << std::endl;
811 new pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser);
812 }
813 /*}}}*/
814 void pkgAcqIndexMergeDiffs::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
815 pkgAcquire::MethodConfig *Cnf)
816 {
817 if(Debug)
818 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
819
820 Item::Done(Message,Size,Hashes,Cnf);
821
822 string const FinalFile = _config->FindDir("Dir::State::lists") + "partial/" + URItoFileName(RealURI);
823
824 if (State == StateFetchDiff)
825 {
826 // rred expects the patch as $FinalFile.ed.$patchname.gz
827 Rename(DestFile, FinalFile + ".ed." + patch.file + ".gz");
828
829 // check if this is the last completed diff
830 State = StateDoneDiff;
831 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
832 I != allPatches->end(); ++I)
833 if ((*I)->State != StateDoneDiff)
834 {
835 if(Debug)
836 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
837 return;
838 }
839
840 // this is the last completed diff, so we are ready to apply now
841 State = StateApplyDiff;
842
843 if(Debug)
844 std::clog << "Sending to rred method: " << FinalFile << std::endl;
845
846 Local = true;
847 Desc.URI = "rred:" + FinalFile;
848 QueueURI(Desc);
849 Mode = "rred";
850 return;
851 }
852 // success in download/apply all diffs, clean up
853 else if (State == StateApplyDiff)
854 {
855 // see if we really got the expected file
856 if(ExpectedHashes.usable() && !ExpectedHashes.VerifyFile(DestFile))
857 {
858 RenameOnError(HashSumMismatch);
859 return;
860 }
861
862
863 std::string FinalFile = _config->FindDir("Dir::State::lists");
864 FinalFile += URItoFileName(RealURI);
865
866 // move the result into place
867 if(Debug)
868 std::clog << "Queue patched file in place: " << std::endl
869 << DestFile << " -> " << FinalFile << std::endl;
870
871 // queue for copy by the transaction manager
872 PartialFile = DestFile;
873 DestFile = FinalFile;
874
875 // ensure the ed's are gone regardless of list-cleanup
876 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
877 I != allPatches->end(); ++I)
878 {
879 std::string PartialFile = _config->FindDir("Dir::State::lists");
880 PartialFile += "partial/" + URItoFileName(RealURI);
881 std::string patch = PartialFile + ".ed." + (*I)->patch.file + ".gz";
882 std::cerr << patch << std::endl;
883 unlink(patch.c_str());
884 }
885
886 // all set and done
887 Complete = true;
888 if(Debug)
889 std::clog << "allDone: " << DestFile << "\n" << std::endl;
890 }
891 }
892 /*}}}*/
893 // AcqIndex::AcqIndex - Constructor /*{{{*/
894 // ---------------------------------------------------------------------
895 /* The package file is added to the queue and a second class is
896 instantiated to fetch the revision file */
897 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
898 string URI,string URIDesc,string ShortDesc,
899 HashStringList const &ExpectedHash)
900 : pkgAcqBaseIndex(Owner, 0, NULL, ExpectedHash, NULL), RealURI(URI)
901 {
902 AutoSelectCompression();
903 Init(URI, URIDesc, ShortDesc);
904
905 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
906 std::clog << "New pkgIndex with TransactionManager "
907 << TransactionManager << std::endl;
908 }
909 /*}}}*/
910 // AcqIndex::AcqIndex - Constructor /*{{{*/
911 // ---------------------------------------------------------------------
912 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
913 pkgAcqMetaBase *TransactionManager,
914 IndexTarget const *Target,
915 HashStringList const &ExpectedHash,
916 indexRecords *MetaIndexParser)
917 : pkgAcqBaseIndex(Owner, TransactionManager, Target, ExpectedHash,
918 MetaIndexParser), RealURI(Target->URI)
919 {
920 // autoselect the compression method
921 AutoSelectCompression();
922 Init(Target->URI, Target->Description, Target->ShortDesc);
923
924 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
925 std::clog << "New pkgIndex with TransactionManager "
926 << TransactionManager << std::endl;
927 }
928 /*}}}*/
929 // AcqIndex::AutoSelectCompression - Select compression /*{{{*/
930 // ---------------------------------------------------------------------
931 void pkgAcqIndex::AutoSelectCompression()
932 {
933 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
934 CompressionExtension = "";
935 if (ExpectedHashes.usable())
936 {
937 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
938 if (*t == "uncompressed" || MetaIndexParser->Exists(string(Target->MetaKey).append(".").append(*t)) == true)
939 CompressionExtension.append(*t).append(" ");
940 }
941 else
942 {
943 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
944 CompressionExtension.append(*t).append(" ");
945 }
946 if (CompressionExtension.empty() == false)
947 CompressionExtension.erase(CompressionExtension.end()-1);
948 }
949 // AcqIndex::Init - defered Constructor /*{{{*/
950 // ---------------------------------------------------------------------
951 void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
952 string const &ShortDesc)
953 {
954 Decompression = false;
955 Erase = false;
956
957 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
958 DestFile += URItoFileName(URI);
959
960 std::string const comprExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
961 if (comprExt == "uncompressed")
962 {
963 Desc.URI = URI;
964 if(Target)
965 MetaKey = string(Target->MetaKey);
966 }
967 else
968 {
969 Desc.URI = URI + '.' + comprExt;
970 if(Target)
971 MetaKey = string(Target->MetaKey) + '.' + comprExt;
972 }
973
974 // load the filesize
975 if(MetaIndexParser)
976 {
977 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
978 if(Record)
979 FileSize = Record->Size;
980
981 InitByHashIfNeeded(MetaKey);
982 }
983
984 Desc.Description = URIDesc;
985 Desc.Owner = this;
986 Desc.ShortDesc = ShortDesc;
987
988 QueueURI(Desc);
989 }
990 /*}}}*/
991 // AcqIndex::AdjustForByHash - modify URI for by-hash support /*{{{*/
992 // ---------------------------------------------------------------------
993 /* */
994 void pkgAcqIndex::InitByHashIfNeeded(const std::string MetaKey)
995 {
996 // TODO:
997 // - (maybe?) add support for by-hash into the sources.list as flag
998 // - make apt-ftparchive generate the hashes (and expire?)
999 std::string HostKnob = "APT::Acquire::" + ::URI(Desc.URI).Host + "::By-Hash";
1000 if(_config->FindB("APT::Acquire::By-Hash", false) == true ||
1001 _config->FindB(HostKnob, false) == true ||
1002 MetaIndexParser->GetSupportsAcquireByHash())
1003 {
1004 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1005 if(Record)
1006 {
1007 // FIXME: should we really use the best hash here? or a fixed one?
1008 const HashString *TargetHash = Record->Hashes.find("");
1009 std::string ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
1010 size_t trailing_slash = Desc.URI.find_last_of("/");
1011 Desc.URI = Desc.URI.replace(
1012 trailing_slash,
1013 Desc.URI.substr(trailing_slash+1).size()+1,
1014 ByHash);
1015 } else {
1016 _error->Warning(
1017 "Fetching ByHash requested but can not find record for %s",
1018 MetaKey.c_str());
1019 }
1020 }
1021 }
1022 /*}}}*/
1023 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1024 // ---------------------------------------------------------------------
1025 /* The only header we use is the last-modified header. */
1026 string pkgAcqIndex::Custom600Headers() const
1027 {
1028 string Final = GetFinalFilename();
1029
1030 string msg = "\nIndex-File: true";
1031 struct stat Buf;
1032 if (stat(Final.c_str(),&Buf) == 0)
1033 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1034
1035 return msg;
1036 }
1037 /*}}}*/
1038 // pkgAcqIndex::Failed - getting the indexfile failed /*{{{*/
1039 // ---------------------------------------------------------------------
1040 /* */
1041 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1042 {
1043 size_t const nextExt = CompressionExtension.find(' ');
1044 if (nextExt != std::string::npos)
1045 {
1046 CompressionExtension = CompressionExtension.substr(nextExt+1);
1047 Init(RealURI, Desc.Description, Desc.ShortDesc);
1048 return;
1049 }
1050
1051 // on decompression failure, remove bad versions in partial/
1052 if (Decompression && Erase) {
1053 string s = _config->FindDir("Dir::State::lists") + "partial/";
1054 s.append(URItoFileName(RealURI));
1055 unlink(s.c_str());
1056 }
1057
1058 Item::Failed(Message,Cnf);
1059
1060 /// cancel the entire transaction
1061 TransactionManager->AbortTransaction();
1062 }
1063 /*}}}*/
1064 // pkgAcqIndex::GetFinalFilename - Return the full final file path /*{{{*/
1065 // ---------------------------------------------------------------------
1066 /* */
1067 std::string pkgAcqIndex::GetFinalFilename() const
1068 {
1069 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1070 std::string FinalFile = _config->FindDir("Dir::State::lists");
1071 FinalFile += URItoFileName(RealURI);
1072 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
1073 FinalFile += ".gz";
1074 return FinalFile;
1075 }
1076 /*}}}*/
1077 // AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
1078 // ---------------------------------------------------------------------
1079 /* */
1080 void pkgAcqIndex::ReverifyAfterIMS()
1081 {
1082 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1083 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
1084 DestFile += ".gz";
1085
1086 // copy FinalFile into partial/ so that we check the hash again
1087 string FinalFile = GetFinalFilename();
1088 Decompression = true;
1089 Desc.URI = "copy:" + FinalFile;
1090 QueueURI(Desc);
1091 }
1092 /*}}}*/
1093 // AcqIndex::Done - Finished a fetch /*{{{*/
1094 // ---------------------------------------------------------------------
1095 /* This goes through a number of states.. On the initial fetch the
1096 method could possibly return an alternate filename which points
1097 to the uncompressed version of the file. If this is so the file
1098 is copied into the partial directory. In all other cases the file
1099 is decompressed with a compressed uri. */
1100 void pkgAcqIndex::Done(string Message, unsigned long long Size,
1101 HashStringList const &Hashes,
1102 pkgAcquire::MethodConfig *Cfg)
1103 {
1104 Item::Done(Message,Size,Hashes,Cfg);
1105 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1106
1107 if (Decompression == true)
1108 {
1109 if (ExpectedHashes.usable() && ExpectedHashes != Hashes)
1110 {
1111 Desc.URI = RealURI;
1112 RenameOnError(HashSumMismatch);
1113 printHashSumComparision(RealURI, ExpectedHashes, Hashes);
1114 Failed(Message, Cfg);
1115 return;
1116 }
1117
1118 // FIXME: this can go away once we only ever download stuff that
1119 // has a valid hash and we never do GET based probing
1120 //
1121 /* Always verify the index file for correctness (all indexes must
1122 * have a Package field) (LP: #346386) (Closes: #627642)
1123 */
1124 FileFd fd(DestFile, FileFd::ReadOnly, FileFd::Extension);
1125 // Only test for correctness if the content of the file is not empty
1126 // (empty is ok)
1127 if (fd.Size() > 0)
1128 {
1129 pkgTagSection sec;
1130 pkgTagFile tag(&fd);
1131
1132 // all our current indexes have a field 'Package' in each section
1133 if (_error->PendingError() == true || tag.Step(sec) == false || sec.Exists("Package") == false)
1134 {
1135 RenameOnError(InvalidFormat);
1136 Failed(Message, Cfg);
1137 return;
1138 }
1139 }
1140
1141 // FIXME: can we void the "Erase" bool here as its very non-local?
1142 std::string CompressedFile = _config->FindDir("Dir::State::lists") + "partial/";
1143 CompressedFile += URItoFileName(RealURI);
1144
1145 // Remove the compressed version.
1146 if (Erase == true)
1147 unlink(CompressedFile.c_str());
1148
1149 // Done, queue for rename on transaction finished
1150 PartialFile = DestFile;
1151 DestFile = GetFinalFilename();
1152
1153 return;
1154 }
1155
1156 // FIXME: use the same method to find
1157 // check the compressed hash too
1158 if(MetaKey != "" && Hashes.size() > 0)
1159 {
1160 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1161 if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
1162 {
1163 RenameOnError(HashSumMismatch);
1164 printHashSumComparision(RealURI, Record->Hashes, Hashes);
1165 Failed(Message, Cfg);
1166 return;
1167 }
1168 }
1169
1170 Erase = false;
1171 Complete = true;
1172
1173 // Handle the unzipd case
1174 string FileName = LookupTag(Message,"Alt-Filename");
1175 if (FileName.empty() == false)
1176 {
1177 Decompression = true;
1178 Local = true;
1179 DestFile += ".decomp";
1180 Desc.URI = "copy:" + FileName;
1181 QueueURI(Desc);
1182 Mode = "copy";
1183 return;
1184 }
1185
1186 FileName = LookupTag(Message,"Filename");
1187 if (FileName.empty() == true)
1188 {
1189 Status = StatError;
1190 ErrorText = "Method gave a blank filename";
1191 }
1192
1193 if (FileName == DestFile)
1194 Erase = true;
1195 else
1196 Local = true;
1197
1198 // do not reverify cdrom sources as apt-cdrom may rewrite the Packages
1199 // file when its doing the indexcopy
1200 if (RealURI.substr(0,6) == "cdrom:" &&
1201 StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1202 return;
1203
1204 // The files timestamp matches, reverify by copy into partial/
1205 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1206 {
1207 Erase = false;
1208 ReverifyAfterIMS();
1209 return;
1210 }
1211 string decompProg;
1212
1213 // If we enable compressed indexes, queue for hash verification
1214 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
1215 {
1216 DestFile = _config->FindDir("Dir::State::lists");
1217 DestFile += URItoFileName(RealURI) + ".gz";
1218
1219 Decompression = true;
1220 Desc.URI = "copy:" + FileName;
1221 QueueURI(Desc);
1222
1223 return;
1224 }
1225
1226 // get the binary name for your used compression type
1227 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
1228 if(decompProg.empty() == false);
1229 else if(compExt == "uncompressed")
1230 decompProg = "copy";
1231 else {
1232 _error->Error("Unsupported extension: %s", compExt.c_str());
1233 return;
1234 }
1235
1236 Decompression = true;
1237 DestFile += ".decomp";
1238 Desc.URI = decompProg + ":" + FileName;
1239 QueueURI(Desc);
1240
1241 // FIXME: this points to a c++ string that goes out of scope
1242 Mode = decompProg.c_str();
1243 }
1244 /*}}}*/
1245 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1246 // ---------------------------------------------------------------------
1247 /* The Translation file is added to the queue */
1248 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1249 string URI,string URIDesc,string ShortDesc)
1250 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList())
1251 {
1252 }
1253 /*}}}*/
1254 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1255 pkgAcqMetaBase *TransactionManager,
1256 IndexTarget const * const Target,
1257 HashStringList const &ExpectedHashes,
1258 indexRecords *MetaIndexParser)
1259 : pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser)
1260 {
1261 // load the filesize
1262 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1263 if(Record)
1264 FileSize = Record->Size;
1265 }
1266 /*}}}*/
1267 // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1268 // ---------------------------------------------------------------------
1269 string pkgAcqIndexTrans::Custom600Headers() const
1270 {
1271 string Final = GetFinalFilename();
1272
1273 struct stat Buf;
1274 if (stat(Final.c_str(),&Buf) != 0)
1275 return "\nFail-Ignore: true\nIndex-File: true";
1276 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1277 }
1278 /*}}}*/
1279 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1280 // ---------------------------------------------------------------------
1281 /* */
1282 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1283 {
1284 size_t const nextExt = CompressionExtension.find(' ');
1285 if (nextExt != std::string::npos)
1286 {
1287 CompressionExtension = CompressionExtension.substr(nextExt+1);
1288 Init(RealURI, Desc.Description, Desc.ShortDesc);
1289 Status = StatIdle;
1290 return;
1291 }
1292
1293 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1294 if (Cnf->LocalOnly == true ||
1295 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1296 {
1297 // Ignore this
1298 Status = StatDone;
1299 Complete = false;
1300 Dequeue();
1301 return;
1302 }
1303
1304 Item::Failed(Message,Cnf);
1305 }
1306 /*}}}*/
1307
1308 void pkgAcqMetaBase::Add(Item *I)
1309 {
1310 Transaction.push_back(I);
1311 }
1312
1313 void pkgAcqMetaBase::AbortTransaction()
1314 {
1315 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1316 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
1317
1318 // ensure the toplevel is in error state too
1319 Status = pkgAcquire::Item::StatError;
1320 for (std::vector<Item*>::iterator I = Transaction.begin();
1321 I != Transaction.end(); ++I)
1322 {
1323 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1324 std::clog << " Cancel: " << (*I)->DestFile << std::endl;
1325 // the transaction will abort, so stop anything that is idle
1326 if ((*I)->Status == pkgAcquire::Item::StatIdle)
1327 (*I)->Status = pkgAcquire::Item::StatDone;
1328 }
1329 }
1330 /*}}}*/
1331 bool pkgAcqMetaBase::TransactionHasError()
1332 {
1333 for (pkgAcquire::ItemIterator I = Transaction.begin();
1334 I != Transaction.end(); ++I)
1335 if((*I)->Status != pkgAcquire::Item::StatDone &&
1336 (*I)->Status != pkgAcquire::Item::StatIdle)
1337 return true;
1338
1339 return false;
1340 }
1341 // Acquire::CommitTransaction - Commit a transaction /*{{{*/
1342 void pkgAcqMetaBase::CommitTransaction()
1343 {
1344 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1345 std::clog << "CommitTransaction: " << this << std::endl;
1346
1347 // move new files into place *and* remove files that are not
1348 // part of the transaction but are still on disk
1349 for (std::vector<Item*>::iterator I = Transaction.begin();
1350 I != Transaction.end(); ++I)
1351 {
1352 if((*I)->PartialFile != "")
1353 {
1354 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1355 std::clog << "mv "
1356 << (*I)->PartialFile << " -> "
1357 << (*I)->DestFile << " "
1358 << (*I)->DescURI()
1359 << std::endl;
1360 Rename((*I)->PartialFile, (*I)->DestFile);
1361 chmod((*I)->DestFile.c_str(),0644);
1362 } else {
1363 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1364 std::clog << "rm "
1365 << (*I)->DestFile
1366 << " "
1367 << (*I)->DescURI()
1368 << std::endl;
1369 unlink((*I)->DestFile.c_str());
1370 }
1371 // mark that this transaction is finished
1372 (*I)->TransactionManager = 0;
1373 }
1374 }
1375
1376 /*{{{*/
1377 bool pkgAcqMetaBase::GenerateAuthWarning(const std::string &RealURI,
1378 const std::string &Message)
1379 {
1380 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1381
1382 if(FileExists(Final))
1383 {
1384 Status = StatTransientNetworkError;
1385 _error->Warning(_("An error occurred during the signature "
1386 "verification. The repository is not updated "
1387 "and the previous index files will be used. "
1388 "GPG error: %s: %s\n"),
1389 Desc.Description.c_str(),
1390 LookupTag(Message,"Message").c_str());
1391 RunScripts("APT::Update::Auth-Failure");
1392 return true;
1393 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1394 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1395 _error->Error(_("GPG error: %s: %s"),
1396 Desc.Description.c_str(),
1397 LookupTag(Message,"Message").c_str());
1398 Status = StatError;
1399 return true;
1400 } else {
1401 _error->Warning(_("GPG error: %s: %s"),
1402 Desc.Description.c_str(),
1403 LookupTag(Message,"Message").c_str());
1404 }
1405 // gpgv method failed
1406 ReportMirrorFailure("GPGFailure");
1407 return false;
1408 }
1409 /*}}}*/
1410
1411
1412 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
1413 pkgAcqMetaBase *TransactionManager,
1414 string URI,string URIDesc,string ShortDesc,
1415 string MetaIndexFile,
1416 const vector<IndexTarget*>* IndexTargets,
1417 indexRecords* MetaIndexParser) :
1418 pkgAcqMetaBase(Owner, HashStringList(), TransactionManager), RealURI(URI),
1419 MetaIndexParser(MetaIndexParser), MetaIndexFile(MetaIndexFile),
1420 URIDesc(URIDesc), ShortDesc(ShortDesc),
1421 IndexTargets(IndexTargets), AuthPass(false), IMSHit(false)
1422 {
1423 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1424 DestFile += URItoFileName(URI);
1425
1426 // remove any partial downloaded sig-file in partial/.
1427 // it may confuse proxies and is too small to warrant a
1428 // partial download anyway
1429 unlink(DestFile.c_str());
1430
1431 // set the TransactionManager
1432 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1433 std::clog << "New pkgAcqMetaSig with TransactionManager "
1434 << TransactionManager << std::endl;
1435
1436 // Create the item
1437 Desc.Description = URIDesc;
1438 Desc.Owner = this;
1439 Desc.ShortDesc = ShortDesc;
1440 Desc.URI = URI;
1441
1442 QueueURI(Desc);
1443 }
1444 /*}}}*/
1445 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1446 {
1447 }
1448 /*}}}*/
1449 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1450 // ---------------------------------------------------------------------
1451 /* The only header we use is the last-modified header. */
1452 string pkgAcqMetaSig::Custom600Headers() const
1453 {
1454 string FinalFile = _config->FindDir("Dir::State::lists");
1455 FinalFile += URItoFileName(RealURI);
1456
1457 struct stat Buf;
1458 if (stat(FinalFile.c_str(),&Buf) != 0)
1459 return "\nIndex-File: true";
1460
1461 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1462 }
1463
1464 void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList const &Hashes,
1465 pkgAcquire::MethodConfig *Cfg)
1466 {
1467 Item::Done(Message, Size, Hashes, Cfg);
1468
1469 string FileName = LookupTag(Message,"Filename");
1470 if (FileName.empty() == true)
1471 {
1472 Status = StatError;
1473 ErrorText = "Method gave a blank filename";
1474 return;
1475 }
1476
1477 if (FileName != DestFile)
1478 {
1479 // We have to copy it into place
1480 Local = true;
1481 Desc.URI = "copy:" + FileName;
1482 QueueURI(Desc);
1483 return;
1484 }
1485
1486 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1487 IMSHit = true;
1488
1489 // adjust paths if its a ims-hit
1490 if(IMSHit)
1491 {
1492 string FinalFile = _config->FindDir("Dir::State::lists");
1493 FinalFile += URItoFileName(RealURI);
1494
1495 DestFile = PartialFile = FinalFile;
1496 }
1497
1498 // queue for verify
1499 if(AuthPass == false)
1500 {
1501 AuthPass = true;
1502 Desc.URI = "gpgv:" + DestFile;
1503 DestFile = MetaIndexFile;
1504 QueueURI(Desc);
1505 return;
1506 }
1507
1508 // queue to copy the file in place if it was not a ims hit, on ims
1509 // hit the file is already at the right place
1510 if(IMSHit == false)
1511 {
1512 PartialFile = _config->FindDir("Dir::State::lists") + "partial/";
1513 PartialFile += URItoFileName(RealURI);
1514
1515 DestFile = _config->FindDir("Dir::State::lists");
1516 DestFile += URItoFileName(RealURI);
1517 }
1518
1519 // we parse the MetaIndexFile here because at this point we can
1520 // trust the data
1521 if(AuthPass == true)
1522 {
1523 // load indexes and queue further downloads
1524 MetaIndexParser->Load(MetaIndexFile);
1525 ((pkgAcqMetaIndex*)TransactionManager)->QueueIndexes(true);
1526 }
1527
1528 Complete = true;
1529 }
1530 /*}}}*/
1531 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1532 {
1533 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1534
1535 // FIXME: meh, this is not really elegant
1536 string InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
1537 "InRelease");
1538 string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
1539
1540 if(RealFileExists(Final) || RealFileExists(FinalInRelease))
1541 {
1542 _error->Error("The repository '%s' is no longer signed.",
1543 URIDesc.c_str());
1544 Rename(MetaIndexFile, MetaIndexFile+".FAILED");
1545 TransactionManager->AbortTransaction();
1546 return;
1547 }
1548
1549 // this ensures that any file in the lists/ dir is removed by the
1550 // transaction
1551 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1552 DestFile += URItoFileName(RealURI);
1553 PartialFile = "";
1554
1555 // FIXME: duplicated code from pkgAcqMetaIndex
1556 if (AuthPass == true)
1557 {
1558 bool Stop = GenerateAuthWarning(RealURI, Message);
1559 if(Stop)
1560 return;
1561 }
1562
1563 // only allow going further if the users explicitely wants it
1564 if(_config->FindB("APT::Get::AllowUnauthenticated", false))
1565 {
1566 _error->Warning("Please use --allow-unauthenticated");
1567 }
1568 else
1569 {
1570 // we parse the indexes here because at this point the user wanted
1571 // a repository that may potentially harm him
1572 MetaIndexParser->Load(MetaIndexFile);
1573 ((pkgAcqMetaIndex*)TransactionManager)->QueueIndexes(true);
1574 }
1575
1576 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1577 if (Cnf->LocalOnly == true ||
1578 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1579 {
1580 // Ignore this
1581 Status = StatDone;
1582 Complete = false;
1583 Dequeue();
1584 return;
1585 }
1586 Item::Failed(Message,Cnf);
1587 }
1588 /*}}}*/
1589 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1590 pkgAcqMetaBase *TransactionManager,
1591 string URI,string URIDesc,string ShortDesc,
1592 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
1593 const vector<IndexTarget*>* IndexTargets,
1594 indexRecords* MetaIndexParser) :
1595 pkgAcqMetaBase(Owner, HashStringList(), TransactionManager), RealURI(URI), IndexTargets(IndexTargets),
1596 URIDesc(URIDesc), ShortDesc(ShortDesc),
1597 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false),
1598 MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
1599 MetaIndexSigShortDesc(MetaIndexSigShortDesc)
1600 {
1601 if(TransactionManager == NULL)
1602 {
1603 this->TransactionManager = this;
1604 this->TransactionManager->Add(this);
1605 }
1606
1607 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1608 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1609 << this->TransactionManager << std::endl;
1610
1611
1612 Init(URIDesc, ShortDesc);
1613 }
1614 /*}}}*/
1615 // pkgAcqMetaIndex::Init - Delayed constructor /*{{{*/
1616 void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
1617 {
1618 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1619 DestFile += URItoFileName(RealURI);
1620
1621 // Create the item
1622 Desc.Description = URIDesc;
1623 Desc.Owner = this;
1624 Desc.ShortDesc = ShortDesc;
1625 Desc.URI = RealURI;
1626
1627 // we expect more item
1628 ExpectedAdditionalItems = IndexTargets->size();
1629 QueueURI(Desc);
1630 }
1631 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1632 // ---------------------------------------------------------------------
1633 /* The only header we use is the last-modified header. */
1634 string pkgAcqMetaIndex::Custom600Headers() const
1635 {
1636 string Final = _config->FindDir("Dir::State::lists");
1637 Final += URItoFileName(RealURI);
1638
1639 struct stat Buf;
1640 if (stat(Final.c_str(),&Buf) != 0)
1641 return "\nIndex-File: true";
1642
1643 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1644 }
1645 /*}}}*/
1646 void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
1647 pkgAcquire::MethodConfig *Cfg)
1648 {
1649 Item::Done(Message,Size,Hashes,Cfg);
1650
1651 // MetaIndexes are done in two passes: one to download the
1652 // metaindex with an appropriate method, and a second to verify it
1653 // with the gpgv method
1654
1655 if (AuthPass == true)
1656 {
1657 AuthDone(Message);
1658
1659 // all cool, move Release file into place
1660 Complete = true;
1661 }
1662 else
1663 {
1664 RetrievalDone(Message);
1665 if (!Complete)
1666 // Still more retrieving to do
1667 return;
1668
1669 if (SigFile != "")
1670 {
1671 // There was a signature file, so pass it to gpgv for
1672 // verification
1673 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1674 std::cerr << "Metaindex acquired, queueing gpg verification ("
1675 << SigFile << "," << DestFile << ")\n";
1676 AuthPass = true;
1677 Desc.URI = "gpgv:" + SigFile;
1678 QueueURI(Desc);
1679 Mode = "gpgv";
1680 return;
1681 }
1682 }
1683
1684 if (Complete == true)
1685 {
1686 string FinalFile = _config->FindDir("Dir::State::lists");
1687 FinalFile += URItoFileName(RealURI);
1688 if (SigFile == DestFile)
1689 SigFile = FinalFile;
1690 // queue for copy in place
1691 PartialFile = DestFile;
1692 DestFile = FinalFile;
1693 }
1694 }
1695 /*}}}*/
1696 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1697 {
1698 // We have just finished downloading a Release file (it is not
1699 // verified yet)
1700
1701 string FileName = LookupTag(Message,"Filename");
1702 if (FileName.empty() == true)
1703 {
1704 Status = StatError;
1705 ErrorText = "Method gave a blank filename";
1706 return;
1707 }
1708
1709 if (FileName != DestFile)
1710 {
1711 Local = true;
1712 Desc.URI = "copy:" + FileName;
1713 QueueURI(Desc);
1714 return;
1715 }
1716
1717 // make sure to verify against the right file on I-M-S hit
1718 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1719 if(IMSHit)
1720 {
1721 string FinalFile = _config->FindDir("Dir::State::lists");
1722 FinalFile += URItoFileName(RealURI);
1723 if (SigFile == DestFile)
1724 {
1725 SigFile = FinalFile;
1726 #if 0
1727 // constructor of pkgAcqMetaClearSig moved it out of the way,
1728 // now move it back in on IMS hit for the 'old' file
1729 string const OldClearSig = DestFile + ".reverify";
1730 if (RealFileExists(OldClearSig) == true)
1731 Rename(OldClearSig, FinalFile);
1732 #endif
1733 }
1734 DestFile = FinalFile;
1735 }
1736
1737 // queue a signature
1738 if(SigFile != DestFile)
1739 new pkgAcqMetaSig(Owner, TransactionManager,
1740 MetaIndexSigURI, MetaIndexSigURIDesc,
1741 MetaIndexSigShortDesc, DestFile, IndexTargets,
1742 MetaIndexParser);
1743
1744 Complete = true;
1745 }
1746 /*}}}*/
1747 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1748 {
1749 // At this point, the gpgv method has succeeded, so there is a
1750 // valid signature from a key in the trusted keyring. We
1751 // perform additional verification of its contents, and use them
1752 // to verify the indexes we are about to download
1753
1754 if (!MetaIndexParser->Load(DestFile))
1755 {
1756 Status = StatAuthError;
1757 ErrorText = MetaIndexParser->ErrorText;
1758 return;
1759 }
1760
1761 if (!VerifyVendor(Message))
1762 {
1763 return;
1764 }
1765
1766 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1767 std::cerr << "Signature verification succeeded: "
1768 << DestFile << std::endl;
1769
1770 // we ensure this by other means
1771 #if 0
1772 // do not trust any previously unverified content that we may have
1773 string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
1774 if (DestFile != SigFile)
1775 LastGoodSigFile.append(".gpg");
1776 LastGoodSigFile.append(".reverify");
1777 if(IMSHit == false && RealFileExists(LastGoodSigFile) == false)
1778 {
1779 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1780 Target != IndexTargets->end();
1781 ++Target)
1782 {
1783 // remove old indexes
1784 std::string index = _config->FindDir("Dir::State::lists") +
1785 URItoFileName((*Target)->URI);
1786 unlink(index.c_str());
1787 // and also old gzipindexes
1788 index += ".gz";
1789 unlink(index.c_str());
1790 }
1791 }
1792 #endif
1793
1794 // Download further indexes with verification
1795 //
1796 // we do not need to download indexfiles if the Release file has not
1797 // changed because without a changed release file there are no new hashes
1798 // and we ensure that the repository is always "complete" (i.e. all
1799 // that is in the release file is downloaded)
1800 if(IMSHit == false)
1801 QueueIndexes(true);
1802
1803 #if 0
1804 // is it a clearsigned MetaIndex file?
1805 if (DestFile == SigFile)
1806 return;
1807
1808 // Done, move signature file into position
1809 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1810 URItoFileName(RealURI) + ".gpg";
1811 Rename(SigFile,VerifiedSigFile);
1812 chmod(VerifiedSigFile.c_str(),0644);
1813 #endif
1814 }
1815 /*}}}*/
1816 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1817 {
1818 bool transInRelease = false;
1819 {
1820 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1821 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1822 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1823 if (k->find("Translation-") != std::string::npos)
1824 {
1825 transInRelease = true;
1826 break;
1827 }
1828 }
1829
1830 // at this point the real Items are loaded in the fetcher
1831 ExpectedAdditionalItems = 0;
1832 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1833 Target != IndexTargets->end();
1834 ++Target)
1835 {
1836 HashStringList ExpectedIndexHashes;
1837 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1838 bool compressedAvailable = false;
1839 if (Record == NULL)
1840 {
1841 if ((*Target)->IsOptional() == true)
1842 {
1843 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1844 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1845 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1846 {
1847 compressedAvailable = true;
1848 break;
1849 }
1850 }
1851 else if (verify == true)
1852 {
1853 Status = StatAuthError;
1854 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1855 return;
1856 }
1857 }
1858 else
1859 {
1860 ExpectedIndexHashes = Record->Hashes;
1861 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1862 {
1863 std::cerr << "Queueing: " << (*Target)->URI << std::endl
1864 << "Expected Hash:" << std::endl;
1865 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
1866 std::cerr << "\t- " << hs->toStr() << std::endl;
1867 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
1868 }
1869 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
1870 {
1871 Status = StatAuthError;
1872 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1873 return;
1874 }
1875 }
1876
1877 if ((*Target)->IsOptional() == true)
1878 {
1879 if (transInRelease == false || Record != NULL || compressedAvailable == true)
1880 {
1881 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
1882 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
1883 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
1884 else
1885 new pkgAcqIndexTrans(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
1886 }
1887 continue;
1888 }
1889
1890 /* Queue Packages file (either diff or full packages files, depending
1891 on the users option) - we also check if the PDiff Index file is listed
1892 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1893 instead, but passing the required info to it is to much hassle */
1894 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1895 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
1896 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
1897 else
1898 new pkgAcqIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
1899 }
1900 }
1901 /*}}}*/
1902 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1903 {
1904 string::size_type pos;
1905
1906 // check for missing sigs (that where not fatal because otherwise we had
1907 // bombed earlier)
1908 string missingkeys;
1909 string msg = _("There is no public key available for the "
1910 "following key IDs:\n");
1911 pos = Message.find("NO_PUBKEY ");
1912 if (pos != std::string::npos)
1913 {
1914 string::size_type start = pos+strlen("NO_PUBKEY ");
1915 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1916 missingkeys += (Fingerprint);
1917 }
1918 if(!missingkeys.empty())
1919 _error->Warning("%s", (msg + missingkeys).c_str());
1920
1921 string Transformed = MetaIndexParser->GetExpectedDist();
1922
1923 if (Transformed == "../project/experimental")
1924 {
1925 Transformed = "experimental";
1926 }
1927
1928 pos = Transformed.rfind('/');
1929 if (pos != string::npos)
1930 {
1931 Transformed = Transformed.substr(0, pos);
1932 }
1933
1934 if (Transformed == ".")
1935 {
1936 Transformed = "";
1937 }
1938
1939 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1940 MetaIndexParser->GetValidUntil() > 0) {
1941 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1942 if (invalid_since > 0)
1943 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1944 // the time since then the file is invalid - formated in the same way as in
1945 // the download progress display (e.g. 7d 3h 42min 1s)
1946 return _error->Error(
1947 _("Release file for %s is expired (invalid since %s). "
1948 "Updates for this repository will not be applied."),
1949 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1950 }
1951
1952 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1953 {
1954 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1955 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1956 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1957 }
1958
1959 if (MetaIndexParser->CheckDist(Transformed) == false)
1960 {
1961 // This might become fatal one day
1962 // Status = StatAuthError;
1963 // ErrorText = "Conflicting distribution; expected "
1964 // + MetaIndexParser->GetExpectedDist() + " but got "
1965 // + MetaIndexParser->GetDist();
1966 // return false;
1967 if (!Transformed.empty())
1968 {
1969 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1970 Desc.Description.c_str(),
1971 Transformed.c_str(),
1972 MetaIndexParser->GetDist().c_str());
1973 }
1974 }
1975
1976 return true;
1977 }
1978 /*}}}*/
1979 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1980 // ---------------------------------------------------------------------
1981 /* */
1982 void pkgAcqMetaIndex::Failed(string Message,
1983 pkgAcquire::MethodConfig * /*Cnf*/)
1984 {
1985 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1986
1987 if (AuthPass == true)
1988 {
1989 bool Stop = GenerateAuthWarning(RealURI, Message);
1990 if(Stop)
1991 return;
1992 }
1993
1994 /* Always move the meta index, even if gpgv failed. This ensures
1995 * that PackageFile objects are correctly filled in */
1996 if (FileExists(DestFile))
1997 {
1998 string FinalFile = _config->FindDir("Dir::State::lists");
1999 FinalFile += URItoFileName(RealURI);
2000 /* InRelease files become Release files, otherwise
2001 * they would be considered as trusted later on */
2002 if (SigFile == DestFile) {
2003 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
2004 "Release");
2005 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
2006 "Release");
2007 SigFile = FinalFile;
2008 }
2009
2010 // Done, queue for rename on transaction finished
2011 PartialFile = DestFile;
2012 DestFile = FinalFile;
2013 }
2014
2015 // No Release file was present, or verification failed, so fall
2016 // back to queueing Packages files without verification
2017 // only allow going further if the users explicitely wants it
2018 if(_config->FindB("APT::Get::AllowUnauthenticated", false))
2019 {
2020 // warn if the repository is unsinged
2021 _error->Warning(_("The data from '%s' is not signed. Packages "
2022 "from that repository can not be authenticated."),
2023 URIDesc.c_str());
2024 _error->Warning("Please use --allow-unauthenticated");
2025 } else {
2026 QueueIndexes(false);
2027 }
2028 }
2029 /*}}}*/
2030
2031 void pkgAcqMetaIndex::Finished()
2032 {
2033 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2034 std::clog << "Finished: " << DestFile <<std::endl;
2035 if(TransactionManager != NULL &&
2036 TransactionManager->TransactionHasError() == false)
2037 TransactionManager->CommitTransaction();
2038 }
2039
2040
2041 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
2042 string const &URI, string const &URIDesc, string const &ShortDesc,
2043 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
2044 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
2045 const vector<IndexTarget*>* IndexTargets,
2046 indexRecords* MetaIndexParser) :
2047 pkgAcqMetaIndex(Owner, NULL, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
2048 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
2049 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
2050 {
2051 SigFile = DestFile;
2052
2053 // index targets + (worst case:) Release/Release.gpg
2054 ExpectedAdditionalItems = IndexTargets->size() + 2;
2055
2056 #if 0
2057 // keep the old InRelease around in case of transistent network errors
2058 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2059 if (RealFileExists(Final) == true)
2060 {
2061 string const LastGoodSig = DestFile + ".reverify";
2062 Rename(Final,LastGoodSig);
2063 }
2064 #endif
2065 }
2066 /*}}}*/
2067 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
2068 {
2069 #if 0
2070 // if the file was never queued undo file-changes done in the constructor
2071 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
2072 {
2073 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2074 string const LastGoodSig = DestFile + ".reverify";
2075 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
2076 Rename(LastGoodSig, Final);
2077 }
2078 #endif
2079 }
2080 /*}}}*/
2081 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
2082 // ---------------------------------------------------------------------
2083 // FIXME: this can go away once the InRelease file is used widely
2084 string pkgAcqMetaClearSig::Custom600Headers() const
2085 {
2086 string Final = _config->FindDir("Dir::State::lists");
2087 Final += URItoFileName(RealURI);
2088
2089 struct stat Buf;
2090 if (stat(Final.c_str(),&Buf) != 0)
2091 {
2092 if (stat(Final.c_str(),&Buf) != 0)
2093 return "\nIndex-File: true\nFail-Ignore: true\n";
2094 }
2095
2096 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2097 }
2098 /*}}}*/
2099 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
2100 // ---------------------------------------------------------------------
2101 void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long Size,
2102 HashStringList const &Hashes,
2103 pkgAcquire::MethodConfig *Cnf)
2104 {
2105 // if we expect a ClearTextSignature (InRelase), ensure that
2106 // this is what we get and if not fail to queue a
2107 // Release/Release.gpg, see #346386
2108 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
2109 {
2110 pkgAcquire::Item::Failed(Message, Cnf);
2111 RenameOnError(NotClearsigned);
2112 TransactionManager->AbortTransaction();
2113 return;
2114 }
2115 pkgAcqMetaIndex::Done(Message, Size, Hashes, Cnf);
2116 }
2117 /*}}}*/
2118 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
2119 {
2120 // we failed, we will not get additional items from this method
2121 ExpectedAdditionalItems = 0;
2122
2123 if (AuthPass == false)
2124 {
2125 // Queue the 'old' InRelease file for removal if we try Release.gpg
2126 // as otherwise the file will stay around and gives a false-auth
2127 // impression (CVE-2012-0214)
2128 string FinalFile = _config->FindDir("Dir::State::lists");
2129 FinalFile.append(URItoFileName(RealURI));
2130 PartialFile = "";
2131 DestFile = FinalFile;
2132
2133 new pkgAcqMetaIndex(Owner, TransactionManager,
2134 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
2135 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
2136 IndexTargets, MetaIndexParser);
2137 if (Cnf->LocalOnly == true ||
2138 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
2139 Dequeue();
2140 }
2141 else
2142 pkgAcqMetaIndex::Failed(Message, Cnf);
2143 }
2144 /*}}}*/
2145 // AcqArchive::AcqArchive - Constructor /*{{{*/
2146 // ---------------------------------------------------------------------
2147 /* This just sets up the initial fetch environment and queues the first
2148 possibilitiy */
2149 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
2150 pkgRecords *Recs,pkgCache::VerIterator const &Version,
2151 string &StoreFilename) :
2152 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
2153 StoreFilename(StoreFilename), Vf(Version.FileList()),
2154 Trusted(false)
2155 {
2156 Retries = _config->FindI("Acquire::Retries",0);
2157
2158 if (Version.Arch() == 0)
2159 {
2160 _error->Error(_("I wasn't able to locate a file for the %s package. "
2161 "This might mean you need to manually fix this package. "
2162 "(due to missing arch)"),
2163 Version.ParentPkg().FullName().c_str());
2164 return;
2165 }
2166
2167 /* We need to find a filename to determine the extension. We make the
2168 assumption here that all the available sources for this version share
2169 the same extension.. */
2170 // Skip not source sources, they do not have file fields.
2171 for (; Vf.end() == false; ++Vf)
2172 {
2173 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2174 continue;
2175 break;
2176 }
2177
2178 // Does not really matter here.. we are going to fail out below
2179 if (Vf.end() != true)
2180 {
2181 // If this fails to get a file name we will bomb out below.
2182 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2183 if (_error->PendingError() == true)
2184 return;
2185
2186 // Generate the final file name as: package_version_arch.foo
2187 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2188 QuoteString(Version.VerStr(),"_:") + '_' +
2189 QuoteString(Version.Arch(),"_:.") +
2190 "." + flExtension(Parse.FileName());
2191 }
2192
2193 // check if we have one trusted source for the package. if so, switch
2194 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2195 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2196 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2197 bool seenUntrusted = false;
2198 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2199 {
2200 pkgIndexFile *Index;
2201 if (Sources->FindIndex(i.File(),Index) == false)
2202 continue;
2203
2204 if (debugAuth == true)
2205 std::cerr << "Checking index: " << Index->Describe()
2206 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2207
2208 if (Index->IsTrusted() == true)
2209 {
2210 Trusted = true;
2211 if (allowUnauth == false)
2212 break;
2213 }
2214 else
2215 seenUntrusted = true;
2216 }
2217
2218 // "allow-unauthenticated" restores apts old fetching behaviour
2219 // that means that e.g. unauthenticated file:// uris are higher
2220 // priority than authenticated http:// uris
2221 if (allowUnauth == true && seenUntrusted == true)
2222 Trusted = false;
2223
2224 // Select a source
2225 if (QueueNext() == false && _error->PendingError() == false)
2226 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2227 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2228 }
2229 /*}}}*/
2230 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2231 // ---------------------------------------------------------------------
2232 /* This queues the next available file version for download. It checks if
2233 the archive is already available in the cache and stashs the MD5 for
2234 checking later. */
2235 bool pkgAcqArchive::QueueNext()
2236 {
2237 for (; Vf.end() == false; ++Vf)
2238 {
2239 // Ignore not source sources
2240 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2241 continue;
2242
2243 // Try to cross match against the source list
2244 pkgIndexFile *Index;
2245 if (Sources->FindIndex(Vf.File(),Index) == false)
2246 continue;
2247
2248 // only try to get a trusted package from another source if that source
2249 // is also trusted
2250 if(Trusted && !Index->IsTrusted())
2251 continue;
2252
2253 // Grab the text package record
2254 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2255 if (_error->PendingError() == true)
2256 return false;
2257
2258 string PkgFile = Parse.FileName();
2259 ExpectedHashes = Parse.Hashes();
2260
2261 if (PkgFile.empty() == true)
2262 return _error->Error(_("The package index files are corrupted. No Filename: "
2263 "field for package %s."),
2264 Version.ParentPkg().Name());
2265
2266 Desc.URI = Index->ArchiveURI(PkgFile);
2267 Desc.Description = Index->ArchiveInfo(Version);
2268 Desc.Owner = this;
2269 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2270
2271 // See if we already have the file. (Legacy filenames)
2272 FileSize = Version->Size;
2273 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2274 struct stat Buf;
2275 if (stat(FinalFile.c_str(),&Buf) == 0)
2276 {
2277 // Make sure the size matches
2278 if ((unsigned long long)Buf.st_size == Version->Size)
2279 {
2280 Complete = true;
2281 Local = true;
2282 Status = StatDone;
2283 StoreFilename = DestFile = FinalFile;
2284 return true;
2285 }
2286
2287 /* Hmm, we have a file and its size does not match, this means it is
2288 an old style mismatched arch */
2289 unlink(FinalFile.c_str());
2290 }
2291
2292 // Check it again using the new style output filenames
2293 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2294 if (stat(FinalFile.c_str(),&Buf) == 0)
2295 {
2296 // Make sure the size matches
2297 if ((unsigned long long)Buf.st_size == Version->Size)
2298 {
2299 Complete = true;
2300 Local = true;
2301 Status = StatDone;
2302 StoreFilename = DestFile = FinalFile;
2303 return true;
2304 }
2305
2306 /* Hmm, we have a file and its size does not match, this shouldn't
2307 happen.. */
2308 unlink(FinalFile.c_str());
2309 }
2310
2311 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2312
2313 // Check the destination file
2314 if (stat(DestFile.c_str(),&Buf) == 0)
2315 {
2316 // Hmm, the partial file is too big, erase it
2317 if ((unsigned long long)Buf.st_size > Version->Size)
2318 unlink(DestFile.c_str());
2319 else
2320 PartialSize = Buf.st_size;
2321 }
2322
2323 // Disables download of archives - useful if no real installation follows,
2324 // e.g. if we are just interested in proposed installation order
2325 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2326 {
2327 Complete = true;
2328 Local = true;
2329 Status = StatDone;
2330 StoreFilename = DestFile = FinalFile;
2331 return true;
2332 }
2333
2334 // Create the item
2335 Local = false;
2336 QueueURI(Desc);
2337
2338 ++Vf;
2339 return true;
2340 }
2341 return false;
2342 }
2343 /*}}}*/
2344 // AcqArchive::Done - Finished fetching /*{{{*/
2345 // ---------------------------------------------------------------------
2346 /* */
2347 void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2348 pkgAcquire::MethodConfig *Cfg)
2349 {
2350 Item::Done(Message, Size, CalcHashes, Cfg);
2351
2352 // Check the size
2353 if (Size != Version->Size)
2354 {
2355 RenameOnError(SizeMismatch);
2356 return;
2357 }
2358
2359 // FIXME: could this empty() check impose *any* sort of security issue?
2360 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2361 {
2362 RenameOnError(HashSumMismatch);
2363 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2364 return;
2365 }
2366
2367 // Grab the output filename
2368 string FileName = LookupTag(Message,"Filename");
2369 if (FileName.empty() == true)
2370 {
2371 Status = StatError;
2372 ErrorText = "Method gave a blank filename";
2373 return;
2374 }
2375
2376 Complete = true;
2377
2378 // Reference filename
2379 if (FileName != DestFile)
2380 {
2381 StoreFilename = DestFile = FileName;
2382 Local = true;
2383 return;
2384 }
2385
2386 // Done, move it into position
2387 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2388 FinalFile += flNotDir(StoreFilename);
2389 Rename(DestFile,FinalFile);
2390
2391 StoreFilename = DestFile = FinalFile;
2392 Complete = true;
2393 }
2394 /*}}}*/
2395 // AcqArchive::Failed - Failure handler /*{{{*/
2396 // ---------------------------------------------------------------------
2397 /* Here we try other sources */
2398 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2399 {
2400 ErrorText = LookupTag(Message,"Message");
2401
2402 /* We don't really want to retry on failed media swaps, this prevents
2403 that. An interesting observation is that permanent failures are not
2404 recorded. */
2405 if (Cnf->Removable == true &&
2406 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2407 {
2408 // Vf = Version.FileList();
2409 while (Vf.end() == false) ++Vf;
2410 StoreFilename = string();
2411 Item::Failed(Message,Cnf);
2412 return;
2413 }
2414
2415 if (QueueNext() == false)
2416 {
2417 // This is the retry counter
2418 if (Retries != 0 &&
2419 Cnf->LocalOnly == false &&
2420 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2421 {
2422 Retries--;
2423 Vf = Version.FileList();
2424 if (QueueNext() == true)
2425 return;
2426 }
2427
2428 StoreFilename = string();
2429 Item::Failed(Message,Cnf);
2430 }
2431 }
2432 /*}}}*/
2433 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2434 // ---------------------------------------------------------------------
2435 APT_PURE bool pkgAcqArchive::IsTrusted() const
2436 {
2437 return Trusted;
2438 }
2439 /*}}}*/
2440 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2441 // ---------------------------------------------------------------------
2442 /* */
2443 void pkgAcqArchive::Finished()
2444 {
2445 if (Status == pkgAcquire::Item::StatDone &&
2446 Complete == true)
2447 return;
2448 StoreFilename = string();
2449 }
2450 /*}}}*/
2451 // AcqFile::pkgAcqFile - Constructor /*{{{*/
2452 // ---------------------------------------------------------------------
2453 /* The file is added to the queue */
2454 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2455 unsigned long long Size,string Dsc,string ShortDesc,
2456 const string &DestDir, const string &DestFilename,
2457 bool IsIndexFile) :
2458 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2459 {
2460 Retries = _config->FindI("Acquire::Retries",0);
2461
2462 if(!DestFilename.empty())
2463 DestFile = DestFilename;
2464 else if(!DestDir.empty())
2465 DestFile = DestDir + "/" + flNotDir(URI);
2466 else
2467 DestFile = flNotDir(URI);
2468
2469 // Create the item
2470 Desc.URI = URI;
2471 Desc.Description = Dsc;
2472 Desc.Owner = this;
2473
2474 // Set the short description to the archive component
2475 Desc.ShortDesc = ShortDesc;
2476
2477 // Get the transfer sizes
2478 FileSize = Size;
2479 struct stat Buf;
2480 if (stat(DestFile.c_str(),&Buf) == 0)
2481 {
2482 // Hmm, the partial file is too big, erase it
2483 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2484 unlink(DestFile.c_str());
2485 else
2486 PartialSize = Buf.st_size;
2487 }
2488
2489 QueueURI(Desc);
2490 }
2491 /*}}}*/
2492 // AcqFile::Done - Item downloaded OK /*{{{*/
2493 // ---------------------------------------------------------------------
2494 /* */
2495 void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2496 pkgAcquire::MethodConfig *Cnf)
2497 {
2498 Item::Done(Message,Size,CalcHashes,Cnf);
2499
2500 // Check the hash
2501 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2502 {
2503 RenameOnError(HashSumMismatch);
2504 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2505 return;
2506 }
2507
2508 string FileName = LookupTag(Message,"Filename");
2509 if (FileName.empty() == true)
2510 {
2511 Status = StatError;
2512 ErrorText = "Method gave a blank filename";
2513 return;
2514 }
2515
2516 Complete = true;
2517
2518 // The files timestamp matches
2519 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2520 return;
2521
2522 // We have to copy it into place
2523 if (FileName != DestFile)
2524 {
2525 Local = true;
2526 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2527 Cnf->Removable == true)
2528 {
2529 Desc.URI = "copy:" + FileName;
2530 QueueURI(Desc);
2531 return;
2532 }
2533
2534 // Erase the file if it is a symlink so we can overwrite it
2535 struct stat St;
2536 if (lstat(DestFile.c_str(),&St) == 0)
2537 {
2538 if (S_ISLNK(St.st_mode) != 0)
2539 unlink(DestFile.c_str());
2540 }
2541
2542 // Symlink the file
2543 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2544 {
2545 ErrorText = "Link to " + DestFile + " failure ";
2546 Status = StatError;
2547 Complete = false;
2548 }
2549 }
2550 }
2551 /*}}}*/
2552 // AcqFile::Failed - Failure handler /*{{{*/
2553 // ---------------------------------------------------------------------
2554 /* Here we try other sources */
2555 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2556 {
2557 ErrorText = LookupTag(Message,"Message");
2558
2559 // This is the retry counter
2560 if (Retries != 0 &&
2561 Cnf->LocalOnly == false &&
2562 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2563 {
2564 Retries--;
2565 QueueURI(Desc);
2566 return;
2567 }
2568
2569 Item::Failed(Message,Cnf);
2570 }
2571 /*}}}*/
2572 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2573 // ---------------------------------------------------------------------
2574 /* The only header we use is the last-modified header. */
2575 string pkgAcqFile::Custom600Headers() const
2576 {
2577 if (IsIndexFile)
2578 return "\nIndex-File: true";
2579 return "";
2580 }
2581 /*}}}*/