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