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