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