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