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