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