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