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