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