]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
add verify for the .diff/Index download and add FIXME for pkgAcqIndexDiffs/pkgAcqMerg...
[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
1196 // Remove the compressed version.
1197 if (Erase == true)
1198 unlink(CompressedFile.c_str());
1199
1200 // Done, queue for rename on transaction finished
1201 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1202
1203 return;
1204 }
1205
1206 // FIXME: use the same method to find
1207 // check the compressed hash too
1208 if(MetaKey != "" && Hashes.size() > 0)
1209 {
1210 indexRecords::checkSum *Record = MetaIndexParser->Lookup(MetaKey);
1211 if(Record && Record->Hashes.usable() && Hashes != Record->Hashes)
1212 {
1213 RenameOnError(HashSumMismatch);
1214 printHashSumComparision(RealURI, Record->Hashes, Hashes);
1215 Failed(Message, Cfg);
1216 return;
1217 }
1218 }
1219
1220 Erase = false;
1221 Complete = true;
1222
1223 // Handle the unzipd case
1224 string FileName = LookupTag(Message,"Alt-Filename");
1225 if (FileName.empty() == false)
1226 {
1227 Decompression = true;
1228 Local = true;
1229 DestFile += ".decomp";
1230 Desc.URI = "copy:" + FileName;
1231 QueueURI(Desc);
1232 ActiveSubprocess = "copy";
1233 #if __GNUC__ >= 4
1234 #pragma GCC diagnostic push
1235 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1236 #endif
1237 Mode = "copy";
1238 #if __GNUC__ >= 4
1239 #pragma GCC diagnostic pop
1240 #endif
1241 return;
1242 }
1243
1244 FileName = LookupTag(Message,"Filename");
1245 if (FileName.empty() == true)
1246 {
1247 Status = StatError;
1248 ErrorText = "Method gave a blank filename";
1249 }
1250
1251 if (FileName == DestFile)
1252 Erase = true;
1253 else
1254 Local = true;
1255
1256 // do not reverify cdrom sources as apt-cdrom may rewrite the Packages
1257 // file when its doing the indexcopy
1258 if (RealURI.substr(0,6) == "cdrom:" &&
1259 StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1260 return;
1261
1262 // The files timestamp matches, reverify by copy into partial/
1263 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1264 {
1265 Erase = false;
1266 ReverifyAfterIMS();
1267 #if 0 // ???
1268 // set destfile to the final destfile
1269 if(_config->FindB("Acquire::GzipIndexes",false) == false)
1270 {
1271 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1272 DestFile += URItoFileName(RealURI);
1273 }
1274
1275 ReverifyAfterIMS(FileName);
1276 #endif
1277 return;
1278 }
1279 string decompProg;
1280
1281 // If we enable compressed indexes, queue for hash verification
1282 if (_config->FindB("Acquire::GzipIndexes",false))
1283 {
1284 DestFile = _config->FindDir("Dir::State::lists");
1285 DestFile += URItoFileName(RealURI) + '.' + compExt;
1286
1287 Decompression = true;
1288 Desc.URI = "copy:" + FileName;
1289 QueueURI(Desc);
1290
1291 return;
1292 }
1293
1294 // get the binary name for your used compression type
1295 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
1296 if(decompProg.empty() == false);
1297 else if(compExt == "uncompressed")
1298 decompProg = "copy";
1299 else {
1300 _error->Error("Unsupported extension: %s", compExt.c_str());
1301 return;
1302 }
1303
1304 Decompression = true;
1305 DestFile += ".decomp";
1306 Desc.URI = decompProg + ":" + FileName;
1307 QueueURI(Desc);
1308
1309 ActiveSubprocess = decompProg;
1310 #if __GNUC__ >= 4
1311 #pragma GCC diagnostic push
1312 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1313 #endif
1314 Mode = ActiveSubprocess.c_str();
1315 #if __GNUC__ >= 4
1316 #pragma GCC diagnostic pop
1317 #endif
1318 }
1319 /*}}}*/
1320 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1321 // ---------------------------------------------------------------------
1322 /* The Translation file is added to the queue */
1323 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1324 string URI,string URIDesc,string ShortDesc)
1325 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList())
1326 {
1327 }
1328 /*}}}*/
1329 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1330 pkgAcqMetaBase *TransactionManager,
1331 IndexTarget const * const Target,
1332 HashStringList const &ExpectedHashes,
1333 indexRecords *MetaIndexParser)
1334 : pkgAcqIndex(Owner, TransactionManager, Target, ExpectedHashes, MetaIndexParser)
1335 {
1336 // load the filesize
1337 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1338 if(Record)
1339 FileSize = Record->Size;
1340 }
1341 /*}}}*/
1342 // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1343 // ---------------------------------------------------------------------
1344 string pkgAcqIndexTrans::Custom600Headers() const
1345 {
1346 string Final = GetFinalFilename();
1347
1348 struct stat Buf;
1349 if (stat(Final.c_str(),&Buf) != 0)
1350 return "\nFail-Ignore: true\nIndex-File: true";
1351 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1352 }
1353 /*}}}*/
1354 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1355 // ---------------------------------------------------------------------
1356 /* */
1357 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1358 {
1359 size_t const nextExt = CompressionExtension.find(' ');
1360 if (nextExt != std::string::npos)
1361 {
1362 CompressionExtension = CompressionExtension.substr(nextExt+1);
1363 Init(RealURI, Desc.Description, Desc.ShortDesc);
1364 Status = StatIdle;
1365 return;
1366 }
1367
1368 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1369 if (Cnf->LocalOnly == true ||
1370 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1371 {
1372 // Ignore this
1373 Status = StatDone;
1374 Complete = false;
1375 Dequeue();
1376 return;
1377 }
1378
1379 Item::Failed(Message,Cnf);
1380 }
1381 /*}}}*/
1382
1383 void pkgAcqMetaBase::Add(Item *I)
1384 {
1385 Transaction.push_back(I);
1386 }
1387
1388 void pkgAcqMetaBase::AbortTransaction()
1389 {
1390 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1391 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
1392
1393 // ensure the toplevel is in error state too
1394 for (std::vector<Item*>::iterator I = Transaction.begin();
1395 I != Transaction.end(); ++I)
1396 {
1397 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1398 std::clog << " Cancel: " << (*I)->DestFile << std::endl;
1399 // the transaction will abort, so stop anything that is idle
1400 if ((*I)->Status == pkgAcquire::Item::StatIdle)
1401 (*I)->Status = pkgAcquire::Item::StatDone;
1402 }
1403 }
1404 /*}}}*/
1405 bool pkgAcqMetaBase::TransactionHasError()
1406 {
1407 for (pkgAcquire::ItemIterator I = Transaction.begin();
1408 I != Transaction.end(); ++I)
1409 if((*I)->Status != pkgAcquire::Item::StatDone &&
1410 (*I)->Status != pkgAcquire::Item::StatIdle)
1411 return true;
1412
1413 return false;
1414 }
1415 // Acquire::CommitTransaction - Commit a transaction /*{{{*/
1416 void pkgAcqMetaBase::CommitTransaction()
1417 {
1418 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1419 std::clog << "CommitTransaction: " << this << std::endl;
1420
1421 // move new files into place *and* remove files that are not
1422 // part of the transaction but are still on disk
1423 for (std::vector<Item*>::iterator I = Transaction.begin();
1424 I != Transaction.end(); ++I)
1425 {
1426 if((*I)->PartialFile != "")
1427 {
1428 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1429 std::clog << "mv "
1430 << (*I)->PartialFile << " -> "
1431 << (*I)->DestFile << " "
1432 << (*I)->DescURI()
1433 << std::endl;
1434 Rename((*I)->PartialFile, (*I)->DestFile);
1435 chmod((*I)->DestFile.c_str(),0644);
1436 } else {
1437 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1438 std::clog << "rm "
1439 << (*I)->DestFile
1440 << " "
1441 << (*I)->DescURI()
1442 << std::endl;
1443 unlink((*I)->DestFile.c_str());
1444 }
1445 // mark that this transaction is finished
1446 (*I)->TransactionManager = 0;
1447 }
1448 }
1449
1450 void pkgAcqMetaBase::TransactionStageCopy(Item *I,
1451 const std::string &From,
1452 const std::string &To)
1453 {
1454 I->PartialFile = From;
1455 I->DestFile = To;
1456 }
1457
1458 void pkgAcqMetaBase::TransactionStageRemoval(Item *I,
1459 const std::string &FinalFile)
1460 {
1461 I->PartialFile = "";
1462 I->DestFile = FinalFile;
1463 }
1464
1465
1466 /*{{{*/
1467 bool pkgAcqMetaBase::GenerateAuthWarning(const std::string &RealURI,
1468 const std::string &Message)
1469 {
1470 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1471
1472 if(FileExists(Final))
1473 {
1474 Status = StatTransientNetworkError;
1475 _error->Warning(_("An error occurred during the signature "
1476 "verification. The repository is not updated "
1477 "and the previous index files will be used. "
1478 "GPG error: %s: %s\n"),
1479 Desc.Description.c_str(),
1480 LookupTag(Message,"Message").c_str());
1481 RunScripts("APT::Update::Auth-Failure");
1482 return true;
1483 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1484 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1485 _error->Error(_("GPG error: %s: %s"),
1486 Desc.Description.c_str(),
1487 LookupTag(Message,"Message").c_str());
1488 Status = StatError;
1489 return true;
1490 } else {
1491 _error->Warning(_("GPG error: %s: %s"),
1492 Desc.Description.c_str(),
1493 LookupTag(Message,"Message").c_str());
1494 }
1495 // gpgv method failed
1496 ReportMirrorFailure("GPGFailure");
1497 return false;
1498 }
1499 /*}}}*/
1500
1501
1502 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
1503 pkgAcqMetaBase *TransactionManager,
1504 string URI,string URIDesc,string ShortDesc,
1505 string MetaIndexFile,
1506 const vector<IndexTarget*>* IndexTargets,
1507 indexRecords* MetaIndexParser) :
1508 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser,
1509 HashStringList(), TransactionManager),
1510 RealURI(URI), MetaIndexFile(MetaIndexFile), URIDesc(URIDesc),
1511 ShortDesc(ShortDesc)
1512 {
1513 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1514 DestFile += URItoFileName(URI);
1515
1516 // remove any partial downloaded sig-file in partial/.
1517 // it may confuse proxies and is too small to warrant a
1518 // partial download anyway
1519 unlink(DestFile.c_str());
1520
1521 // set the TransactionManager
1522 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1523 std::clog << "New pkgAcqMetaSig with TransactionManager "
1524 << TransactionManager << std::endl;
1525
1526 // Create the item
1527 Desc.Description = URIDesc;
1528 Desc.Owner = this;
1529 Desc.ShortDesc = ShortDesc;
1530 Desc.URI = URI;
1531
1532 QueueURI(Desc);
1533 }
1534 /*}}}*/
1535 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1536 {
1537 }
1538 /*}}}*/
1539 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1540 // ---------------------------------------------------------------------
1541 /* The only header we use is the last-modified header. */
1542 string pkgAcqMetaSig::Custom600Headers() const
1543 {
1544 string FinalFile = _config->FindDir("Dir::State::lists");
1545 FinalFile += URItoFileName(RealURI);
1546
1547 struct stat Buf;
1548 if (stat(FinalFile.c_str(),&Buf) != 0)
1549 return "\nIndex-File: true";
1550
1551 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1552 }
1553
1554 void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList const &Hashes,
1555 pkgAcquire::MethodConfig *Cfg)
1556 {
1557 Item::Done(Message, Size, Hashes, Cfg);
1558
1559 string FileName = LookupTag(Message,"Filename");
1560 if (FileName.empty() == true)
1561 {
1562 Status = StatError;
1563 ErrorText = "Method gave a blank filename";
1564 return;
1565 }
1566
1567 if (FileName != DestFile)
1568 {
1569 // We have to copy it into place
1570 Local = true;
1571 Desc.URI = "copy:" + FileName;
1572 QueueURI(Desc);
1573 return;
1574 }
1575
1576 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1577 IMSHit = true;
1578
1579 // adjust paths if its a ims-hit
1580 if(IMSHit)
1581 {
1582 string FinalFile = _config->FindDir("Dir::State::lists");
1583 FinalFile += URItoFileName(RealURI);
1584
1585 TransactionManager->TransactionStageCopy(this, FinalFile, FinalFile);
1586 }
1587
1588 // queue for verify
1589 if(AuthPass == false)
1590 {
1591 AuthPass = true;
1592 Desc.URI = "gpgv:" + DestFile;
1593 DestFile = MetaIndexFile;
1594 QueueURI(Desc);
1595 return;
1596 }
1597
1598 // queue to copy the file in place if it was not a ims hit, on ims
1599 // hit the file is already at the right place
1600 if(IMSHit == false)
1601 {
1602 PartialFile = _config->FindDir("Dir::State::lists") + "partial/";
1603 PartialFile += URItoFileName(RealURI);
1604
1605 std::string FinalFile = _config->FindDir("Dir::State::lists");
1606 FinalFile += URItoFileName(RealURI);
1607
1608 TransactionManager->TransactionStageCopy(this, PartialFile, FinalFile);
1609 }
1610
1611 // we parse the MetaIndexFile here because at this point we can
1612 // trust the data
1613 if(AuthPass == true)
1614 {
1615 // load indexes and queue further downloads
1616 MetaIndexParser->Load(MetaIndexFile);
1617 QueueIndexes(true);
1618 }
1619
1620 Complete = true;
1621 }
1622 /*}}}*/
1623 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1624 {
1625 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1626
1627 // FIXME: meh, this is not really elegant
1628 string InReleaseURI = RealURI.replace(RealURI.rfind("Release.gpg"), 12,
1629 "InRelease");
1630 string FinalInRelease = _config->FindDir("Dir::State::lists") + URItoFileName(InReleaseURI);
1631
1632 if(RealFileExists(Final) || RealFileExists(FinalInRelease))
1633 {
1634 _error->Error("The repository '%s' is no longer signed.",
1635 URIDesc.c_str());
1636 Rename(MetaIndexFile, MetaIndexFile+".FAILED");
1637 Status = pkgAcquire::Item::StatError;
1638 TransactionManager->AbortTransaction();
1639 return;
1640 }
1641
1642 // this ensures that any file in the lists/ dir is removed by the
1643 // transaction
1644 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1645 DestFile += URItoFileName(RealURI);
1646 TransactionManager->TransactionStageRemoval(this, DestFile);
1647
1648 // FIXME: duplicated code from pkgAcqMetaIndex
1649 if (AuthPass == true)
1650 {
1651 bool Stop = GenerateAuthWarning(RealURI, Message);
1652 if(Stop)
1653 return;
1654 }
1655
1656 // only allow going further if the users explicitely wants it
1657 if(_config->FindB("APT::Get::AllowUnauthenticated", false) == true)
1658 {
1659 // we parse the indexes here because at this point the user wanted
1660 // a repository that may potentially harm him
1661 MetaIndexParser->Load(MetaIndexFile);
1662 QueueIndexes(true);
1663 }
1664 else
1665 {
1666 _error->Warning("Use --allow-unauthenticated to force the update");
1667 }
1668
1669 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1670 if (Cnf->LocalOnly == true ||
1671 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1672 {
1673 // Ignore this
1674 Status = StatDone;
1675 Complete = false;
1676 Dequeue();
1677 return;
1678 }
1679 Item::Failed(Message,Cnf);
1680 }
1681 /*}}}*/
1682 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1683 pkgAcqMetaBase *TransactionManager,
1684 string URI,string URIDesc,string ShortDesc,
1685 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
1686 const vector<IndexTarget*>* IndexTargets,
1687 indexRecords* MetaIndexParser) :
1688 pkgAcqMetaBase(Owner, IndexTargets, MetaIndexParser, HashStringList(),
1689 TransactionManager),
1690 RealURI(URI), URIDesc(URIDesc), ShortDesc(ShortDesc),
1691 MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
1692 MetaIndexSigShortDesc(MetaIndexSigShortDesc)
1693 {
1694 if(TransactionManager == NULL)
1695 {
1696 this->TransactionManager = this;
1697 this->TransactionManager->Add(this);
1698 }
1699
1700 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1701 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1702 << this->TransactionManager << std::endl;
1703
1704
1705 Init(URIDesc, ShortDesc);
1706 }
1707 /*}}}*/
1708 // pkgAcqMetaIndex::Init - Delayed constructor /*{{{*/
1709 void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
1710 {
1711 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1712 DestFile += URItoFileName(RealURI);
1713
1714 // Create the item
1715 Desc.Description = URIDesc;
1716 Desc.Owner = this;
1717 Desc.ShortDesc = ShortDesc;
1718 Desc.URI = RealURI;
1719
1720 // we expect more item
1721 ExpectedAdditionalItems = IndexTargets->size();
1722 QueueURI(Desc);
1723 }
1724 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1725 // ---------------------------------------------------------------------
1726 /* The only header we use is the last-modified header. */
1727 string pkgAcqMetaIndex::Custom600Headers() const
1728 {
1729 string Final = _config->FindDir("Dir::State::lists");
1730 Final += URItoFileName(RealURI);
1731
1732 struct stat Buf;
1733 if (stat(Final.c_str(),&Buf) != 0)
1734 return "\nIndex-File: true";
1735
1736 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1737 }
1738 /*}}}*/
1739 void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
1740 pkgAcquire::MethodConfig *Cfg)
1741 {
1742 Item::Done(Message,Size,Hashes,Cfg);
1743
1744 // MetaIndexes are done in two passes: one to download the
1745 // metaindex with an appropriate method, and a second to verify it
1746 // with the gpgv method
1747
1748 if (AuthPass == true)
1749 {
1750 AuthDone(Message);
1751
1752 // all cool, move Release file into place
1753 Complete = true;
1754 }
1755 else
1756 {
1757 RetrievalDone(Message);
1758 if (!Complete)
1759 // Still more retrieving to do
1760 return;
1761
1762 if (SigFile != "")
1763 {
1764 // There was a signature file, so pass it to gpgv for
1765 // verification
1766 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1767 std::cerr << "Metaindex acquired, queueing gpg verification ("
1768 << SigFile << "," << DestFile << ")\n";
1769 AuthPass = true;
1770 Desc.URI = "gpgv:" + SigFile;
1771 QueueURI(Desc);
1772 ActiveSubprocess = "gpgv";
1773 #if __GNUC__ >= 4
1774 #pragma GCC diagnostic push
1775 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1776 #endif
1777 Mode = "gpgv";
1778 #if __GNUC__ >= 4
1779 #pragma GCC diagnostic pop
1780 #endif
1781 return;
1782 }
1783 }
1784
1785 if (Complete == true)
1786 {
1787 string FinalFile = _config->FindDir("Dir::State::lists");
1788 FinalFile += URItoFileName(RealURI);
1789 if (SigFile == DestFile)
1790 SigFile = FinalFile;
1791
1792 // queue for copy in place
1793 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
1794 }
1795 }
1796 /*}}}*/
1797 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1798 {
1799 // We have just finished downloading a Release file (it is not
1800 // verified yet)
1801
1802 string FileName = LookupTag(Message,"Filename");
1803 if (FileName.empty() == true)
1804 {
1805 Status = StatError;
1806 ErrorText = "Method gave a blank filename";
1807 return;
1808 }
1809
1810 if (FileName != DestFile)
1811 {
1812 Local = true;
1813 Desc.URI = "copy:" + FileName;
1814 QueueURI(Desc);
1815 return;
1816 }
1817
1818 // make sure to verify against the right file on I-M-S hit
1819 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1820 if(IMSHit)
1821 {
1822 string FinalFile = _config->FindDir("Dir::State::lists");
1823 FinalFile += URItoFileName(RealURI);
1824 if (SigFile == DestFile)
1825 {
1826 SigFile = FinalFile;
1827 #if 0
1828 // constructor of pkgAcqMetaClearSig moved it out of the way,
1829 // now move it back in on IMS hit for the 'old' file
1830 string const OldClearSig = DestFile + ".reverify";
1831 if (RealFileExists(OldClearSig) == true)
1832 Rename(OldClearSig, FinalFile);
1833 #endif
1834 }
1835 DestFile = FinalFile;
1836 }
1837
1838 // queue a signature
1839 if(SigFile != DestFile)
1840 new pkgAcqMetaSig(Owner, TransactionManager,
1841 MetaIndexSigURI, MetaIndexSigURIDesc,
1842 MetaIndexSigShortDesc, DestFile, IndexTargets,
1843 MetaIndexParser);
1844
1845 Complete = true;
1846 }
1847 /*}}}*/
1848 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1849 {
1850 // At this point, the gpgv method has succeeded, so there is a
1851 // valid signature from a key in the trusted keyring. We
1852 // perform additional verification of its contents, and use them
1853 // to verify the indexes we are about to download
1854
1855 if (!MetaIndexParser->Load(DestFile))
1856 {
1857 Status = StatAuthError;
1858 ErrorText = MetaIndexParser->ErrorText;
1859 return;
1860 }
1861
1862 if (!VerifyVendor(Message))
1863 {
1864 return;
1865 }
1866
1867 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1868 std::cerr << "Signature verification succeeded: "
1869 << DestFile << std::endl;
1870
1871 // we ensure this by other means
1872 #if 0
1873 // do not trust any previously unverified content that we may have
1874 string LastGoodSigFile = _config->FindDir("Dir::State::lists").append("partial/").append(URItoFileName(RealURI));
1875 if (DestFile != SigFile)
1876 LastGoodSigFile.append(".gpg");
1877 LastGoodSigFile.append(".reverify");
1878 if(IMSHit == false && RealFileExists(LastGoodSigFile) == false)
1879 {
1880 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1881 Target != IndexTargets->end();
1882 ++Target)
1883 {
1884 // remove old indexes
1885 std::string index = _config->FindDir("Dir::State::lists") +
1886 URItoFileName((*Target)->URI);
1887 unlink(index.c_str());
1888 // and also old gzipindexes
1889 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1890 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1891 {
1892 index += '.' + (*t);
1893 unlink(index.c_str());
1894 }
1895 }
1896 }
1897 #endif
1898
1899 // Download further indexes with verification
1900 //
1901 // it would be really nice if we could simply do
1902 // if (IMSHit == false) QueueIndexes(true)
1903 // and skip the download if the Release file has not changed
1904 // - but right now the list cleaner will needs to be tricked
1905 // to not delete all our packages/source indexes in this case
1906 QueueIndexes(true);
1907
1908 #if 0
1909 // is it a clearsigned MetaIndex file?
1910 if (DestFile == SigFile)
1911 return;
1912
1913 // Done, move signature file into position
1914 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1915 URItoFileName(RealURI) + ".gpg";
1916 Rename(SigFile,VerifiedSigFile);
1917 chmod(VerifiedSigFile.c_str(),0644);
1918 #endif
1919 }
1920 /*}}}*/
1921 void pkgAcqMetaBase::QueueIndexes(bool verify) /*{{{*/
1922 {
1923 bool transInRelease = false;
1924 {
1925 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1926 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1927 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1928 if (k->find("Translation-") != std::string::npos)
1929 {
1930 transInRelease = true;
1931 break;
1932 }
1933 }
1934
1935 // at this point the real Items are loaded in the fetcher
1936 ExpectedAdditionalItems = 0;
1937 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1938 Target != IndexTargets->end();
1939 ++Target)
1940 {
1941 HashStringList ExpectedIndexHashes;
1942 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1943 bool compressedAvailable = false;
1944 if (Record == NULL)
1945 {
1946 if ((*Target)->IsOptional() == true)
1947 {
1948 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1949 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1950 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1951 {
1952 compressedAvailable = true;
1953 break;
1954 }
1955 }
1956 else if (verify == true)
1957 {
1958 Status = StatAuthError;
1959 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1960 return;
1961 }
1962 }
1963 else
1964 {
1965 ExpectedIndexHashes = Record->Hashes;
1966 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1967 {
1968 std::cerr << "Queueing: " << (*Target)->URI << std::endl
1969 << "Expected Hash:" << std::endl;
1970 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
1971 std::cerr << "\t- " << hs->toStr() << std::endl;
1972 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
1973 }
1974 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
1975 {
1976 Status = StatAuthError;
1977 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1978 return;
1979 }
1980 }
1981
1982 if ((*Target)->IsOptional() == true)
1983 {
1984 if (transInRelease == false || Record != NULL || compressedAvailable == true)
1985 {
1986 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
1987 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
1988 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
1989 else
1990 new pkgAcqIndexTrans(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
1991 }
1992 continue;
1993 }
1994
1995 /* Queue Packages file (either diff or full packages files, depending
1996 on the users option) - we also check if the PDiff Index file is listed
1997 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1998 instead, but passing the required info to it is to much hassle */
1999 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
2000 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
2001 new pkgAcqDiffIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2002 else
2003 new pkgAcqIndex(Owner, TransactionManager, *Target, ExpectedIndexHashes, MetaIndexParser);
2004 }
2005 }
2006 /*}}}*/
2007 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
2008 {
2009 string::size_type pos;
2010
2011 // check for missing sigs (that where not fatal because otherwise we had
2012 // bombed earlier)
2013 string missingkeys;
2014 string msg = _("There is no public key available for the "
2015 "following key IDs:\n");
2016 pos = Message.find("NO_PUBKEY ");
2017 if (pos != std::string::npos)
2018 {
2019 string::size_type start = pos+strlen("NO_PUBKEY ");
2020 string Fingerprint = Message.substr(start, Message.find("\n")-start);
2021 missingkeys += (Fingerprint);
2022 }
2023 if(!missingkeys.empty())
2024 _error->Warning("%s", (msg + missingkeys).c_str());
2025
2026 string Transformed = MetaIndexParser->GetExpectedDist();
2027
2028 if (Transformed == "../project/experimental")
2029 {
2030 Transformed = "experimental";
2031 }
2032
2033 pos = Transformed.rfind('/');
2034 if (pos != string::npos)
2035 {
2036 Transformed = Transformed.substr(0, pos);
2037 }
2038
2039 if (Transformed == ".")
2040 {
2041 Transformed = "";
2042 }
2043
2044 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
2045 MetaIndexParser->GetValidUntil() > 0) {
2046 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
2047 if (invalid_since > 0)
2048 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
2049 // the time since then the file is invalid - formated in the same way as in
2050 // the download progress display (e.g. 7d 3h 42min 1s)
2051 return _error->Error(
2052 _("Release file for %s is expired (invalid since %s). "
2053 "Updates for this repository will not be applied."),
2054 RealURI.c_str(), TimeToStr(invalid_since).c_str());
2055 }
2056
2057 if (_config->FindB("Debug::pkgAcquire::Auth", false))
2058 {
2059 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
2060 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
2061 std::cerr << "Transformed Dist: " << Transformed << std::endl;
2062 }
2063
2064 if (MetaIndexParser->CheckDist(Transformed) == false)
2065 {
2066 // This might become fatal one day
2067 // Status = StatAuthError;
2068 // ErrorText = "Conflicting distribution; expected "
2069 // + MetaIndexParser->GetExpectedDist() + " but got "
2070 // + MetaIndexParser->GetDist();
2071 // return false;
2072 if (!Transformed.empty())
2073 {
2074 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
2075 Desc.Description.c_str(),
2076 Transformed.c_str(),
2077 MetaIndexParser->GetDist().c_str());
2078 }
2079 }
2080
2081 return true;
2082 }
2083 /*}}}*/
2084 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
2085 // ---------------------------------------------------------------------
2086 /* */
2087 void pkgAcqMetaIndex::Failed(string Message,
2088 pkgAcquire::MethodConfig * /*Cnf*/)
2089 {
2090 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2091
2092 if (AuthPass == true)
2093 {
2094 bool Stop = GenerateAuthWarning(RealURI, Message);
2095 if(Stop)
2096 return;
2097 }
2098
2099 /* Always move the meta index, even if gpgv failed. This ensures
2100 * that PackageFile objects are correctly filled in */
2101 if (FileExists(DestFile))
2102 {
2103 string FinalFile = _config->FindDir("Dir::State::lists");
2104 FinalFile += URItoFileName(RealURI);
2105 /* InRelease files become Release files, otherwise
2106 * they would be considered as trusted later on */
2107 if (SigFile == DestFile) {
2108 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
2109 "Release");
2110 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
2111 "Release");
2112 SigFile = FinalFile;
2113 }
2114
2115 // Done, queue for rename on transaction finished
2116 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2117 }
2118
2119 _error->Warning(_("The data from '%s' is not signed. Packages "
2120 "from that repository can not be authenticated."),
2121 URIDesc.c_str());
2122
2123 // No Release file was present, or verification failed, so fall
2124 // back to queueing Packages files without verification
2125 // only allow going further if the users explicitely wants it
2126 if(_config->FindB("APT::Get::AllowUnauthenticated", false) == true)
2127 {
2128 QueueIndexes(false);
2129 } else {
2130 // warn if the repository is unsinged
2131 _error->Warning("Use --allow-unauthenticated to force the update");
2132 }
2133 }
2134 /*}}}*/
2135
2136 void pkgAcqMetaIndex::Finished()
2137 {
2138 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2139 std::clog << "Finished: " << DestFile <<std::endl;
2140 if(TransactionManager != NULL &&
2141 TransactionManager->TransactionHasError() == false)
2142 TransactionManager->CommitTransaction();
2143 }
2144
2145
2146 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
2147 string const &URI, string const &URIDesc, string const &ShortDesc,
2148 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
2149 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
2150 const vector<IndexTarget*>* IndexTargets,
2151 indexRecords* MetaIndexParser) :
2152 pkgAcqMetaIndex(Owner, NULL, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
2153 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
2154 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
2155 {
2156 SigFile = DestFile;
2157
2158 // index targets + (worst case:) Release/Release.gpg
2159 ExpectedAdditionalItems = IndexTargets->size() + 2;
2160
2161 #if 0
2162 // keep the old InRelease around in case of transistent network errors
2163 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2164 if (RealFileExists(Final) == true)
2165 {
2166 string const LastGoodSig = DestFile + ".reverify";
2167 Rename(Final,LastGoodSig);
2168 }
2169 #endif
2170 }
2171 /*}}}*/
2172 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
2173 {
2174 #if 0
2175 // if the file was never queued undo file-changes done in the constructor
2176 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
2177 {
2178 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
2179 string const LastGoodSig = DestFile + ".reverify";
2180 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
2181 Rename(LastGoodSig, Final);
2182 }
2183 #endif
2184 }
2185 /*}}}*/
2186 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
2187 // ---------------------------------------------------------------------
2188 // FIXME: this can go away once the InRelease file is used widely
2189 string pkgAcqMetaClearSig::Custom600Headers() const
2190 {
2191 string Final = _config->FindDir("Dir::State::lists");
2192 Final += URItoFileName(RealURI);
2193
2194 struct stat Buf;
2195 if (stat(Final.c_str(),&Buf) != 0)
2196 {
2197 if (stat(Final.c_str(),&Buf) != 0)
2198 return "\nIndex-File: true\nFail-Ignore: true\n";
2199 }
2200
2201 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2202 }
2203 /*}}}*/
2204 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
2205 // ---------------------------------------------------------------------
2206 void pkgAcqMetaClearSig::Done(std::string Message,unsigned long long Size,
2207 HashStringList const &Hashes,
2208 pkgAcquire::MethodConfig *Cnf)
2209 {
2210 // if we expect a ClearTextSignature (InRelase), ensure that
2211 // this is what we get and if not fail to queue a
2212 // Release/Release.gpg, see #346386
2213 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
2214 {
2215 pkgAcquire::Item::Failed(Message, Cnf);
2216 RenameOnError(NotClearsigned);
2217 TransactionManager->AbortTransaction();
2218 return;
2219 }
2220 pkgAcqMetaIndex::Done(Message, Size, Hashes, Cnf);
2221 }
2222 /*}}}*/
2223 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
2224 {
2225 // we failed, we will not get additional items from this method
2226 ExpectedAdditionalItems = 0;
2227
2228 if (AuthPass == false)
2229 {
2230 // Queue the 'old' InRelease file for removal if we try Release.gpg
2231 // as otherwise the file will stay around and gives a false-auth
2232 // impression (CVE-2012-0214)
2233 string FinalFile = _config->FindDir("Dir::State::lists");
2234 FinalFile.append(URItoFileName(RealURI));
2235 TransactionManager->TransactionStageRemoval(this, FinalFile);
2236
2237 new pkgAcqMetaIndex(Owner, TransactionManager,
2238 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
2239 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
2240 IndexTargets, MetaIndexParser);
2241 if (Cnf->LocalOnly == true ||
2242 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
2243 Dequeue();
2244 }
2245 else
2246 pkgAcqMetaIndex::Failed(Message, Cnf);
2247 }
2248 /*}}}*/
2249 // AcqArchive::AcqArchive - Constructor /*{{{*/
2250 // ---------------------------------------------------------------------
2251 /* This just sets up the initial fetch environment and queues the first
2252 possibilitiy */
2253 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
2254 pkgRecords *Recs,pkgCache::VerIterator const &Version,
2255 string &StoreFilename) :
2256 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
2257 StoreFilename(StoreFilename), Vf(Version.FileList()),
2258 Trusted(false)
2259 {
2260 Retries = _config->FindI("Acquire::Retries",0);
2261
2262 if (Version.Arch() == 0)
2263 {
2264 _error->Error(_("I wasn't able to locate a file for the %s package. "
2265 "This might mean you need to manually fix this package. "
2266 "(due to missing arch)"),
2267 Version.ParentPkg().FullName().c_str());
2268 return;
2269 }
2270
2271 /* We need to find a filename to determine the extension. We make the
2272 assumption here that all the available sources for this version share
2273 the same extension.. */
2274 // Skip not source sources, they do not have file fields.
2275 for (; Vf.end() == false; ++Vf)
2276 {
2277 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2278 continue;
2279 break;
2280 }
2281
2282 // Does not really matter here.. we are going to fail out below
2283 if (Vf.end() != true)
2284 {
2285 // If this fails to get a file name we will bomb out below.
2286 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2287 if (_error->PendingError() == true)
2288 return;
2289
2290 // Generate the final file name as: package_version_arch.foo
2291 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2292 QuoteString(Version.VerStr(),"_:") + '_' +
2293 QuoteString(Version.Arch(),"_:.") +
2294 "." + flExtension(Parse.FileName());
2295 }
2296
2297 // check if we have one trusted source for the package. if so, switch
2298 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2299 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2300 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2301 bool seenUntrusted = false;
2302 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2303 {
2304 pkgIndexFile *Index;
2305 if (Sources->FindIndex(i.File(),Index) == false)
2306 continue;
2307
2308 if (debugAuth == true)
2309 std::cerr << "Checking index: " << Index->Describe()
2310 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2311
2312 if (Index->IsTrusted() == true)
2313 {
2314 Trusted = true;
2315 if (allowUnauth == false)
2316 break;
2317 }
2318 else
2319 seenUntrusted = true;
2320 }
2321
2322 // "allow-unauthenticated" restores apts old fetching behaviour
2323 // that means that e.g. unauthenticated file:// uris are higher
2324 // priority than authenticated http:// uris
2325 if (allowUnauth == true && seenUntrusted == true)
2326 Trusted = false;
2327
2328 // Select a source
2329 if (QueueNext() == false && _error->PendingError() == false)
2330 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2331 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2332 }
2333 /*}}}*/
2334 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2335 // ---------------------------------------------------------------------
2336 /* This queues the next available file version for download. It checks if
2337 the archive is already available in the cache and stashs the MD5 for
2338 checking later. */
2339 bool pkgAcqArchive::QueueNext()
2340 {
2341 for (; Vf.end() == false; ++Vf)
2342 {
2343 // Ignore not source sources
2344 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2345 continue;
2346
2347 // Try to cross match against the source list
2348 pkgIndexFile *Index;
2349 if (Sources->FindIndex(Vf.File(),Index) == false)
2350 continue;
2351
2352 // only try to get a trusted package from another source if that source
2353 // is also trusted
2354 if(Trusted && !Index->IsTrusted())
2355 continue;
2356
2357 // Grab the text package record
2358 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2359 if (_error->PendingError() == true)
2360 return false;
2361
2362 string PkgFile = Parse.FileName();
2363 ExpectedHashes = Parse.Hashes();
2364
2365 if (PkgFile.empty() == true)
2366 return _error->Error(_("The package index files are corrupted. No Filename: "
2367 "field for package %s."),
2368 Version.ParentPkg().Name());
2369
2370 Desc.URI = Index->ArchiveURI(PkgFile);
2371 Desc.Description = Index->ArchiveInfo(Version);
2372 Desc.Owner = this;
2373 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2374
2375 // See if we already have the file. (Legacy filenames)
2376 FileSize = Version->Size;
2377 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2378 struct stat Buf;
2379 if (stat(FinalFile.c_str(),&Buf) == 0)
2380 {
2381 // Make sure the size matches
2382 if ((unsigned long long)Buf.st_size == Version->Size)
2383 {
2384 Complete = true;
2385 Local = true;
2386 Status = StatDone;
2387 StoreFilename = DestFile = FinalFile;
2388 return true;
2389 }
2390
2391 /* Hmm, we have a file and its size does not match, this means it is
2392 an old style mismatched arch */
2393 unlink(FinalFile.c_str());
2394 }
2395
2396 // Check it again using the new style output filenames
2397 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2398 if (stat(FinalFile.c_str(),&Buf) == 0)
2399 {
2400 // Make sure the size matches
2401 if ((unsigned long long)Buf.st_size == Version->Size)
2402 {
2403 Complete = true;
2404 Local = true;
2405 Status = StatDone;
2406 StoreFilename = DestFile = FinalFile;
2407 return true;
2408 }
2409
2410 /* Hmm, we have a file and its size does not match, this shouldn't
2411 happen.. */
2412 unlink(FinalFile.c_str());
2413 }
2414
2415 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2416
2417 // Check the destination file
2418 if (stat(DestFile.c_str(),&Buf) == 0)
2419 {
2420 // Hmm, the partial file is too big, erase it
2421 if ((unsigned long long)Buf.st_size > Version->Size)
2422 unlink(DestFile.c_str());
2423 else
2424 PartialSize = Buf.st_size;
2425 }
2426
2427 // Disables download of archives - useful if no real installation follows,
2428 // e.g. if we are just interested in proposed installation order
2429 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2430 {
2431 Complete = true;
2432 Local = true;
2433 Status = StatDone;
2434 StoreFilename = DestFile = FinalFile;
2435 return true;
2436 }
2437
2438 // Create the item
2439 Local = false;
2440 QueueURI(Desc);
2441
2442 ++Vf;
2443 return true;
2444 }
2445 return false;
2446 }
2447 /*}}}*/
2448 // AcqArchive::Done - Finished fetching /*{{{*/
2449 // ---------------------------------------------------------------------
2450 /* */
2451 void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2452 pkgAcquire::MethodConfig *Cfg)
2453 {
2454 Item::Done(Message, Size, CalcHashes, Cfg);
2455
2456 // Check the size
2457 if (Size != Version->Size)
2458 {
2459 RenameOnError(SizeMismatch);
2460 return;
2461 }
2462
2463 // FIXME: could this empty() check impose *any* sort of security issue?
2464 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2465 {
2466 RenameOnError(HashSumMismatch);
2467 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2468 return;
2469 }
2470
2471 // Grab the output filename
2472 string FileName = LookupTag(Message,"Filename");
2473 if (FileName.empty() == true)
2474 {
2475 Status = StatError;
2476 ErrorText = "Method gave a blank filename";
2477 return;
2478 }
2479
2480 Complete = true;
2481
2482 // Reference filename
2483 if (FileName != DestFile)
2484 {
2485 StoreFilename = DestFile = FileName;
2486 Local = true;
2487 return;
2488 }
2489
2490 // Done, move it into position
2491 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2492 FinalFile += flNotDir(StoreFilename);
2493 Rename(DestFile,FinalFile);
2494
2495 StoreFilename = DestFile = FinalFile;
2496 Complete = true;
2497 }
2498 /*}}}*/
2499 // AcqArchive::Failed - Failure handler /*{{{*/
2500 // ---------------------------------------------------------------------
2501 /* Here we try other sources */
2502 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2503 {
2504 ErrorText = LookupTag(Message,"Message");
2505
2506 /* We don't really want to retry on failed media swaps, this prevents
2507 that. An interesting observation is that permanent failures are not
2508 recorded. */
2509 if (Cnf->Removable == true &&
2510 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2511 {
2512 // Vf = Version.FileList();
2513 while (Vf.end() == false) ++Vf;
2514 StoreFilename = string();
2515 Item::Failed(Message,Cnf);
2516 return;
2517 }
2518
2519 if (QueueNext() == false)
2520 {
2521 // This is the retry counter
2522 if (Retries != 0 &&
2523 Cnf->LocalOnly == false &&
2524 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2525 {
2526 Retries--;
2527 Vf = Version.FileList();
2528 if (QueueNext() == true)
2529 return;
2530 }
2531
2532 StoreFilename = string();
2533 Item::Failed(Message,Cnf);
2534 }
2535 }
2536 /*}}}*/
2537 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2538 // ---------------------------------------------------------------------
2539 APT_PURE bool pkgAcqArchive::IsTrusted() const
2540 {
2541 return Trusted;
2542 }
2543 /*}}}*/
2544 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2545 // ---------------------------------------------------------------------
2546 /* */
2547 void pkgAcqArchive::Finished()
2548 {
2549 if (Status == pkgAcquire::Item::StatDone &&
2550 Complete == true)
2551 return;
2552 StoreFilename = string();
2553 }
2554 /*}}}*/
2555 // AcqFile::pkgAcqFile - Constructor /*{{{*/
2556 // ---------------------------------------------------------------------
2557 /* The file is added to the queue */
2558 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2559 unsigned long long Size,string Dsc,string ShortDesc,
2560 const string &DestDir, const string &DestFilename,
2561 bool IsIndexFile) :
2562 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2563 {
2564 Retries = _config->FindI("Acquire::Retries",0);
2565
2566 if(!DestFilename.empty())
2567 DestFile = DestFilename;
2568 else if(!DestDir.empty())
2569 DestFile = DestDir + "/" + flNotDir(URI);
2570 else
2571 DestFile = flNotDir(URI);
2572
2573 // Create the item
2574 Desc.URI = URI;
2575 Desc.Description = Dsc;
2576 Desc.Owner = this;
2577
2578 // Set the short description to the archive component
2579 Desc.ShortDesc = ShortDesc;
2580
2581 // Get the transfer sizes
2582 FileSize = Size;
2583 struct stat Buf;
2584 if (stat(DestFile.c_str(),&Buf) == 0)
2585 {
2586 // Hmm, the partial file is too big, erase it
2587 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2588 unlink(DestFile.c_str());
2589 else
2590 PartialSize = Buf.st_size;
2591 }
2592
2593 QueueURI(Desc);
2594 }
2595 /*}}}*/
2596 // AcqFile::Done - Item downloaded OK /*{{{*/
2597 // ---------------------------------------------------------------------
2598 /* */
2599 void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2600 pkgAcquire::MethodConfig *Cnf)
2601 {
2602 Item::Done(Message,Size,CalcHashes,Cnf);
2603
2604 // Check the hash
2605 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2606 {
2607 RenameOnError(HashSumMismatch);
2608 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2609 return;
2610 }
2611
2612 string FileName = LookupTag(Message,"Filename");
2613 if (FileName.empty() == true)
2614 {
2615 Status = StatError;
2616 ErrorText = "Method gave a blank filename";
2617 return;
2618 }
2619
2620 Complete = true;
2621
2622 // The files timestamp matches
2623 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2624 return;
2625
2626 // We have to copy it into place
2627 if (FileName != DestFile)
2628 {
2629 Local = true;
2630 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2631 Cnf->Removable == true)
2632 {
2633 Desc.URI = "copy:" + FileName;
2634 QueueURI(Desc);
2635 return;
2636 }
2637
2638 // Erase the file if it is a symlink so we can overwrite it
2639 struct stat St;
2640 if (lstat(DestFile.c_str(),&St) == 0)
2641 {
2642 if (S_ISLNK(St.st_mode) != 0)
2643 unlink(DestFile.c_str());
2644 }
2645
2646 // Symlink the file
2647 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2648 {
2649 ErrorText = "Link to " + DestFile + " failure ";
2650 Status = StatError;
2651 Complete = false;
2652 }
2653 }
2654 }
2655 /*}}}*/
2656 // AcqFile::Failed - Failure handler /*{{{*/
2657 // ---------------------------------------------------------------------
2658 /* Here we try other sources */
2659 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2660 {
2661 ErrorText = LookupTag(Message,"Message");
2662
2663 // This is the retry counter
2664 if (Retries != 0 &&
2665 Cnf->LocalOnly == false &&
2666 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2667 {
2668 Retries--;
2669 QueueURI(Desc);
2670 return;
2671 }
2672
2673 Item::Failed(Message,Cnf);
2674 }
2675 /*}}}*/
2676 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2677 // ---------------------------------------------------------------------
2678 /* The only header we use is the last-modified header. */
2679 string pkgAcqFile::Custom600Headers() const
2680 {
2681 if (IsIndexFile)
2682 return "\nIndex-File: true";
2683 return "";
2684 }
2685 /*}}}*/