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