]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
43c09e7b58f47da2740e671432f6535b560ef7c4
[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 string FinalFile = _config->FindDir("Dir::State::lists");
1168 FinalFile += URItoFileName(RealURI);
1169 DestFile = FinalFile;
1170 #if 0
1171 /* We restore the original name to DestFile so that the clean operation
1172 will work OK */
1173 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1174 DestFile += URItoFileName(RealURI);
1175
1176 // Remove the compressed version.
1177 if (Erase == true)
1178 unlink(DestFile.c_str());
1179 #endif
1180 return;
1181 }
1182
1183 Erase = false;
1184 Complete = true;
1185
1186 // Handle the unzipd case
1187 string FileName = LookupTag(Message,"Alt-Filename");
1188 if (FileName.empty() == false)
1189 {
1190 // The files timestamp matches
1191 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
1192 return;
1193 Decompression = true;
1194 Local = true;
1195 DestFile += ".decomp";
1196 Desc.URI = "copy:" + FileName;
1197 QueueURI(Desc);
1198 Mode = "copy";
1199 return;
1200 }
1201
1202 FileName = LookupTag(Message,"Filename");
1203 if (FileName.empty() == true)
1204 {
1205 Status = StatError;
1206 ErrorText = "Method gave a blank filename";
1207 }
1208
1209 std::string const compExt = CompressionExtension.substr(0, CompressionExtension.find(' '));
1210
1211 // The files timestamp matches
1212 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true) {
1213 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz")
1214 // Update DestFile for .gz suffix so that the clean operation keeps it
1215 DestFile += ".gz";
1216 return;
1217 }
1218
1219 if (FileName == DestFile)
1220 Erase = true;
1221 else
1222 Local = true;
1223
1224 string decompProg;
1225
1226 // If we enable compressed indexes and already have gzip, keep it
1227 if (_config->FindB("Acquire::GzipIndexes",false) && compExt == "gz" && !Local) {
1228 string FinalFile = _config->FindDir("Dir::State::lists");
1229 FinalFile += URItoFileName(RealURI) + ".gz";
1230 Rename(DestFile,FinalFile);
1231 chmod(FinalFile.c_str(),0644);
1232
1233 // Update DestFile for .gz suffix so that the clean operation keeps it
1234 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1235 DestFile += URItoFileName(RealURI) + ".gz";
1236 return;
1237 }
1238
1239 // get the binary name for your used compression type
1240 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
1241 if(decompProg.empty() == false);
1242 else if(compExt == "uncompressed")
1243 decompProg = "copy";
1244 else {
1245 _error->Error("Unsupported extension: %s", compExt.c_str());
1246 return;
1247 }
1248
1249 Decompression = true;
1250 DestFile += ".decomp";
1251 Desc.URI = decompProg + ":" + FileName;
1252 QueueURI(Desc);
1253
1254 // FIXME: this points to a c++ string that goes out of scope
1255 Mode = decompProg.c_str();
1256 }
1257 /*}}}*/
1258 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
1259 // ---------------------------------------------------------------------
1260 /* The Translation file is added to the queue */
1261 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1262 string URI,string URIDesc,string ShortDesc)
1263 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashStringList(), "")
1264 {
1265 }
1266 /*}}}*/
1267 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
1268 unsigned long TransactionID,
1269 IndexTarget const * const Target,
1270 HashStringList const &ExpectedHashes,
1271 indexRecords *MetaIndexParser)
1272 : pkgAcqIndex(Owner, TransactionID, Target, ExpectedHashes, MetaIndexParser)
1273 {
1274 // load the filesize
1275 indexRecords::checkSum *Record = MetaIndexParser->Lookup(string(Target->MetaKey));
1276 if(Record)
1277 FileSize = Record->Size;
1278 }
1279 /*}}}*/
1280 // AcqIndexTrans::Custom600Headers - Insert custom request headers /*{{{*/
1281 // ---------------------------------------------------------------------
1282 string pkgAcqIndexTrans::Custom600Headers() const
1283 {
1284 string Final = _config->FindDir("Dir::State::lists");
1285 Final += URItoFileName(RealURI);
1286
1287 struct stat Buf;
1288 if (stat(Final.c_str(),&Buf) != 0)
1289 return "\nFail-Ignore: true\nIndex-File: true";
1290 return "\nFail-Ignore: true\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1291 }
1292 /*}}}*/
1293 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
1294 // ---------------------------------------------------------------------
1295 /* */
1296 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1297 {
1298 size_t const nextExt = CompressionExtension.find(' ');
1299 if (nextExt != std::string::npos)
1300 {
1301 CompressionExtension = CompressionExtension.substr(nextExt+1);
1302 Init(RealURI, Desc.Description, Desc.ShortDesc);
1303 Status = StatIdle;
1304 return;
1305 }
1306
1307 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1308 if (Cnf->LocalOnly == true ||
1309 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1310 {
1311 // Ignore this
1312 Status = StatDone;
1313 Complete = false;
1314 Dequeue();
1315 return;
1316 }
1317
1318 Item::Failed(Message,Cnf);
1319 }
1320 /*}}}*/
1321 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
1322 unsigned long TransactionID,
1323 string URI,string URIDesc,string ShortDesc,
1324 string MetaIndexFile,
1325 const vector<IndexTarget*>* IndexTargets,
1326 indexRecords* MetaIndexParser) :
1327 Item(Owner, HashStringList(), TransactionID), RealURI(URI),
1328 MetaIndexParser(MetaIndexParser), MetaIndexFile(MetaIndexFile),
1329 IndexTargets(IndexTargets), AuthPass(false), IMSHit(false)
1330 {
1331 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1332 DestFile += URItoFileName(URI);
1333
1334 // remove any partial downloaded sig-file in partial/.
1335 // it may confuse proxies and is too small to warrant a
1336 // partial download anyway
1337 unlink(DestFile.c_str());
1338
1339 // set the TransactionID
1340 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1341 std::clog << "New pkgAcqMetaSig with TransactionID "
1342 << TransactionID << std::endl;
1343
1344 // Create the item
1345 Desc.Description = URIDesc;
1346 Desc.Owner = this;
1347 Desc.ShortDesc = ShortDesc;
1348 Desc.URI = URI;
1349
1350 QueueURI(Desc);
1351 }
1352 /*}}}*/
1353 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1354 {
1355 }
1356 /*}}}*/
1357 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1358 // ---------------------------------------------------------------------
1359 /* The only header we use is the last-modified header. */
1360 string pkgAcqMetaSig::Custom600Headers() const
1361 {
1362 string FinalFile = _config->FindDir("Dir::State::lists");
1363 FinalFile += URItoFileName(RealURI);
1364
1365 struct stat Buf;
1366 if (stat(FinalFile.c_str(),&Buf) != 0)
1367 return "\nIndex-File: true";
1368
1369 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1370 }
1371
1372 void pkgAcqMetaSig::Done(string Message,unsigned long long Size, HashStringList const &Hashes,
1373 pkgAcquire::MethodConfig *Cfg)
1374 {
1375 Item::Done(Message, Size, Hashes, Cfg);
1376
1377 string FileName = LookupTag(Message,"Filename");
1378 if (FileName.empty() == true)
1379 {
1380 Status = StatError;
1381 ErrorText = "Method gave a blank filename";
1382 return;
1383 }
1384
1385 if (FileName != DestFile)
1386 {
1387 // We have to copy it into place
1388 Local = true;
1389 Desc.URI = "copy:" + FileName;
1390 QueueURI(Desc);
1391 return;
1392 }
1393
1394 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1395 IMSHit = true;
1396
1397 // adjust paths if its a ims-hit
1398 if(IMSHit)
1399 {
1400 string FinalFile = _config->FindDir("Dir::State::lists");
1401 FinalFile += URItoFileName(RealURI);
1402
1403 DestFile = PartialFile = FinalFile;
1404 }
1405
1406 // queue for verify
1407 if(AuthPass == false)
1408 {
1409 AuthPass = true;
1410 Desc.URI = "gpgv:" + DestFile;
1411 DestFile = MetaIndexFile;
1412 QueueURI(Desc);
1413 return;
1414 }
1415
1416 // queue to copy the file in place if it was not a ims hit, on ims
1417 // hit the file is already at the right place
1418 if(IMSHit == false)
1419 {
1420 PartialFile = _config->FindDir("Dir::State::lists") + "partial/";
1421 PartialFile += URItoFileName(RealURI);
1422
1423 DestFile = _config->FindDir("Dir::State::lists");
1424 DestFile += URItoFileName(RealURI);
1425 }
1426
1427 Complete = true;
1428
1429 }
1430 /*}}}*/
1431 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
1432 {
1433 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1434
1435 // this ensures that any file in the lists/ dir is removed by the
1436 // transaction
1437 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1438 DestFile += URItoFileName(RealURI);
1439 PartialFile = "";
1440
1441 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1442 if (Cnf->LocalOnly == true ||
1443 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1444 {
1445 // Ignore this
1446 Status = StatDone;
1447 Complete = false;
1448 Dequeue();
1449 return;
1450 }
1451 Item::Failed(Message,Cnf);
1452 }
1453 /*}}}*/
1454 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
1455 unsigned long TransactionID,
1456 string URI,string URIDesc,string ShortDesc,
1457 string MetaIndexSigURI,string MetaIndexSigURIDesc, string MetaIndexSigShortDesc,
1458 const vector<IndexTarget*>* IndexTargets,
1459 indexRecords* MetaIndexParser) :
1460 Item(Owner, HashStringList(), TransactionID), RealURI(URI), IndexTargets(IndexTargets),
1461 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false),
1462 MetaIndexSigURI(MetaIndexSigURI), MetaIndexSigURIDesc(MetaIndexSigURIDesc),
1463 MetaIndexSigShortDesc(MetaIndexSigShortDesc)
1464 {
1465 if(TransactionID == 0)
1466 this->TransactionID = (unsigned long)this;
1467
1468 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1469 std::clog << "New pkgAcqMetaIndex with TransactionID "
1470 << TransactionID << std::endl;
1471
1472 Init(URIDesc, ShortDesc);
1473 }
1474 /*}}}*/
1475 // pkgAcqMetaIndex::Init - Delayed constructor /*{{{*/
1476 void pkgAcqMetaIndex::Init(std::string URIDesc, std::string ShortDesc)
1477 {
1478 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1479 DestFile += URItoFileName(RealURI);
1480
1481 // Create the item
1482 Desc.Description = URIDesc;
1483 Desc.Owner = this;
1484 Desc.ShortDesc = ShortDesc;
1485 Desc.URI = RealURI;
1486
1487 // we expect more item
1488 ExpectedAdditionalItems = IndexTargets->size();
1489 QueueURI(Desc);
1490 }
1491 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
1492 // ---------------------------------------------------------------------
1493 /* The only header we use is the last-modified header. */
1494 string pkgAcqMetaIndex::Custom600Headers() const
1495 {
1496 string Final = _config->FindDir("Dir::State::lists");
1497 Final += URItoFileName(RealURI);
1498
1499 struct stat Buf;
1500 if (stat(Final.c_str(),&Buf) != 0)
1501 return "\nIndex-File: true";
1502
1503 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1504 }
1505 /*}}}*/
1506 void pkgAcqMetaIndex::Done(string Message,unsigned long long Size,HashStringList const &Hashes, /*{{{*/
1507 pkgAcquire::MethodConfig *Cfg)
1508 {
1509 Item::Done(Message,Size,Hashes,Cfg);
1510
1511 // MetaIndexes are done in two passes: one to download the
1512 // metaindex with an appropriate method, and a second to verify it
1513 // with the gpgv method
1514
1515 if (AuthPass == true)
1516 {
1517 AuthDone(Message);
1518
1519 // all cool, move Release file into place
1520 Complete = true;
1521 }
1522 else
1523 {
1524 RetrievalDone(Message);
1525 if (!Complete)
1526 // Still more retrieving to do
1527 return;
1528
1529 if (SigFile == "")
1530 {
1531 // load indexes, the signature will downloaded afterwards
1532 MetaIndexParser->Load(DestFile);
1533 QueueIndexes(true);
1534 }
1535 else
1536 {
1537 // FIXME: move this into pkgAcqMetaClearSig::Done on the next
1538 // ABI break
1539
1540 // if we expect a ClearTextSignature (InRelase), ensure that
1541 // this is what we get and if not fail to queue a
1542 // Release/Release.gpg, see #346386
1543 if (SigFile == DestFile && !StartsWithGPGClearTextSignature(DestFile))
1544 {
1545 Failed(Message, Cfg);
1546 return;
1547 }
1548
1549 // There was a signature file, so pass it to gpgv for
1550 // verification
1551 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1552 std::cerr << "Metaindex acquired, queueing gpg verification ("
1553 << SigFile << "," << DestFile << ")\n";
1554 AuthPass = true;
1555 Desc.URI = "gpgv:" + SigFile;
1556 QueueURI(Desc);
1557 Mode = "gpgv";
1558 return;
1559 }
1560 }
1561
1562 if (Complete == true)
1563 {
1564 string FinalFile = _config->FindDir("Dir::State::lists");
1565 FinalFile += URItoFileName(RealURI);
1566 if (SigFile == DestFile)
1567 SigFile = FinalFile;
1568 // queue for copy in place
1569 PartialFile = DestFile;
1570 DestFile = FinalFile;
1571 }
1572 }
1573 /*}}}*/
1574 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
1575 {
1576 // We have just finished downloading a Release file (it is not
1577 // verified yet)
1578
1579 string FileName = LookupTag(Message,"Filename");
1580 if (FileName.empty() == true)
1581 {
1582 Status = StatError;
1583 ErrorText = "Method gave a blank filename";
1584 return;
1585 }
1586
1587 if (FileName != DestFile)
1588 {
1589 Local = true;
1590 Desc.URI = "copy:" + FileName;
1591 QueueURI(Desc);
1592 return;
1593 }
1594
1595 // make sure to verify against the right file on I-M-S hit
1596 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
1597 if(IMSHit)
1598 {
1599 string FinalFile = _config->FindDir("Dir::State::lists");
1600 FinalFile += URItoFileName(RealURI);
1601 if (SigFile == DestFile)
1602 {
1603 SigFile = FinalFile;
1604 #if 0
1605 // constructor of pkgAcqMetaClearSig moved it out of the way,
1606 // now move it back in on IMS hit for the 'old' file
1607 string const OldClearSig = DestFile + ".reverify";
1608 if (RealFileExists(OldClearSig) == true)
1609 Rename(OldClearSig, FinalFile);
1610 #endif
1611 }
1612 DestFile = FinalFile;
1613 }
1614
1615 // queue a signature
1616 if(SigFile != DestFile)
1617 new pkgAcqMetaSig(Owner, TransactionID,
1618 MetaIndexSigURI, MetaIndexSigURIDesc,
1619 MetaIndexSigShortDesc, DestFile, IndexTargets,
1620 MetaIndexParser);
1621
1622 Complete = true;
1623 }
1624 /*}}}*/
1625 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1626 {
1627 // At this point, the gpgv method has succeeded, so there is a
1628 // valid signature from a key in the trusted keyring. We
1629 // perform additional verification of its contents, and use them
1630 // to verify the indexes we are about to download
1631
1632 if (!MetaIndexParser->Load(DestFile))
1633 {
1634 Status = StatAuthError;
1635 ErrorText = MetaIndexParser->ErrorText;
1636 return;
1637 }
1638
1639 if (!VerifyVendor(Message))
1640 {
1641 return;
1642 }
1643
1644 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1645 std::cerr << "Signature verification succeeded: "
1646 << DestFile << std::endl;
1647
1648 // Download further indexes with verification
1649 QueueIndexes(true);
1650
1651 #if 0
1652 // is it a clearsigned MetaIndex file?
1653 if (DestFile == SigFile)
1654 return;
1655
1656 // Done, move signature file into position
1657 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1658 URItoFileName(RealURI) + ".gpg";
1659 Rename(SigFile,VerifiedSigFile);
1660 chmod(VerifiedSigFile.c_str(),0644);
1661 #endif
1662 }
1663 /*}}}*/
1664 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1665 {
1666 bool transInRelease = false;
1667 {
1668 std::vector<std::string> const keys = MetaIndexParser->MetaKeys();
1669 for (std::vector<std::string>::const_iterator k = keys.begin(); k != keys.end(); ++k)
1670 // FIXME: Feels wrong to check for hardcoded string here, but what should we do else…
1671 if (k->find("Translation-") != std::string::npos)
1672 {
1673 transInRelease = true;
1674 break;
1675 }
1676 }
1677
1678 // at this point the real Items are loaded in the fetcher
1679 ExpectedAdditionalItems = 0;
1680 for (vector <IndexTarget*>::const_iterator Target = IndexTargets->begin();
1681 Target != IndexTargets->end();
1682 ++Target)
1683 {
1684 HashStringList ExpectedIndexHashes;
1685 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1686 bool compressedAvailable = false;
1687 if (Record == NULL)
1688 {
1689 if ((*Target)->IsOptional() == true)
1690 {
1691 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
1692 for (std::vector<std::string>::const_iterator t = types.begin(); t != types.end(); ++t)
1693 if (MetaIndexParser->Exists((*Target)->MetaKey + "." + *t) == true)
1694 {
1695 compressedAvailable = true;
1696 break;
1697 }
1698 }
1699 else if (verify == true)
1700 {
1701 Status = StatAuthError;
1702 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), (*Target)->MetaKey.c_str());
1703 return;
1704 }
1705 }
1706 else
1707 {
1708 ExpectedIndexHashes = Record->Hashes;
1709 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1710 {
1711 std::cerr << "Queueing: " << (*Target)->URI << std::endl
1712 << "Expected Hash:" << std::endl;
1713 for (HashStringList::const_iterator hs = ExpectedIndexHashes.begin(); hs != ExpectedIndexHashes.end(); ++hs)
1714 std::cerr << "\t- " << hs->toStr() << std::endl;
1715 std::cerr << "For: " << Record->MetaKeyFilename << std::endl;
1716 }
1717 if (verify == true && ExpectedIndexHashes.empty() == true && (*Target)->IsOptional() == false)
1718 {
1719 Status = StatAuthError;
1720 strprintf(ErrorText, _("Unable to find hash sum for '%s' in Release file"), (*Target)->MetaKey.c_str());
1721 return;
1722 }
1723 }
1724
1725 if ((*Target)->IsOptional() == true)
1726 {
1727 if ((*Target)->IsSubIndex() == true)
1728 new pkgAcqSubIndex(Owner, TransactionID,
1729 (*Target)->URI, (*Target)->Description,
1730 (*Target)->ShortDesc, ExpectedIndexHashes);
1731 else if (transInRelease == false || Record != NULL || compressedAvailable == true)
1732 {
1733 if (_config->FindB("Acquire::PDiffs",true) == true && transInRelease == true &&
1734 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true)
1735 new pkgAcqDiffIndex(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
1736 else
1737 new pkgAcqIndexTrans(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
1738 }
1739 continue;
1740 }
1741
1742 /* Queue Packages file (either diff or full packages files, depending
1743 on the users option) - we also check if the PDiff Index file is listed
1744 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1745 instead, but passing the required info to it is to much hassle */
1746 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1747 MetaIndexParser->Exists((*Target)->MetaKey + ".diff/Index") == true))
1748 new pkgAcqDiffIndex(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
1749 else
1750 new pkgAcqIndex(Owner, TransactionID, *Target, ExpectedIndexHashes, MetaIndexParser);
1751 }
1752 }
1753 /*}}}*/
1754 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1755 {
1756 string::size_type pos;
1757
1758 // check for missing sigs (that where not fatal because otherwise we had
1759 // bombed earlier)
1760 string missingkeys;
1761 string msg = _("There is no public key available for the "
1762 "following key IDs:\n");
1763 pos = Message.find("NO_PUBKEY ");
1764 if (pos != std::string::npos)
1765 {
1766 string::size_type start = pos+strlen("NO_PUBKEY ");
1767 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1768 missingkeys += (Fingerprint);
1769 }
1770 if(!missingkeys.empty())
1771 _error->Warning("%s", (msg + missingkeys).c_str());
1772
1773 string Transformed = MetaIndexParser->GetExpectedDist();
1774
1775 if (Transformed == "../project/experimental")
1776 {
1777 Transformed = "experimental";
1778 }
1779
1780 pos = Transformed.rfind('/');
1781 if (pos != string::npos)
1782 {
1783 Transformed = Transformed.substr(0, pos);
1784 }
1785
1786 if (Transformed == ".")
1787 {
1788 Transformed = "";
1789 }
1790
1791 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1792 MetaIndexParser->GetValidUntil() > 0) {
1793 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1794 if (invalid_since > 0)
1795 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1796 // the time since then the file is invalid - formated in the same way as in
1797 // the download progress display (e.g. 7d 3h 42min 1s)
1798 return _error->Error(
1799 _("Release file for %s is expired (invalid since %s). "
1800 "Updates for this repository will not be applied."),
1801 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1802 }
1803
1804 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1805 {
1806 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1807 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1808 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1809 }
1810
1811 if (MetaIndexParser->CheckDist(Transformed) == false)
1812 {
1813 // This might become fatal one day
1814 // Status = StatAuthError;
1815 // ErrorText = "Conflicting distribution; expected "
1816 // + MetaIndexParser->GetExpectedDist() + " but got "
1817 // + MetaIndexParser->GetDist();
1818 // return false;
1819 if (!Transformed.empty())
1820 {
1821 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1822 Desc.Description.c_str(),
1823 Transformed.c_str(),
1824 MetaIndexParser->GetDist().c_str());
1825 }
1826 }
1827
1828 return true;
1829 }
1830 /*}}}*/
1831 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1832 // ---------------------------------------------------------------------
1833 /* */
1834 void pkgAcqMetaIndex::Failed(string Message,
1835 pkgAcquire::MethodConfig * /*Cnf*/)
1836 {
1837 if (AuthPass == true)
1838 {
1839 // gpgv method failed, if we have a good signature
1840 string LastGoodSigFile = _config->FindDir("Dir::State::lists");
1841 LastGoodSigFile += URItoFileName(RealURI);
1842 if (DestFile != SigFile)
1843 LastGoodSigFile.append(".gpg");
1844
1845 if(FileExists(LastGoodSigFile))
1846 {
1847 Status = StatTransientNetworkError;
1848 _error->Warning(_("An error occurred during the signature "
1849 "verification. The repository is not updated "
1850 "and the previous index files will be used. "
1851 "GPG error: %s: %s\n"),
1852 Desc.Description.c_str(),
1853 LookupTag(Message,"Message").c_str());
1854 RunScripts("APT::Update::Auth-Failure");
1855 return;
1856 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1857 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1858 _error->Error(_("GPG error: %s: %s"),
1859 Desc.Description.c_str(),
1860 LookupTag(Message,"Message").c_str());
1861 Status = StatError;
1862 return;
1863 } else {
1864 _error->Warning(_("GPG error: %s: %s"),
1865 Desc.Description.c_str(),
1866 LookupTag(Message,"Message").c_str());
1867 }
1868 // gpgv method failed
1869 ReportMirrorFailure("GPGFailure");
1870 }
1871
1872 /* Always move the meta index, even if gpgv failed. This ensures
1873 * that PackageFile objects are correctly filled in */
1874 if (FileExists(DestFile)) {
1875 string FinalFile = _config->FindDir("Dir::State::lists");
1876 FinalFile += URItoFileName(RealURI);
1877 /* InRelease files become Release files, otherwise
1878 * they would be considered as trusted later on */
1879 if (SigFile == DestFile) {
1880 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
1881 "Release");
1882 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
1883 "Release");
1884 SigFile = FinalFile;
1885 }
1886
1887 // Done, queue for rename on transaction finished
1888 PartialFile = DestFile;
1889 DestFile = FinalFile;
1890 }
1891
1892 // No Release file was present, or verification failed, so fall
1893 // back to queueing Packages files without verification
1894 QueueIndexes(false);
1895 }
1896 /*}}}*/
1897
1898 void pkgAcqMetaIndex::Finished()
1899 {
1900 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1901 std::clog << "Finished: " << DestFile <<std::endl;
1902 if(Owner->TransactionHasError(TransactionID) == false &&
1903 TransactionID > 0)
1904 Owner->CommitTransaction(TransactionID);
1905 }
1906
1907
1908 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
1909 string const &URI, string const &URIDesc, string const &ShortDesc,
1910 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
1911 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
1912 const vector<IndexTarget*>* IndexTargets,
1913 indexRecords* MetaIndexParser) :
1914 pkgAcqMetaIndex(Owner, (unsigned long)this, URI, URIDesc, ShortDesc, MetaSigURI, MetaSigURIDesc,MetaSigShortDesc, IndexTargets, MetaIndexParser),
1915 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1916 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
1917 {
1918 SigFile = DestFile;
1919
1920 // index targets + (worst case:) Release/Release.gpg
1921 ExpectedAdditionalItems = IndexTargets->size() + 2;
1922
1923 #if 0
1924 // keep the old InRelease around in case of transistent network errors
1925 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1926 if (RealFileExists(Final) == true)
1927 {
1928 string const LastGoodSig = DestFile + ".reverify";
1929 Rename(Final,LastGoodSig);
1930 }
1931 #endif
1932 }
1933 /*}}}*/
1934 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1935 {
1936 #if 0
1937 // if the file was never queued undo file-changes done in the constructor
1938 if (QueueCounter == 1 && Status == StatIdle && FileSize == 0 && Complete == false)
1939 {
1940 string const Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
1941 string const LastGoodSig = DestFile + ".reverify";
1942 if (RealFileExists(Final) == false && RealFileExists(LastGoodSig) == true)
1943 Rename(LastGoodSig, Final);
1944 }
1945 #endif
1946 }
1947 /*}}}*/
1948 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1949 // ---------------------------------------------------------------------
1950 // FIXME: this can go away once the InRelease file is used widely
1951 string pkgAcqMetaClearSig::Custom600Headers() const
1952 {
1953 string Final = _config->FindDir("Dir::State::lists");
1954 Final += URItoFileName(RealURI);
1955
1956 struct stat Buf;
1957 if (stat(Final.c_str(),&Buf) != 0)
1958 {
1959 if (stat(Final.c_str(),&Buf) != 0)
1960 return "\nIndex-File: true\nFail-Ignore: true\n";
1961 }
1962
1963 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1964 }
1965 /*}}}*/
1966 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1967 {
1968 // we failed, we will not get additional items from this method
1969 ExpectedAdditionalItems = 0;
1970
1971 if (AuthPass == false)
1972 {
1973 // Queue the 'old' InRelease file for removal if we try Release.gpg
1974 // as otherwise the file will stay around and gives a false-auth
1975 // impression (CVE-2012-0214)
1976 string FinalFile = _config->FindDir("Dir::State::lists");
1977 FinalFile.append(URItoFileName(RealURI));
1978 PartialFile = "";
1979 DestFile = FinalFile;
1980
1981 new pkgAcqMetaIndex(Owner, TransactionID,
1982 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1983 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
1984 IndexTargets, MetaIndexParser);
1985 if (Cnf->LocalOnly == true ||
1986 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
1987 Dequeue();
1988 }
1989 else
1990 pkgAcqMetaIndex::Failed(Message, Cnf);
1991 }
1992 /*}}}*/
1993 // AcqArchive::AcqArchive - Constructor /*{{{*/
1994 // ---------------------------------------------------------------------
1995 /* This just sets up the initial fetch environment and queues the first
1996 possibilitiy */
1997 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1998 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1999 string &StoreFilename) :
2000 Item(Owner, HashStringList()), Version(Version), Sources(Sources), Recs(Recs),
2001 StoreFilename(StoreFilename), Vf(Version.FileList()),
2002 Trusted(false)
2003 {
2004 Retries = _config->FindI("Acquire::Retries",0);
2005
2006 if (Version.Arch() == 0)
2007 {
2008 _error->Error(_("I wasn't able to locate a file for the %s package. "
2009 "This might mean you need to manually fix this package. "
2010 "(due to missing arch)"),
2011 Version.ParentPkg().FullName().c_str());
2012 return;
2013 }
2014
2015 /* We need to find a filename to determine the extension. We make the
2016 assumption here that all the available sources for this version share
2017 the same extension.. */
2018 // Skip not source sources, they do not have file fields.
2019 for (; Vf.end() == false; ++Vf)
2020 {
2021 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2022 continue;
2023 break;
2024 }
2025
2026 // Does not really matter here.. we are going to fail out below
2027 if (Vf.end() != true)
2028 {
2029 // If this fails to get a file name we will bomb out below.
2030 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2031 if (_error->PendingError() == true)
2032 return;
2033
2034 // Generate the final file name as: package_version_arch.foo
2035 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2036 QuoteString(Version.VerStr(),"_:") + '_' +
2037 QuoteString(Version.Arch(),"_:.") +
2038 "." + flExtension(Parse.FileName());
2039 }
2040
2041 // check if we have one trusted source for the package. if so, switch
2042 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2043 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2044 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2045 bool seenUntrusted = false;
2046 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2047 {
2048 pkgIndexFile *Index;
2049 if (Sources->FindIndex(i.File(),Index) == false)
2050 continue;
2051
2052 if (debugAuth == true)
2053 std::cerr << "Checking index: " << Index->Describe()
2054 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2055
2056 if (Index->IsTrusted() == true)
2057 {
2058 Trusted = true;
2059 if (allowUnauth == false)
2060 break;
2061 }
2062 else
2063 seenUntrusted = true;
2064 }
2065
2066 // "allow-unauthenticated" restores apts old fetching behaviour
2067 // that means that e.g. unauthenticated file:// uris are higher
2068 // priority than authenticated http:// uris
2069 if (allowUnauth == true && seenUntrusted == true)
2070 Trusted = false;
2071
2072 // Select a source
2073 if (QueueNext() == false && _error->PendingError() == false)
2074 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2075 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2076 }
2077 /*}}}*/
2078 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2079 // ---------------------------------------------------------------------
2080 /* This queues the next available file version for download. It checks if
2081 the archive is already available in the cache and stashs the MD5 for
2082 checking later. */
2083 bool pkgAcqArchive::QueueNext()
2084 {
2085 for (; Vf.end() == false; ++Vf)
2086 {
2087 // Ignore not source sources
2088 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
2089 continue;
2090
2091 // Try to cross match against the source list
2092 pkgIndexFile *Index;
2093 if (Sources->FindIndex(Vf.File(),Index) == false)
2094 continue;
2095
2096 // only try to get a trusted package from another source if that source
2097 // is also trusted
2098 if(Trusted && !Index->IsTrusted())
2099 continue;
2100
2101 // Grab the text package record
2102 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2103 if (_error->PendingError() == true)
2104 return false;
2105
2106 string PkgFile = Parse.FileName();
2107 ExpectedHashes = Parse.Hashes();
2108
2109 if (PkgFile.empty() == true)
2110 return _error->Error(_("The package index files are corrupted. No Filename: "
2111 "field for package %s."),
2112 Version.ParentPkg().Name());
2113
2114 Desc.URI = Index->ArchiveURI(PkgFile);
2115 Desc.Description = Index->ArchiveInfo(Version);
2116 Desc.Owner = this;
2117 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2118
2119 // See if we already have the file. (Legacy filenames)
2120 FileSize = Version->Size;
2121 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2122 struct stat Buf;
2123 if (stat(FinalFile.c_str(),&Buf) == 0)
2124 {
2125 // Make sure the size matches
2126 if ((unsigned long long)Buf.st_size == Version->Size)
2127 {
2128 Complete = true;
2129 Local = true;
2130 Status = StatDone;
2131 StoreFilename = DestFile = FinalFile;
2132 return true;
2133 }
2134
2135 /* Hmm, we have a file and its size does not match, this means it is
2136 an old style mismatched arch */
2137 unlink(FinalFile.c_str());
2138 }
2139
2140 // Check it again using the new style output filenames
2141 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2142 if (stat(FinalFile.c_str(),&Buf) == 0)
2143 {
2144 // Make sure the size matches
2145 if ((unsigned long long)Buf.st_size == Version->Size)
2146 {
2147 Complete = true;
2148 Local = true;
2149 Status = StatDone;
2150 StoreFilename = DestFile = FinalFile;
2151 return true;
2152 }
2153
2154 /* Hmm, we have a file and its size does not match, this shouldn't
2155 happen.. */
2156 unlink(FinalFile.c_str());
2157 }
2158
2159 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2160
2161 // Check the destination file
2162 if (stat(DestFile.c_str(),&Buf) == 0)
2163 {
2164 // Hmm, the partial file is too big, erase it
2165 if ((unsigned long long)Buf.st_size > Version->Size)
2166 unlink(DestFile.c_str());
2167 else
2168 PartialSize = Buf.st_size;
2169 }
2170
2171 // Disables download of archives - useful if no real installation follows,
2172 // e.g. if we are just interested in proposed installation order
2173 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2174 {
2175 Complete = true;
2176 Local = true;
2177 Status = StatDone;
2178 StoreFilename = DestFile = FinalFile;
2179 return true;
2180 }
2181
2182 // Create the item
2183 Local = false;
2184 QueueURI(Desc);
2185
2186 ++Vf;
2187 return true;
2188 }
2189 return false;
2190 }
2191 /*}}}*/
2192 // AcqArchive::Done - Finished fetching /*{{{*/
2193 // ---------------------------------------------------------------------
2194 /* */
2195 void pkgAcqArchive::Done(string Message,unsigned long long Size, HashStringList const &CalcHashes,
2196 pkgAcquire::MethodConfig *Cfg)
2197 {
2198 Item::Done(Message, Size, CalcHashes, Cfg);
2199
2200 // Check the size
2201 if (Size != Version->Size)
2202 {
2203 RenameOnError(SizeMismatch);
2204 return;
2205 }
2206
2207 // FIXME: could this empty() check impose *any* sort of security issue?
2208 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2209 {
2210 RenameOnError(HashSumMismatch);
2211 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2212 return;
2213 }
2214
2215 // Grab the output filename
2216 string FileName = LookupTag(Message,"Filename");
2217 if (FileName.empty() == true)
2218 {
2219 Status = StatError;
2220 ErrorText = "Method gave a blank filename";
2221 return;
2222 }
2223
2224 Complete = true;
2225
2226 // Reference filename
2227 if (FileName != DestFile)
2228 {
2229 StoreFilename = DestFile = FileName;
2230 Local = true;
2231 return;
2232 }
2233
2234 // Done, move it into position
2235 string FinalFile = _config->FindDir("Dir::Cache::Archives");
2236 FinalFile += flNotDir(StoreFilename);
2237 Rename(DestFile,FinalFile);
2238
2239 StoreFilename = DestFile = FinalFile;
2240 Complete = true;
2241 }
2242 /*}}}*/
2243 // AcqArchive::Failed - Failure handler /*{{{*/
2244 // ---------------------------------------------------------------------
2245 /* Here we try other sources */
2246 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2247 {
2248 ErrorText = LookupTag(Message,"Message");
2249
2250 /* We don't really want to retry on failed media swaps, this prevents
2251 that. An interesting observation is that permanent failures are not
2252 recorded. */
2253 if (Cnf->Removable == true &&
2254 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2255 {
2256 // Vf = Version.FileList();
2257 while (Vf.end() == false) ++Vf;
2258 StoreFilename = string();
2259 Item::Failed(Message,Cnf);
2260 return;
2261 }
2262
2263 if (QueueNext() == false)
2264 {
2265 // This is the retry counter
2266 if (Retries != 0 &&
2267 Cnf->LocalOnly == false &&
2268 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2269 {
2270 Retries--;
2271 Vf = Version.FileList();
2272 if (QueueNext() == true)
2273 return;
2274 }
2275
2276 StoreFilename = string();
2277 Item::Failed(Message,Cnf);
2278 }
2279 }
2280 /*}}}*/
2281 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
2282 // ---------------------------------------------------------------------
2283 APT_PURE bool pkgAcqArchive::IsTrusted() const
2284 {
2285 return Trusted;
2286 }
2287 /*}}}*/
2288 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
2289 // ---------------------------------------------------------------------
2290 /* */
2291 void pkgAcqArchive::Finished()
2292 {
2293 if (Status == pkgAcquire::Item::StatDone &&
2294 Complete == true)
2295 return;
2296 StoreFilename = string();
2297 }
2298 /*}}}*/
2299 // AcqFile::pkgAcqFile - Constructor /*{{{*/
2300 // ---------------------------------------------------------------------
2301 /* The file is added to the queue */
2302 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI, HashStringList const &Hashes,
2303 unsigned long long Size,string Dsc,string ShortDesc,
2304 const string &DestDir, const string &DestFilename,
2305 bool IsIndexFile) :
2306 Item(Owner, Hashes), IsIndexFile(IsIndexFile)
2307 {
2308 Retries = _config->FindI("Acquire::Retries",0);
2309
2310 if(!DestFilename.empty())
2311 DestFile = DestFilename;
2312 else if(!DestDir.empty())
2313 DestFile = DestDir + "/" + flNotDir(URI);
2314 else
2315 DestFile = flNotDir(URI);
2316
2317 // Create the item
2318 Desc.URI = URI;
2319 Desc.Description = Dsc;
2320 Desc.Owner = this;
2321
2322 // Set the short description to the archive component
2323 Desc.ShortDesc = ShortDesc;
2324
2325 // Get the transfer sizes
2326 FileSize = Size;
2327 struct stat Buf;
2328 if (stat(DestFile.c_str(),&Buf) == 0)
2329 {
2330 // Hmm, the partial file is too big, erase it
2331 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
2332 unlink(DestFile.c_str());
2333 else
2334 PartialSize = Buf.st_size;
2335 }
2336
2337 QueueURI(Desc);
2338 }
2339 /*}}}*/
2340 // AcqFile::Done - Item downloaded OK /*{{{*/
2341 // ---------------------------------------------------------------------
2342 /* */
2343 void pkgAcqFile::Done(string Message,unsigned long long Size,HashStringList const &CalcHashes,
2344 pkgAcquire::MethodConfig *Cnf)
2345 {
2346 Item::Done(Message,Size,CalcHashes,Cnf);
2347
2348 // Check the hash
2349 if(ExpectedHashes.usable() && ExpectedHashes != CalcHashes)
2350 {
2351 RenameOnError(HashSumMismatch);
2352 printHashSumComparision(DestFile, ExpectedHashes, CalcHashes);
2353 return;
2354 }
2355
2356 string FileName = LookupTag(Message,"Filename");
2357 if (FileName.empty() == true)
2358 {
2359 Status = StatError;
2360 ErrorText = "Method gave a blank filename";
2361 return;
2362 }
2363
2364 Complete = true;
2365
2366 // The files timestamp matches
2367 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2368 return;
2369
2370 // We have to copy it into place
2371 if (FileName != DestFile)
2372 {
2373 Local = true;
2374 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2375 Cnf->Removable == true)
2376 {
2377 Desc.URI = "copy:" + FileName;
2378 QueueURI(Desc);
2379 return;
2380 }
2381
2382 // Erase the file if it is a symlink so we can overwrite it
2383 struct stat St;
2384 if (lstat(DestFile.c_str(),&St) == 0)
2385 {
2386 if (S_ISLNK(St.st_mode) != 0)
2387 unlink(DestFile.c_str());
2388 }
2389
2390 // Symlink the file
2391 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2392 {
2393 ErrorText = "Link to " + DestFile + " failure ";
2394 Status = StatError;
2395 Complete = false;
2396 }
2397 }
2398 }
2399 /*}}}*/
2400 // AcqFile::Failed - Failure handler /*{{{*/
2401 // ---------------------------------------------------------------------
2402 /* Here we try other sources */
2403 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2404 {
2405 ErrorText = LookupTag(Message,"Message");
2406
2407 // This is the retry counter
2408 if (Retries != 0 &&
2409 Cnf->LocalOnly == false &&
2410 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2411 {
2412 Retries--;
2413 QueueURI(Desc);
2414 return;
2415 }
2416
2417 Item::Failed(Message,Cnf);
2418 }
2419 /*}}}*/
2420 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2421 // ---------------------------------------------------------------------
2422 /* The only header we use is the last-modified header. */
2423 string pkgAcqFile::Custom600Headers() const
2424 {
2425 if (IsIndexFile)
2426 return "\nIndex-File: true";
2427 return "";
2428 }
2429 /*}}}*/