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