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