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