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