merge with debian/sid
[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 if possible
1272 MetaIndexParser->Load(DestFile);
1273 QueueIndexes(false);
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 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1392 if (Record == NULL)
1393 {
1394 if (verify == true && (*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 (verify == true && 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 if ((*Target)->IsOptional() == true)
1419 {
1420 if ((*Target)->IsSubIndex() == true)
1421 new pkgAcqSubIndex(Owner, (*Target)->URI, (*Target)->Description,
1422 (*Target)->ShortDesc, ExpectedIndexHash);
1423 else
1424 new pkgAcqIndexTrans(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
1425 continue;
1426 }
1427
1428 /* Queue Packages file (either diff or full packages files, depending
1429 on the users option) - we also check if the PDiff Index file is listed
1430 in the Meta-Index file. Ideal would be if pkgAcqDiffIndex would test this
1431 instead, but passing the required info to it is to much hassle */
1432 if(_config->FindB("Acquire::PDiffs",true) == true && (verify == false ||
1433 MetaIndexParser->Exists(string((*Target)->MetaKey).append(".diff/Index")) == true))
1434 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
1435 (*Target)->ShortDesc, ExpectedIndexHash);
1436 else
1437 new pkgAcqIndex(Owner, *Target, ExpectedIndexHash, MetaIndexParser);
1438 }
1439 }
1440 /*}}}*/
1441 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1442 {
1443 string::size_type pos;
1444
1445 // check for missing sigs (that where not fatal because otherwise we had
1446 // bombed earlier)
1447 string missingkeys;
1448 string msg = _("There is no public key available for the "
1449 "following key IDs:\n");
1450 pos = Message.find("NO_PUBKEY ");
1451 if (pos != std::string::npos)
1452 {
1453 string::size_type start = pos+strlen("NO_PUBKEY ");
1454 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1455 missingkeys += (Fingerprint);
1456 }
1457 if(!missingkeys.empty())
1458 _error->Warning("%s", string(msg+missingkeys).c_str());
1459
1460 string Transformed = MetaIndexParser->GetExpectedDist();
1461
1462 if (Transformed == "../project/experimental")
1463 {
1464 Transformed = "experimental";
1465 }
1466
1467 pos = Transformed.rfind('/');
1468 if (pos != string::npos)
1469 {
1470 Transformed = Transformed.substr(0, pos);
1471 }
1472
1473 if (Transformed == ".")
1474 {
1475 Transformed = "";
1476 }
1477
1478 if (_config->FindB("Acquire::Check-Valid-Until", true) == true &&
1479 MetaIndexParser->GetValidUntil() > 0) {
1480 time_t const invalid_since = time(NULL) - MetaIndexParser->GetValidUntil();
1481 if (invalid_since > 0)
1482 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1483 // the time since then the file is invalid - formated in the same way as in
1484 // the download progress display (e.g. 7d 3h 42min 1s)
1485 return _error->Error(
1486 _("Release file for %s is expired (invalid since %s). "
1487 "Updates for this repository will not be applied."),
1488 RealURI.c_str(), TimeToStr(invalid_since).c_str());
1489 }
1490
1491 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1492 {
1493 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1494 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1495 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1496 }
1497
1498 if (MetaIndexParser->CheckDist(Transformed) == false)
1499 {
1500 // This might become fatal one day
1501 // Status = StatAuthError;
1502 // ErrorText = "Conflicting distribution; expected "
1503 // + MetaIndexParser->GetExpectedDist() + " but got "
1504 // + MetaIndexParser->GetDist();
1505 // return false;
1506 if (!Transformed.empty())
1507 {
1508 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1509 Desc.Description.c_str(),
1510 Transformed.c_str(),
1511 MetaIndexParser->GetDist().c_str());
1512 }
1513 }
1514
1515 return true;
1516 }
1517 /*}}}*/
1518 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1519 // ---------------------------------------------------------------------
1520 /* */
1521 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1522 {
1523 if (AuthPass == true)
1524 {
1525 // gpgv method failed, if we have a good signature
1526 string LastGoodSigFile = _config->FindDir("Dir::State::lists");
1527 if (DestFile == SigFile)
1528 LastGoodSigFile.append(URItoFileName(RealURI));
1529 else
1530 LastGoodSigFile.append("partial/").append(URItoFileName(RealURI)).append(".gpg.reverify");
1531
1532 if(FileExists(LastGoodSigFile))
1533 {
1534 if (DestFile != SigFile)
1535 {
1536 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1537 URItoFileName(RealURI) + ".gpg";
1538 Rename(LastGoodSigFile,VerifiedSigFile);
1539 }
1540 Status = StatTransientNetworkError;
1541 _error->Warning(_("A error occurred during the signature "
1542 "verification. The repository is not updated "
1543 "and the previous index files will be used. "
1544 "GPG error: %s: %s\n"),
1545 Desc.Description.c_str(),
1546 LookupTag(Message,"Message").c_str());
1547 RunScripts("APT::Update::Auth-Failure");
1548 return;
1549 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
1550 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
1551 _error->Error(_("GPG error: %s: %s"),
1552 Desc.Description.c_str(),
1553 LookupTag(Message,"Message").c_str());
1554 return;
1555 } else {
1556 _error->Warning(_("GPG error: %s: %s"),
1557 Desc.Description.c_str(),
1558 LookupTag(Message,"Message").c_str());
1559 }
1560 // gpgv method failed
1561 ReportMirrorFailure("GPGFailure");
1562 }
1563
1564 /* Always move the meta index, even if gpgv failed. This ensures
1565 * that PackageFile objects are correctly filled in */
1566 if (FileExists(DestFile)) {
1567 string FinalFile = _config->FindDir("Dir::State::lists");
1568 FinalFile += URItoFileName(RealURI);
1569 /* InRelease files become Release files, otherwise
1570 * they would be considered as trusted later on */
1571 if (SigFile == DestFile) {
1572 RealURI = RealURI.replace(RealURI.rfind("InRelease"), 9,
1573 "Release");
1574 FinalFile = FinalFile.replace(FinalFile.rfind("InRelease"), 9,
1575 "Release");
1576 SigFile = FinalFile;
1577 }
1578 Rename(DestFile,FinalFile);
1579 chmod(FinalFile.c_str(),0644);
1580
1581 DestFile = FinalFile;
1582 }
1583
1584 // No Release file was present, or verification failed, so fall
1585 // back to queueing Packages files without verification
1586 QueueIndexes(false);
1587 }
1588 /*}}}*/
1589 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire *Owner, /*{{{*/
1590 string const &URI, string const &URIDesc, string const &ShortDesc,
1591 string const &MetaIndexURI, string const &MetaIndexURIDesc, string const &MetaIndexShortDesc,
1592 string const &MetaSigURI, string const &MetaSigURIDesc, string const &MetaSigShortDesc,
1593 const vector<struct IndexTarget*>* IndexTargets,
1594 indexRecords* MetaIndexParser) :
1595 pkgAcqMetaIndex(Owner, URI, URIDesc, ShortDesc, "", IndexTargets, MetaIndexParser),
1596 MetaIndexURI(MetaIndexURI), MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
1597 MetaSigURI(MetaSigURI), MetaSigURIDesc(MetaSigURIDesc), MetaSigShortDesc(MetaSigShortDesc)
1598 {
1599 SigFile = DestFile;
1600 }
1601 /*}}}*/
1602 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1603 // ---------------------------------------------------------------------
1604 // FIXME: this can go away once the InRelease file is used widely
1605 string pkgAcqMetaClearSig::Custom600Headers()
1606 {
1607 string Final = _config->FindDir("Dir::State::lists");
1608 Final += URItoFileName(RealURI);
1609
1610 struct stat Buf;
1611 if (stat(Final.c_str(),&Buf) != 0)
1612 return "\nIndex-File: true\nFail-Ignore: true\n";
1613
1614 return "\nIndex-File: true\nFail-Ignore: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1615 }
1616 /*}}}*/
1617 void pkgAcqMetaClearSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
1618 {
1619 if (AuthPass == false)
1620 {
1621 new pkgAcqMetaSig(Owner,
1622 MetaSigURI, MetaSigURIDesc, MetaSigShortDesc,
1623 MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
1624 IndexTargets, MetaIndexParser);
1625 if (Cnf->LocalOnly == true ||
1626 StringToBool(LookupTag(Message, "Transient-Failure"), false) == false)
1627 Dequeue();
1628 }
1629 else
1630 pkgAcqMetaIndex::Failed(Message, Cnf);
1631 }
1632 /*}}}*/
1633 // AcqArchive::AcqArchive - Constructor /*{{{*/
1634 // ---------------------------------------------------------------------
1635 /* This just sets up the initial fetch environment and queues the first
1636 possibilitiy */
1637 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1638 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1639 string &StoreFilename) :
1640 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
1641 StoreFilename(StoreFilename), Vf(Version.FileList()),
1642 Trusted(false)
1643 {
1644 Retries = _config->FindI("Acquire::Retries",0);
1645
1646 if (Version.Arch() == 0)
1647 {
1648 _error->Error(_("I wasn't able to locate a file for the %s package. "
1649 "This might mean you need to manually fix this package. "
1650 "(due to missing arch)"),
1651 Version.ParentPkg().Name());
1652 return;
1653 }
1654
1655 /* We need to find a filename to determine the extension. We make the
1656 assumption here that all the available sources for this version share
1657 the same extension.. */
1658 // Skip not source sources, they do not have file fields.
1659 for (; Vf.end() == false; Vf++)
1660 {
1661 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1662 continue;
1663 break;
1664 }
1665
1666 // Does not really matter here.. we are going to fail out below
1667 if (Vf.end() != true)
1668 {
1669 // If this fails to get a file name we will bomb out below.
1670 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1671 if (_error->PendingError() == true)
1672 return;
1673
1674 // Generate the final file name as: package_version_arch.foo
1675 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1676 QuoteString(Version.VerStr(),"_:") + '_' +
1677 QuoteString(Version.Arch(),"_:.") +
1678 "." + flExtension(Parse.FileName());
1679 }
1680
1681 // check if we have one trusted source for the package. if so, switch
1682 // to "TrustedOnly" mode
1683 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
1684 {
1685 pkgIndexFile *Index;
1686 if (Sources->FindIndex(i.File(),Index) == false)
1687 continue;
1688 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1689 {
1690 std::cerr << "Checking index: " << Index->Describe()
1691 << "(Trusted=" << Index->IsTrusted() << ")\n";
1692 }
1693 if (Index->IsTrusted()) {
1694 Trusted = true;
1695 break;
1696 }
1697 }
1698
1699 // "allow-unauthenticated" restores apts old fetching behaviour
1700 // that means that e.g. unauthenticated file:// uris are higher
1701 // priority than authenticated http:// uris
1702 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
1703 Trusted = false;
1704
1705 // Select a source
1706 if (QueueNext() == false && _error->PendingError() == false)
1707 _error->Error(_("I wasn't able to locate a file for the %s package. "
1708 "This might mean you need to manually fix this package."),
1709 Version.ParentPkg().Name());
1710 }
1711 /*}}}*/
1712 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1713 // ---------------------------------------------------------------------
1714 /* This queues the next available file version for download. It checks if
1715 the archive is already available in the cache and stashs the MD5 for
1716 checking later. */
1717 bool pkgAcqArchive::QueueNext()
1718 {
1719 string const ForceHash = _config->Find("Acquire::ForceHash");
1720 for (; Vf.end() == false; ++Vf)
1721 {
1722 // Ignore not source sources
1723 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1724 continue;
1725
1726 // Try to cross match against the source list
1727 pkgIndexFile *Index;
1728 if (Sources->FindIndex(Vf.File(),Index) == false)
1729 continue;
1730
1731 // only try to get a trusted package from another source if that source
1732 // is also trusted
1733 if(Trusted && !Index->IsTrusted())
1734 continue;
1735
1736 // Grab the text package record
1737 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1738 if (_error->PendingError() == true)
1739 return false;
1740
1741 string PkgFile = Parse.FileName();
1742 if (ForceHash.empty() == false)
1743 {
1744 if(stringcasecmp(ForceHash, "sha512") == 0)
1745 ExpectedHash = HashString("SHA512", Parse.SHA512Hash());
1746 if(stringcasecmp(ForceHash, "sha256") == 0)
1747 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
1748 else if (stringcasecmp(ForceHash, "sha1") == 0)
1749 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
1750 else
1751 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1752 }
1753 else
1754 {
1755 string Hash;
1756 if ((Hash = Parse.SHA512Hash()).empty() == false)
1757 ExpectedHash = HashString("SHA512", Hash);
1758 else if ((Hash = Parse.SHA256Hash()).empty() == false)
1759 ExpectedHash = HashString("SHA256", Hash);
1760 else if ((Hash = Parse.SHA1Hash()).empty() == false)
1761 ExpectedHash = HashString("SHA1", Hash);
1762 else
1763 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1764 }
1765 if (PkgFile.empty() == true)
1766 return _error->Error(_("The package index files are corrupted. No Filename: "
1767 "field for package %s."),
1768 Version.ParentPkg().Name());
1769
1770 Desc.URI = Index->ArchiveURI(PkgFile);
1771 Desc.Description = Index->ArchiveInfo(Version);
1772 Desc.Owner = this;
1773 Desc.ShortDesc = Version.ParentPkg().Name();
1774
1775 // See if we already have the file. (Legacy filenames)
1776 FileSize = Version->Size;
1777 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1778 struct stat Buf;
1779 if (stat(FinalFile.c_str(),&Buf) == 0)
1780 {
1781 // Make sure the size matches
1782 if ((unsigned long long)Buf.st_size == Version->Size)
1783 {
1784 Complete = true;
1785 Local = true;
1786 Status = StatDone;
1787 StoreFilename = DestFile = FinalFile;
1788 return true;
1789 }
1790
1791 /* Hmm, we have a file and its size does not match, this means it is
1792 an old style mismatched arch */
1793 unlink(FinalFile.c_str());
1794 }
1795
1796 // Check it again using the new style output filenames
1797 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1798 if (stat(FinalFile.c_str(),&Buf) == 0)
1799 {
1800 // Make sure the size matches
1801 if ((unsigned long long)Buf.st_size == Version->Size)
1802 {
1803 Complete = true;
1804 Local = true;
1805 Status = StatDone;
1806 StoreFilename = DestFile = FinalFile;
1807 return true;
1808 }
1809
1810 /* Hmm, we have a file and its size does not match, this shouldnt
1811 happen.. */
1812 unlink(FinalFile.c_str());
1813 }
1814
1815 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
1816
1817 // Check the destination file
1818 if (stat(DestFile.c_str(),&Buf) == 0)
1819 {
1820 // Hmm, the partial file is too big, erase it
1821 if ((unsigned long long)Buf.st_size > Version->Size)
1822 unlink(DestFile.c_str());
1823 else
1824 PartialSize = Buf.st_size;
1825 }
1826
1827 // Create the item
1828 Local = false;
1829 Desc.URI = Index->ArchiveURI(PkgFile);
1830 Desc.Description = Index->ArchiveInfo(Version);
1831 Desc.Owner = this;
1832 Desc.ShortDesc = Version.ParentPkg().Name();
1833 QueueURI(Desc);
1834
1835 ++Vf;
1836 return true;
1837 }
1838 return false;
1839 }
1840 /*}}}*/
1841 // AcqArchive::Done - Finished fetching /*{{{*/
1842 // ---------------------------------------------------------------------
1843 /* */
1844 void pkgAcqArchive::Done(string Message,unsigned long long Size,string CalcHash,
1845 pkgAcquire::MethodConfig *Cfg)
1846 {
1847 Item::Done(Message,Size,CalcHash,Cfg);
1848
1849 // Check the size
1850 if (Size != Version->Size)
1851 {
1852 Status = StatError;
1853 ErrorText = _("Size mismatch");
1854 return;
1855 }
1856
1857 // Check the hash
1858 if(ExpectedHash.toStr() != CalcHash)
1859 {
1860 Status = StatError;
1861 ErrorText = _("Hash Sum mismatch");
1862 if(FileExists(DestFile))
1863 Rename(DestFile,DestFile + ".FAILED");
1864 return;
1865 }
1866
1867 // Grab the output filename
1868 string FileName = LookupTag(Message,"Filename");
1869 if (FileName.empty() == true)
1870 {
1871 Status = StatError;
1872 ErrorText = "Method gave a blank filename";
1873 return;
1874 }
1875
1876 Complete = true;
1877
1878 // Reference filename
1879 if (FileName != DestFile)
1880 {
1881 StoreFilename = DestFile = FileName;
1882 Local = true;
1883 return;
1884 }
1885
1886 // Done, move it into position
1887 string FinalFile = _config->FindDir("Dir::Cache::Archives");
1888 FinalFile += flNotDir(StoreFilename);
1889 Rename(DestFile,FinalFile);
1890
1891 StoreFilename = DestFile = FinalFile;
1892 Complete = true;
1893 }
1894 /*}}}*/
1895 // AcqArchive::Failed - Failure handler /*{{{*/
1896 // ---------------------------------------------------------------------
1897 /* Here we try other sources */
1898 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1899 {
1900 ErrorText = LookupTag(Message,"Message");
1901
1902 /* We don't really want to retry on failed media swaps, this prevents
1903 that. An interesting observation is that permanent failures are not
1904 recorded. */
1905 if (Cnf->Removable == true &&
1906 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1907 {
1908 // Vf = Version.FileList();
1909 while (Vf.end() == false) ++Vf;
1910 StoreFilename = string();
1911 Item::Failed(Message,Cnf);
1912 return;
1913 }
1914
1915 if (QueueNext() == false)
1916 {
1917 // This is the retry counter
1918 if (Retries != 0 &&
1919 Cnf->LocalOnly == false &&
1920 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1921 {
1922 Retries--;
1923 Vf = Version.FileList();
1924 if (QueueNext() == true)
1925 return;
1926 }
1927
1928 StoreFilename = string();
1929 Item::Failed(Message,Cnf);
1930 }
1931 }
1932 /*}}}*/
1933 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
1934 // ---------------------------------------------------------------------
1935 bool pkgAcqArchive::IsTrusted()
1936 {
1937 return Trusted;
1938 }
1939 /*}}}*/
1940 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1941 // ---------------------------------------------------------------------
1942 /* */
1943 void pkgAcqArchive::Finished()
1944 {
1945 if (Status == pkgAcquire::Item::StatDone &&
1946 Complete == true)
1947 return;
1948 StoreFilename = string();
1949 }
1950 /*}}}*/
1951 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1952 // ---------------------------------------------------------------------
1953 /* The file is added to the queue */
1954 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
1955 unsigned long long Size,string Dsc,string ShortDesc,
1956 const string &DestDir, const string &DestFilename,
1957 bool IsIndexFile) :
1958 Item(Owner), ExpectedHash(Hash), IsIndexFile(IsIndexFile)
1959 {
1960 Retries = _config->FindI("Acquire::Retries",0);
1961
1962 if(!DestFilename.empty())
1963 DestFile = DestFilename;
1964 else if(!DestDir.empty())
1965 DestFile = DestDir + "/" + flNotDir(URI);
1966 else
1967 DestFile = flNotDir(URI);
1968
1969 // Create the item
1970 Desc.URI = URI;
1971 Desc.Description = Dsc;
1972 Desc.Owner = this;
1973
1974 // Set the short description to the archive component
1975 Desc.ShortDesc = ShortDesc;
1976
1977 // Get the transfer sizes
1978 FileSize = Size;
1979 struct stat Buf;
1980 if (stat(DestFile.c_str(),&Buf) == 0)
1981 {
1982 // Hmm, the partial file is too big, erase it
1983 if ((unsigned long long)Buf.st_size > Size)
1984 unlink(DestFile.c_str());
1985 else
1986 PartialSize = Buf.st_size;
1987 }
1988
1989 QueueURI(Desc);
1990 }
1991 /*}}}*/
1992 // AcqFile::Done - Item downloaded OK /*{{{*/
1993 // ---------------------------------------------------------------------
1994 /* */
1995 void pkgAcqFile::Done(string Message,unsigned long long Size,string CalcHash,
1996 pkgAcquire::MethodConfig *Cnf)
1997 {
1998 Item::Done(Message,Size,CalcHash,Cnf);
1999
2000 // Check the hash
2001 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
2002 {
2003 Status = StatError;
2004 ErrorText = _("Hash Sum mismatch");
2005 Rename(DestFile,DestFile + ".FAILED");
2006 return;
2007 }
2008
2009 string FileName = LookupTag(Message,"Filename");
2010 if (FileName.empty() == true)
2011 {
2012 Status = StatError;
2013 ErrorText = "Method gave a blank filename";
2014 return;
2015 }
2016
2017 Complete = true;
2018
2019 // The files timestamp matches
2020 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2021 return;
2022
2023 // We have to copy it into place
2024 if (FileName != DestFile)
2025 {
2026 Local = true;
2027 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
2028 Cnf->Removable == true)
2029 {
2030 Desc.URI = "copy:" + FileName;
2031 QueueURI(Desc);
2032 return;
2033 }
2034
2035 // Erase the file if it is a symlink so we can overwrite it
2036 struct stat St;
2037 if (lstat(DestFile.c_str(),&St) == 0)
2038 {
2039 if (S_ISLNK(St.st_mode) != 0)
2040 unlink(DestFile.c_str());
2041 }
2042
2043 // Symlink the file
2044 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
2045 {
2046 ErrorText = "Link to " + DestFile + " failure ";
2047 Status = StatError;
2048 Complete = false;
2049 }
2050 }
2051 }
2052 /*}}}*/
2053 // AcqFile::Failed - Failure handler /*{{{*/
2054 // ---------------------------------------------------------------------
2055 /* Here we try other sources */
2056 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
2057 {
2058 ErrorText = LookupTag(Message,"Message");
2059
2060 // This is the retry counter
2061 if (Retries != 0 &&
2062 Cnf->LocalOnly == false &&
2063 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2064 {
2065 Retries--;
2066 QueueURI(Desc);
2067 return;
2068 }
2069
2070 Item::Failed(Message,Cnf);
2071 }
2072 /*}}}*/
2073 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2074 // ---------------------------------------------------------------------
2075 /* The only header we use is the last-modified header. */
2076 string pkgAcqFile::Custom600Headers()
2077 {
2078 if (IsIndexFile)
2079 return "\nIndex-File: true";
2080 return "";
2081 }
2082 /*}}}*/