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