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