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