]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
44 strings left
[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/vendorlist.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/strutl.h>
23 #include <apt-pkg/fileutl.h>
24 #include <apt-pkg/md5.h>
25 #include <apt-pkg/sha1.h>
26 #include <apt-pkg/tagfile.h>
27
28 #include <apti18n.h>
29
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <string>
34 #include <sstream>
35 #include <stdio.h>
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 if (QueueCounter <= 1)
68 {
69 /* This indicates that the file is not available right now but might
70 be sometime later. If we do a retry cycle then this should be
71 retried [CDROMs] */
72 if (Cnf->LocalOnly == true &&
73 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
74 {
75 Status = StatIdle;
76 Dequeue();
77 return;
78 }
79
80 Status = StatError;
81 Dequeue();
82 }
83 }
84 /*}}}*/
85 // Acquire::Item::Start - Item has begun to download /*{{{*/
86 // ---------------------------------------------------------------------
87 /* Stash status and the file size. Note that setting Complete means
88 sub-phases of the acquire process such as decompresion are operating */
89 void pkgAcquire::Item::Start(string /*Message*/,unsigned long Size)
90 {
91 Status = StatFetching;
92 if (FileSize == 0 && Complete == false)
93 FileSize = Size;
94 }
95 /*}}}*/
96 // Acquire::Item::Done - Item downloaded OK /*{{{*/
97 // ---------------------------------------------------------------------
98 /* */
99 void pkgAcquire::Item::Done(string Message,unsigned long Size,string Hash,
100 pkgAcquire::MethodConfig *Cnf)
101 {
102 // We just downloaded something..
103 string FileName = LookupTag(Message,"Filename");
104 // we only inform the Log class if it was actually not a local thing
105 if (Complete == false && !Local && FileName == DestFile)
106 {
107 if (Owner->Log != 0)
108 Owner->Log->Fetched(Size,atoi(LookupTag(Message,"Resume-Point","0").c_str()));
109 }
110
111 if (FileSize == 0)
112 FileSize= Size;
113
114 Status = StatDone;
115 ErrorText = string();
116 Owner->Dequeue(this);
117 }
118 /*}}}*/
119 // Acquire::Item::Rename - Rename a file /*{{{*/
120 // ---------------------------------------------------------------------
121 /* This helper function is used by alot of item methods as thier final
122 step */
123 void pkgAcquire::Item::Rename(string From,string To)
124 {
125 if (rename(From.c_str(),To.c_str()) != 0)
126 {
127 char S[300];
128 snprintf(S,sizeof(S),_("rename failed, %s (%s -> %s)."),strerror(errno),
129 From.c_str(),To.c_str());
130 Status = StatError;
131 ErrorText = S;
132 }
133 }
134 /*}}}*/
135 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
136 // ---------------------------------------------------------------------
137 /* Get the DiffIndex file first and see if there are patches availabe
138 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
139 * patches. If anything goes wrong in that process, it will fall back to
140 * the original packages file
141 */
142 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire *Owner,
143 string URI,string URIDesc,string ShortDesc,
144 HashString ExpectedHash)
145 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
146 Description(URIDesc)
147 {
148
149 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
150
151 Desc.Description = URIDesc + "/DiffIndex";
152 Desc.Owner = this;
153 Desc.ShortDesc = ShortDesc;
154 Desc.URI = URI + ".diff/Index";
155
156 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
157 DestFile += URItoFileName(URI) + string(".DiffIndex");
158
159 if(Debug)
160 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
161
162 // look for the current package file
163 CurrentPackagesFile = _config->FindDir("Dir::State::lists");
164 CurrentPackagesFile += URItoFileName(RealURI);
165
166 // FIXME: this file:/ check is a hack to prevent fetching
167 // from local sources. this is really silly, and
168 // should be fixed cleanly as soon as possible
169 if(!FileExists(CurrentPackagesFile) ||
170 Desc.URI.substr(0,strlen("file:/")) == "file:/")
171 {
172 // we don't have a pkg file or we don't want to queue
173 if(Debug)
174 std::clog << "No index file, local or canceld by user" << std::endl;
175 Failed("", NULL);
176 return;
177 }
178
179 if(Debug)
180 std::clog << "pkgAcqIndexDiffs::pkgAcqIndexDiffs(): "
181 << CurrentPackagesFile << std::endl;
182
183 QueueURI(Desc);
184
185 }
186 /*}}}*/
187 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
188 // ---------------------------------------------------------------------
189 /* The only header we use is the last-modified header. */
190 string pkgAcqDiffIndex::Custom600Headers()
191 {
192 string Final = _config->FindDir("Dir::State::lists");
193 Final += URItoFileName(RealURI) + string(".IndexDiff");
194
195 if(Debug)
196 std::clog << "Custom600Header-IMS: " << Final << std::endl;
197
198 struct stat Buf;
199 if (stat(Final.c_str(),&Buf) != 0)
200 return "\nIndex-File: true";
201
202 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
203 }
204 /*}}}*/
205 bool pkgAcqDiffIndex::ParseDiffIndex(string IndexDiffFile) /*{{{*/
206 {
207 if(Debug)
208 std::clog << "pkgAcqIndexDiffs::ParseIndexDiff() " << IndexDiffFile
209 << std::endl;
210
211 pkgTagSection Tags;
212 string ServerSha1;
213 vector<DiffInfo> available_patches;
214
215 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
216 pkgTagFile TF(&Fd);
217 if (_error->PendingError() == true)
218 return false;
219
220 if(TF.Step(Tags) == true)
221 {
222 string local_sha1;
223 bool found = false;
224 DiffInfo d;
225 string size;
226
227 string tmp = Tags.FindS("SHA1-Current");
228 std::stringstream ss(tmp);
229 ss >> ServerSha1;
230
231 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly);
232 SHA1Summation SHA1;
233 SHA1.AddFD(fd.Fd(), fd.Size());
234 local_sha1 = string(SHA1.Result());
235
236 if(local_sha1 == ServerSha1)
237 {
238 // we have the same sha1 as the server
239 if(Debug)
240 std::clog << "Package file is up-to-date" << std::endl;
241 // set found to true, this will queue a pkgAcqIndexDiffs with
242 // a empty availabe_patches
243 found = true;
244 }
245 else
246 {
247 if(Debug)
248 std::clog << "SHA1-Current: " << ServerSha1 << std::endl;
249
250 // check the historie and see what patches we need
251 string history = Tags.FindS("SHA1-History");
252 std::stringstream hist(history);
253 while(hist >> d.sha1 >> size >> d.file)
254 {
255 d.size = atoi(size.c_str());
256 // read until the first match is found
257 if(d.sha1 == local_sha1)
258 found=true;
259 // from that point on, we probably need all diffs
260 if(found)
261 {
262 if(Debug)
263 std::clog << "Need to get diff: " << d.file << std::endl;
264 available_patches.push_back(d);
265 }
266 }
267 }
268
269 // we have something, queue the next diff
270 if(found)
271 {
272 // queue the diffs
273 string::size_type last_space = Description.rfind(" ");
274 if(last_space != string::npos)
275 Description.erase(last_space, Description.size()-last_space);
276 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
277 ExpectedHash, available_patches);
278 Complete = false;
279 Status = StatDone;
280 Dequeue();
281 return true;
282 }
283 }
284
285 // Nothing found, report and return false
286 // Failing here is ok, if we return false later, the full
287 // IndexFile is queued
288 if(Debug)
289 std::clog << "Can't find a patch in the index file" << std::endl;
290 return false;
291 }
292 /*}}}*/
293 void pkgAcqDiffIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
294 {
295 if(Debug)
296 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << std::endl
297 << "Falling back to normal index file aquire" << std::endl;
298
299 new pkgAcqIndex(Owner, RealURI, Description, Desc.ShortDesc,
300 ExpectedHash);
301
302 Complete = false;
303 Status = StatDone;
304 Dequeue();
305 }
306 /*}}}*/
307 void pkgAcqDiffIndex::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
308 pkgAcquire::MethodConfig *Cnf)
309 {
310 if(Debug)
311 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
312
313 Item::Done(Message,Size,Md5Hash,Cnf);
314
315 string FinalFile;
316 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
317
318 // sucess in downloading the index
319 // rename the index
320 FinalFile += string(".IndexDiff");
321 if(Debug)
322 std::clog << "Renaming: " << DestFile << " -> " << FinalFile
323 << std::endl;
324 Rename(DestFile,FinalFile);
325 chmod(FinalFile.c_str(),0644);
326 DestFile = FinalFile;
327
328 if(!ParseDiffIndex(DestFile))
329 return Failed("", NULL);
330
331 Complete = true;
332 Status = StatDone;
333 Dequeue();
334 return;
335 }
336 /*}}}*/
337 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
338 // ---------------------------------------------------------------------
339 /* The package diff is added to the queue. one object is constructed
340 * for each diff and the index
341 */
342 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire *Owner,
343 string URI,string URIDesc,string ShortDesc,
344 HashString ExpectedHash,
345 vector<DiffInfo> diffs)
346 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash),
347 available_patches(diffs)
348 {
349
350 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
351 DestFile += URItoFileName(URI);
352
353 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
354
355 Description = URIDesc;
356 Desc.Owner = this;
357 Desc.ShortDesc = ShortDesc;
358
359 if(available_patches.size() == 0)
360 {
361 // we are done (yeah!)
362 Finish(true);
363 }
364 else
365 {
366 // get the next diff
367 State = StateFetchDiff;
368 QueueNextDiff();
369 }
370 }
371 /*}}}*/
372 void pkgAcqIndexDiffs::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
373 {
374 if(Debug)
375 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << std::endl
376 << "Falling back to normal index file aquire" << std::endl;
377 new pkgAcqIndex(Owner, RealURI, Description,Desc.ShortDesc,
378 ExpectedHash);
379 Finish();
380 }
381 /*}}}*/
382 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
383 void pkgAcqIndexDiffs::Finish(bool allDone)
384 {
385 // we restore the original name, this is required, otherwise
386 // the file will be cleaned
387 if(allDone)
388 {
389 DestFile = _config->FindDir("Dir::State::lists");
390 DestFile += URItoFileName(RealURI);
391
392 if(!ExpectedHash.empty() && !ExpectedHash.VerifyFile(DestFile))
393 {
394 Status = StatAuthError;
395 ErrorText = _("MD5Sum mismatch");
396 Rename(DestFile,DestFile + ".FAILED");
397 Dequeue();
398 return;
399 }
400
401 // this is for the "real" finish
402 Complete = true;
403 Status = StatDone;
404 Dequeue();
405 if(Debug)
406 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
407 return;
408 }
409
410 if(Debug)
411 std::clog << "Finishing: " << Desc.URI << std::endl;
412 Complete = false;
413 Status = StatDone;
414 Dequeue();
415 return;
416 }
417 /*}}}*/
418 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
419 {
420
421 // calc sha1 of the just patched file
422 string FinalFile = _config->FindDir("Dir::State::lists");
423 FinalFile += URItoFileName(RealURI);
424
425 FileFd fd(FinalFile, FileFd::ReadOnly);
426 SHA1Summation SHA1;
427 SHA1.AddFD(fd.Fd(), fd.Size());
428 string local_sha1 = string(SHA1.Result());
429 if(Debug)
430 std::clog << "QueueNextDiff: "
431 << FinalFile << " (" << local_sha1 << ")"<<std::endl;
432
433 // remove all patches until the next matching patch is found
434 // this requires the Index file to be ordered
435 for(vector<DiffInfo>::iterator I=available_patches.begin();
436 available_patches.size() > 0 &&
437 I != available_patches.end() &&
438 (*I).sha1 != local_sha1;
439 I++)
440 {
441 available_patches.erase(I);
442 }
443
444 // error checking and falling back if no patch was found
445 if(available_patches.size() == 0)
446 {
447 Failed("", NULL);
448 return false;
449 }
450
451 // queue the right diff
452 Desc.URI = string(RealURI) + ".diff/" + available_patches[0].file + ".gz";
453 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
454 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
455 DestFile += URItoFileName(RealURI + ".diff/" + available_patches[0].file);
456
457 if(Debug)
458 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
459
460 QueueURI(Desc);
461
462 return true;
463 }
464 /*}}}*/
465 void pkgAcqIndexDiffs::Done(string Message,unsigned long Size,string Md5Hash, /*{{{*/
466 pkgAcquire::MethodConfig *Cnf)
467 {
468 if(Debug)
469 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
470
471 Item::Done(Message,Size,Md5Hash,Cnf);
472
473 string FinalFile;
474 FinalFile = _config->FindDir("Dir::State::lists")+URItoFileName(RealURI);
475
476 // sucess in downloading a diff, enter ApplyDiff state
477 if(State == StateFetchDiff)
478 {
479
480 if(Debug)
481 std::clog << "Sending to gzip method: " << FinalFile << std::endl;
482
483 string FileName = LookupTag(Message,"Filename");
484 State = StateUnzipDiff;
485 Local = true;
486 Desc.URI = "gzip:" + FileName;
487 DestFile += ".decomp";
488 QueueURI(Desc);
489 Mode = "gzip";
490 return;
491 }
492
493 // sucess in downloading a diff, enter ApplyDiff state
494 if(State == StateUnzipDiff)
495 {
496
497 // rred excepts the patch as $FinalFile.ed
498 Rename(DestFile,FinalFile+".ed");
499
500 if(Debug)
501 std::clog << "Sending to rred method: " << FinalFile << std::endl;
502
503 State = StateApplyDiff;
504 Local = true;
505 Desc.URI = "rred:" + FinalFile;
506 QueueURI(Desc);
507 Mode = "rred";
508 return;
509 }
510
511
512 // success in download/apply a diff, queue next (if needed)
513 if(State == StateApplyDiff)
514 {
515 // remove the just applied patch
516 available_patches.erase(available_patches.begin());
517
518 // move into place
519 if(Debug)
520 {
521 std::clog << "Moving patched file in place: " << std::endl
522 << DestFile << " -> " << FinalFile << std::endl;
523 }
524 Rename(DestFile,FinalFile);
525 chmod(FinalFile.c_str(),0644);
526
527 // see if there is more to download
528 if(available_patches.size() > 0) {
529 new pkgAcqIndexDiffs(Owner, RealURI, Description, Desc.ShortDesc,
530 ExpectedHash, available_patches);
531 return Finish();
532 } else
533 return Finish(true);
534 }
535 }
536 /*}}}*/
537 // AcqIndex::AcqIndex - Constructor /*{{{*/
538 // ---------------------------------------------------------------------
539 /* The package file is added to the queue and a second class is
540 instantiated to fetch the revision file */
541 pkgAcqIndex::pkgAcqIndex(pkgAcquire *Owner,
542 string URI,string URIDesc,string ShortDesc,
543 HashString ExpectedHash, string comprExt)
544 : Item(Owner), RealURI(URI), ExpectedHash(ExpectedHash)
545 {
546 Decompression = false;
547 Erase = false;
548
549 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
550 DestFile += URItoFileName(URI);
551
552 if(comprExt.empty())
553 {
554 // autoselect the compression method
555 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
556 if (types.empty() == true)
557 comprExt = "plain";
558 else
559 comprExt = "." + types[0];
560 }
561 CompressionExtension = ((comprExt == "plain" || comprExt == ".") ? "" : comprExt);
562
563 Desc.URI = URI + CompressionExtension;
564
565 Desc.Description = URIDesc;
566 Desc.Owner = this;
567 Desc.ShortDesc = ShortDesc;
568
569 QueueURI(Desc);
570 }
571 /*}}}*/
572 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
573 // ---------------------------------------------------------------------
574 /* The only header we use is the last-modified header. */
575 string pkgAcqIndex::Custom600Headers()
576 {
577 string Final = _config->FindDir("Dir::State::lists");
578 Final += URItoFileName(RealURI);
579
580 struct stat Buf;
581 if (stat(Final.c_str(),&Buf) != 0)
582 return "\nIndex-File: true";
583
584 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
585 }
586 /*}}}*/
587 void pkgAcqIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf) /*{{{*/
588 {
589 std::vector<std::string> types = APT::Configuration::getCompressionTypes();
590
591 for (std::vector<std::string>::const_iterator t = types.begin();
592 t != types.end(); t++)
593 {
594 // jump over all already tried compression types
595 const unsigned int nameLen = Desc.URI.size() - (*t).size();
596 if(Desc.URI.substr(nameLen) != *t)
597 continue;
598
599 // we want to try it with the next extension (and make sure to
600 // not skip over the end)
601 t++;
602 if (t == types.end())
603 break;
604
605 // queue new download
606 Desc.URI = Desc.URI.substr(0, nameLen) + *t;
607 new pkgAcqIndex(Owner, RealURI, Desc.Description, Desc.ShortDesc,
608 ExpectedHash, string(".").append(*t));
609
610 Status = StatDone;
611 Complete = false;
612 Dequeue();
613 return;
614 }
615
616 // on decompression failure, remove bad versions in partial/
617 if(Decompression && Erase) {
618 string s = _config->FindDir("Dir::State::lists") + "partial/";
619 s += URItoFileName(RealURI);
620 unlink(s.c_str());
621 }
622
623 Item::Failed(Message,Cnf);
624 }
625 /*}}}*/
626 // AcqIndex::Done - Finished a fetch /*{{{*/
627 // ---------------------------------------------------------------------
628 /* This goes through a number of states.. On the initial fetch the
629 method could possibly return an alternate filename which points
630 to the uncompressed version of the file. If this is so the file
631 is copied into the partial directory. In all other cases the file
632 is decompressed with a gzip uri. */
633 void pkgAcqIndex::Done(string Message,unsigned long Size,string Hash,
634 pkgAcquire::MethodConfig *Cfg)
635 {
636 Item::Done(Message,Size,Hash,Cfg);
637
638 if (Decompression == true)
639 {
640 if (_config->FindB("Debug::pkgAcquire::Auth", false))
641 {
642 std::cerr << std::endl << RealURI << ": Computed Hash: " << Hash;
643 std::cerr << " Expected Hash: " << ExpectedHash.toStr() << std::endl;
644 }
645
646 if (!ExpectedHash.empty() && ExpectedHash.toStr() != Hash)
647 {
648 Status = StatAuthError;
649 ErrorText = _("Hash Sum mismatch");
650 Rename(DestFile,DestFile + ".FAILED");
651 return;
652 }
653 // Done, move it into position
654 string FinalFile = _config->FindDir("Dir::State::lists");
655 FinalFile += URItoFileName(RealURI);
656 Rename(DestFile,FinalFile);
657 chmod(FinalFile.c_str(),0644);
658
659 /* We restore the original name to DestFile so that the clean operation
660 will work OK */
661 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
662 DestFile += URItoFileName(RealURI);
663
664 // Remove the compressed version.
665 if (Erase == true)
666 unlink(DestFile.c_str());
667 return;
668 }
669
670 Erase = false;
671 Complete = true;
672
673 // Handle the unzipd case
674 string FileName = LookupTag(Message,"Alt-Filename");
675 if (FileName.empty() == false)
676 {
677 // The files timestamp matches
678 if (StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false) == true)
679 return;
680 Decompression = true;
681 Local = true;
682 DestFile += ".decomp";
683 Desc.URI = "copy:" + FileName;
684 QueueURI(Desc);
685 Mode = "copy";
686 return;
687 }
688
689 FileName = LookupTag(Message,"Filename");
690 if (FileName.empty() == true)
691 {
692 Status = StatError;
693 ErrorText = "Method gave a blank filename";
694 }
695
696 // The files timestamp matches
697 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
698 return;
699
700 if (FileName == DestFile)
701 Erase = true;
702 else
703 Local = true;
704
705 string compExt = flExtension(flNotDir(URI(Desc.URI).Path));
706 string decompProg;
707
708 // get the binary name for your used compression type
709 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(compExt),"");
710 if(decompProg.empty() == false);
711 // flExtensions returns the full name if no extension is found
712 // this is why we have this complicated compare operation here
713 // FIMXE: add a new flJustExtension() that return "" if no
714 // extension is found and use that above so that it can
715 // be tested against ""
716 else if(compExt == flNotDir(URI(Desc.URI).Path))
717 decompProg = "copy";
718 else {
719 _error->Error("Unsupported extension: %s", compExt.c_str());
720 return;
721 }
722
723 Decompression = true;
724 DestFile += ".decomp";
725 Desc.URI = decompProg + ":" + FileName;
726 QueueURI(Desc);
727 Mode = decompProg.c_str();
728 }
729 /*}}}*/
730 // AcqIndexTrans::pkgAcqIndexTrans - Constructor /*{{{*/
731 // ---------------------------------------------------------------------
732 /* The Translation file is added to the queue */
733 pkgAcqIndexTrans::pkgAcqIndexTrans(pkgAcquire *Owner,
734 string URI,string URIDesc,string ShortDesc)
735 : pkgAcqIndex(Owner, URI, URIDesc, ShortDesc, HashString(), "")
736 {
737 }
738 /*}}}*/
739 // AcqIndexTrans::Failed - Silence failure messages for missing files /*{{{*/
740 // ---------------------------------------------------------------------
741 /* */
742 void pkgAcqIndexTrans::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
743 {
744 if (Cnf->LocalOnly == true ||
745 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
746 {
747 // Ignore this
748 Status = StatDone;
749 Complete = false;
750 Dequeue();
751 return;
752 }
753
754 Item::Failed(Message,Cnf);
755 }
756 /*}}}*/
757 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire *Owner, /*{{{*/
758 string URI,string URIDesc,string ShortDesc,
759 string MetaIndexURI, string MetaIndexURIDesc,
760 string MetaIndexShortDesc,
761 const vector<IndexTarget*>* IndexTargets,
762 indexRecords* MetaIndexParser) :
763 Item(Owner), RealURI(URI), MetaIndexURI(MetaIndexURI),
764 MetaIndexURIDesc(MetaIndexURIDesc), MetaIndexShortDesc(MetaIndexShortDesc),
765 MetaIndexParser(MetaIndexParser), IndexTargets(IndexTargets)
766 {
767 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
768 DestFile += URItoFileName(URI);
769
770 // remove any partial downloaded sig-file in partial/.
771 // it may confuse proxies and is too small to warrant a
772 // partial download anyway
773 unlink(DestFile.c_str());
774
775 // Create the item
776 Desc.Description = URIDesc;
777 Desc.Owner = this;
778 Desc.ShortDesc = ShortDesc;
779 Desc.URI = URI;
780
781 string Final = _config->FindDir("Dir::State::lists");
782 Final += URItoFileName(RealURI);
783 struct stat Buf;
784 if (stat(Final.c_str(),&Buf) == 0)
785 {
786 // File was already in place. It needs to be re-downloaded/verified
787 // because Release might have changed, we do give it a differnt
788 // name than DestFile because otherwise the http method will
789 // send If-Range requests and there are too many broken servers
790 // out there that do not understand them
791 LastGoodSig = DestFile+".reverify";
792 Rename(Final,LastGoodSig);
793 }
794
795 QueueURI(Desc);
796 }
797 /*}}}*/
798 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
799 // ---------------------------------------------------------------------
800 /* The only header we use is the last-modified header. */
801 string pkgAcqMetaSig::Custom600Headers()
802 {
803 struct stat Buf;
804 if (stat(LastGoodSig.c_str(),&Buf) != 0)
805 return "\nIndex-File: true";
806
807 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
808 }
809
810 void pkgAcqMetaSig::Done(string Message,unsigned long Size,string MD5,
811 pkgAcquire::MethodConfig *Cfg)
812 {
813 Item::Done(Message,Size,MD5,Cfg);
814
815 string FileName = LookupTag(Message,"Filename");
816 if (FileName.empty() == true)
817 {
818 Status = StatError;
819 ErrorText = "Method gave a blank filename";
820 return;
821 }
822
823 if (FileName != DestFile)
824 {
825 // We have to copy it into place
826 Local = true;
827 Desc.URI = "copy:" + FileName;
828 QueueURI(Desc);
829 return;
830 }
831
832 Complete = true;
833
834 // put the last known good file back on i-m-s hit (it will
835 // be re-verified again)
836 // Else do nothing, we have the new file in DestFile then
837 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
838 Rename(LastGoodSig, DestFile);
839
840 // queue a pkgAcqMetaIndex to be verified against the sig we just retrieved
841 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
842 DestFile, IndexTargets, MetaIndexParser);
843
844 }
845 /*}}}*/
846 void pkgAcqMetaSig::Failed(string Message,pkgAcquire::MethodConfig *Cnf)/*{{{*/
847 {
848 string Final = _config->FindDir("Dir::State::lists") + URItoFileName(RealURI);
849
850 // if we get a network error we fail gracefully
851 if(Status == StatTransientNetworkError)
852 {
853 Item::Failed(Message,Cnf);
854 // move the sigfile back on transient network failures
855 if(FileExists(DestFile))
856 Rename(LastGoodSig,Final);
857
858 // set the status back to , Item::Failed likes to reset it
859 Status = pkgAcquire::Item::StatTransientNetworkError;
860 return;
861 }
862
863 // Delete any existing sigfile when the acquire failed
864 unlink(Final.c_str());
865
866 // queue a pkgAcqMetaIndex with no sigfile
867 new pkgAcqMetaIndex(Owner, MetaIndexURI, MetaIndexURIDesc, MetaIndexShortDesc,
868 "", IndexTargets, MetaIndexParser);
869
870 if (Cnf->LocalOnly == true ||
871 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
872 {
873 // Ignore this
874 Status = StatDone;
875 Complete = false;
876 Dequeue();
877 return;
878 }
879
880 Item::Failed(Message,Cnf);
881 }
882 /*}}}*/
883 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire *Owner, /*{{{*/
884 string URI,string URIDesc,string ShortDesc,
885 string SigFile,
886 const vector<struct IndexTarget*>* IndexTargets,
887 indexRecords* MetaIndexParser) :
888 Item(Owner), RealURI(URI), SigFile(SigFile), IndexTargets(IndexTargets),
889 MetaIndexParser(MetaIndexParser), AuthPass(false), IMSHit(false)
890 {
891 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
892 DestFile += URItoFileName(URI);
893
894 // Create the item
895 Desc.Description = URIDesc;
896 Desc.Owner = this;
897 Desc.ShortDesc = ShortDesc;
898 Desc.URI = URI;
899
900 QueueURI(Desc);
901 }
902 /*}}}*/
903 // pkgAcqMetaIndex::Custom600Headers - Insert custom request headers /*{{{*/
904 // ---------------------------------------------------------------------
905 /* The only header we use is the last-modified header. */
906 string pkgAcqMetaIndex::Custom600Headers()
907 {
908 string Final = _config->FindDir("Dir::State::lists");
909 Final += URItoFileName(RealURI);
910
911 struct stat Buf;
912 if (stat(Final.c_str(),&Buf) != 0)
913 return "\nIndex-File: true";
914
915 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
916 }
917 /*}}}*/
918 void pkgAcqMetaIndex::Done(string Message,unsigned long Size,string Hash, /*{{{*/
919 pkgAcquire::MethodConfig *Cfg)
920 {
921 Item::Done(Message,Size,Hash,Cfg);
922
923 // MetaIndexes are done in two passes: one to download the
924 // metaindex with an appropriate method, and a second to verify it
925 // with the gpgv method
926
927 if (AuthPass == true)
928 {
929 AuthDone(Message);
930 }
931 else
932 {
933 RetrievalDone(Message);
934 if (!Complete)
935 // Still more retrieving to do
936 return;
937
938 if (SigFile == "")
939 {
940 // There was no signature file, so we are finished. Download
941 // the indexes without verification.
942 QueueIndexes(false);
943 }
944 else
945 {
946 // There was a signature file, so pass it to gpgv for
947 // verification
948
949 if (_config->FindB("Debug::pkgAcquire::Auth", false))
950 std::cerr << "Metaindex acquired, queueing gpg verification ("
951 << SigFile << "," << DestFile << ")\n";
952 AuthPass = true;
953 Desc.URI = "gpgv:" + SigFile;
954 QueueURI(Desc);
955 Mode = "gpgv";
956 }
957 }
958 }
959 /*}}}*/
960 void pkgAcqMetaIndex::RetrievalDone(string Message) /*{{{*/
961 {
962 // We have just finished downloading a Release file (it is not
963 // verified yet)
964
965 string FileName = LookupTag(Message,"Filename");
966 if (FileName.empty() == true)
967 {
968 Status = StatError;
969 ErrorText = "Method gave a blank filename";
970 return;
971 }
972
973 if (FileName != DestFile)
974 {
975 Local = true;
976 Desc.URI = "copy:" + FileName;
977 QueueURI(Desc);
978 return;
979 }
980
981 // see if the download was a IMSHit
982 IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false);
983 Complete = true;
984
985 string FinalFile = _config->FindDir("Dir::State::lists");
986 FinalFile += URItoFileName(RealURI);
987
988 // If we get a IMS hit we can remove the empty file in partial
989 // othersie we move the file in place
990 if (IMSHit)
991 unlink(DestFile.c_str());
992 else
993 Rename(DestFile,FinalFile);
994
995 chmod(FinalFile.c_str(),0644);
996 DestFile = FinalFile;
997 }
998 /*}}}*/
999 void pkgAcqMetaIndex::AuthDone(string Message) /*{{{*/
1000 {
1001 // At this point, the gpgv method has succeeded, so there is a
1002 // valid signature from a key in the trusted keyring. We
1003 // perform additional verification of its contents, and use them
1004 // to verify the indexes we are about to download
1005
1006 if (!MetaIndexParser->Load(DestFile))
1007 {
1008 Status = StatAuthError;
1009 ErrorText = MetaIndexParser->ErrorText;
1010 return;
1011 }
1012
1013 if (!VerifyVendor(Message))
1014 {
1015 return;
1016 }
1017
1018 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1019 std::cerr << "Signature verification succeeded: "
1020 << DestFile << std::endl;
1021
1022 // Download further indexes with verification
1023 QueueIndexes(true);
1024
1025 // Done, move signature file into position
1026
1027 string VerifiedSigFile = _config->FindDir("Dir::State::lists") +
1028 URItoFileName(RealURI) + ".gpg";
1029 Rename(SigFile,VerifiedSigFile);
1030 chmod(VerifiedSigFile.c_str(),0644);
1031 }
1032 /*}}}*/
1033 void pkgAcqMetaIndex::QueueIndexes(bool verify) /*{{{*/
1034 {
1035 for (vector <struct IndexTarget*>::const_iterator Target = IndexTargets->begin();
1036 Target != IndexTargets->end();
1037 Target++)
1038 {
1039 HashString ExpectedIndexHash;
1040 if (verify)
1041 {
1042 const indexRecords::checkSum *Record = MetaIndexParser->Lookup((*Target)->MetaKey);
1043 if (!Record)
1044 {
1045 Status = StatAuthError;
1046 ErrorText = "Unable to find expected entry "
1047 + (*Target)->MetaKey + " in Meta-index file (malformed Release file?)";
1048 return;
1049 }
1050 ExpectedIndexHash = Record->Hash;
1051 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1052 {
1053 std::cerr << "Queueing: " << (*Target)->URI << std::endl;
1054 std::cerr << "Expected Hash: " << ExpectedIndexHash.toStr() << std::endl;
1055 }
1056 if (ExpectedIndexHash.empty())
1057 {
1058 Status = StatAuthError;
1059 ErrorText = "Unable to find hash sum for "
1060 + (*Target)->MetaKey + " in Meta-index file";
1061 return;
1062 }
1063 }
1064
1065 // Queue Packages file (either diff or full packages files, depending
1066 // on the users option)
1067 if(_config->FindB("Acquire::PDiffs",true) == true)
1068 new pkgAcqDiffIndex(Owner, (*Target)->URI, (*Target)->Description,
1069 (*Target)->ShortDesc, ExpectedIndexHash);
1070 else
1071 new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description,
1072 (*Target)->ShortDesc, ExpectedIndexHash);
1073 }
1074 }
1075 /*}}}*/
1076 bool pkgAcqMetaIndex::VerifyVendor(string Message) /*{{{*/
1077 {
1078 // // Maybe this should be made available from above so we don't have
1079 // // to read and parse it every time?
1080 // pkgVendorList List;
1081 // List.ReadMainList();
1082
1083 // const Vendor* Vndr = NULL;
1084 // for (std::vector<string>::const_iterator I = GPGVOutput.begin(); I != GPGVOutput.end(); I++)
1085 // {
1086 // string::size_type pos = (*I).find("VALIDSIG ");
1087 // if (_config->FindB("Debug::Vendor", false))
1088 // std::cerr << "Looking for VALIDSIG in \"" << (*I) << "\": pos " << pos
1089 // << std::endl;
1090 // if (pos != std::string::npos)
1091 // {
1092 // string Fingerprint = (*I).substr(pos+sizeof("VALIDSIG"));
1093 // if (_config->FindB("Debug::Vendor", false))
1094 // std::cerr << "Looking for \"" << Fingerprint << "\" in vendor..." <<
1095 // std::endl;
1096 // Vndr = List.FindVendor(Fingerprint) != "";
1097 // if (Vndr != NULL);
1098 // break;
1099 // }
1100 // }
1101 string::size_type pos;
1102
1103 // check for missing sigs (that where not fatal because otherwise we had
1104 // bombed earlier)
1105 string missingkeys;
1106 string msg = _("There is no public key available for the "
1107 "following key IDs:\n");
1108 pos = Message.find("NO_PUBKEY ");
1109 if (pos != std::string::npos)
1110 {
1111 string::size_type start = pos+strlen("NO_PUBKEY ");
1112 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1113 missingkeys += (Fingerprint);
1114 }
1115 if(!missingkeys.empty())
1116 _error->Warning("%s", string(msg+missingkeys).c_str());
1117
1118 string Transformed = MetaIndexParser->GetExpectedDist();
1119
1120 if (Transformed == "../project/experimental")
1121 {
1122 Transformed = "experimental";
1123 }
1124
1125 pos = Transformed.rfind('/');
1126 if (pos != string::npos)
1127 {
1128 Transformed = Transformed.substr(0, pos);
1129 }
1130
1131 if (Transformed == ".")
1132 {
1133 Transformed = "";
1134 }
1135
1136 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1137 {
1138 std::cerr << "Got Codename: " << MetaIndexParser->GetDist() << std::endl;
1139 std::cerr << "Expecting Dist: " << MetaIndexParser->GetExpectedDist() << std::endl;
1140 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1141 }
1142
1143 if (MetaIndexParser->CheckDist(Transformed) == false)
1144 {
1145 // This might become fatal one day
1146 // Status = StatAuthError;
1147 // ErrorText = "Conflicting distribution; expected "
1148 // + MetaIndexParser->GetExpectedDist() + " but got "
1149 // + MetaIndexParser->GetDist();
1150 // return false;
1151 if (!Transformed.empty())
1152 {
1153 _error->Warning("Conflicting distribution: %s (expected %s but got %s)",
1154 Desc.Description.c_str(),
1155 Transformed.c_str(),
1156 MetaIndexParser->GetDist().c_str());
1157 }
1158 }
1159
1160 return true;
1161 }
1162 /*}}}*/
1163 // pkgAcqMetaIndex::Failed - no Release file present or no signature file present /*{{{*/
1164 // ---------------------------------------------------------------------
1165 /* */
1166 void pkgAcqMetaIndex::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1167 {
1168 if (AuthPass == true)
1169 {
1170 // if we fail the authentication but got the file via a IMS-Hit
1171 // this means that the file wasn't downloaded and that it might be
1172 // just stale (server problem, proxy etc). we delete what we have
1173 // queue it again without i-m-s
1174 // alternatively we could just unlink the file and let the user try again
1175 if (IMSHit)
1176 {
1177 Complete = false;
1178 Local = false;
1179 AuthPass = false;
1180 unlink(DestFile.c_str());
1181
1182 DestFile = _config->FindDir("Dir::State::lists") + "partial/";
1183 DestFile += URItoFileName(RealURI);
1184 Desc.URI = RealURI;
1185 QueueURI(Desc);
1186 return;
1187 }
1188
1189 // gpgv method failed
1190 _error->Warning("GPG error: %s: %s",
1191 Desc.Description.c_str(),
1192 LookupTag(Message,"Message").c_str());
1193
1194 }
1195
1196 // No Release file was present, or verification failed, so fall
1197 // back to queueing Packages files without verification
1198 QueueIndexes(false);
1199 }
1200 /*}}}*/
1201 // AcqArchive::AcqArchive - Constructor /*{{{*/
1202 // ---------------------------------------------------------------------
1203 /* This just sets up the initial fetch environment and queues the first
1204 possibilitiy */
1205 pkgAcqArchive::pkgAcqArchive(pkgAcquire *Owner,pkgSourceList *Sources,
1206 pkgRecords *Recs,pkgCache::VerIterator const &Version,
1207 string &StoreFilename) :
1208 Item(Owner), Version(Version), Sources(Sources), Recs(Recs),
1209 StoreFilename(StoreFilename), Vf(Version.FileList()),
1210 Trusted(false)
1211 {
1212 Retries = _config->FindI("Acquire::Retries",0);
1213
1214 if (Version.Arch() == 0)
1215 {
1216 _error->Error(_("I wasn't able to locate a file for the %s package. "
1217 "This might mean you need to manually fix this package. "
1218 "(due to missing arch)"),
1219 Version.ParentPkg().Name());
1220 return;
1221 }
1222
1223 /* We need to find a filename to determine the extension. We make the
1224 assumption here that all the available sources for this version share
1225 the same extension.. */
1226 // Skip not source sources, they do not have file fields.
1227 for (; Vf.end() == false; Vf++)
1228 {
1229 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1230 continue;
1231 break;
1232 }
1233
1234 // Does not really matter here.. we are going to fail out below
1235 if (Vf.end() != true)
1236 {
1237 // If this fails to get a file name we will bomb out below.
1238 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1239 if (_error->PendingError() == true)
1240 return;
1241
1242 // Generate the final file name as: package_version_arch.foo
1243 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
1244 QuoteString(Version.VerStr(),"_:") + '_' +
1245 QuoteString(Version.Arch(),"_:.") +
1246 "." + flExtension(Parse.FileName());
1247 }
1248
1249 // check if we have one trusted source for the package. if so, switch
1250 // to "TrustedOnly" mode
1251 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; i++)
1252 {
1253 pkgIndexFile *Index;
1254 if (Sources->FindIndex(i.File(),Index) == false)
1255 continue;
1256 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1257 {
1258 std::cerr << "Checking index: " << Index->Describe()
1259 << "(Trusted=" << Index->IsTrusted() << ")\n";
1260 }
1261 if (Index->IsTrusted()) {
1262 Trusted = true;
1263 break;
1264 }
1265 }
1266
1267 // "allow-unauthenticated" restores apts old fetching behaviour
1268 // that means that e.g. unauthenticated file:// uris are higher
1269 // priority than authenticated http:// uris
1270 if (_config->FindB("APT::Get::AllowUnauthenticated",false) == true)
1271 Trusted = false;
1272
1273 // Select a source
1274 if (QueueNext() == false && _error->PendingError() == false)
1275 _error->Error(_("I wasn't able to locate file for the %s package. "
1276 "This might mean you need to manually fix this package."),
1277 Version.ParentPkg().Name());
1278 }
1279 /*}}}*/
1280 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
1281 // ---------------------------------------------------------------------
1282 /* This queues the next available file version for download. It checks if
1283 the archive is already available in the cache and stashs the MD5 for
1284 checking later. */
1285 bool pkgAcqArchive::QueueNext()
1286 {
1287 for (; Vf.end() == false; Vf++)
1288 {
1289 // Ignore not source sources
1290 if ((Vf.File()->Flags & pkgCache::Flag::NotSource) != 0)
1291 continue;
1292
1293 // Try to cross match against the source list
1294 pkgIndexFile *Index;
1295 if (Sources->FindIndex(Vf.File(),Index) == false)
1296 continue;
1297
1298 // only try to get a trusted package from another source if that source
1299 // is also trusted
1300 if(Trusted && !Index->IsTrusted())
1301 continue;
1302
1303 // Grab the text package record
1304 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
1305 if (_error->PendingError() == true)
1306 return false;
1307
1308 string PkgFile = Parse.FileName();
1309 if(Parse.SHA256Hash() != "")
1310 ExpectedHash = HashString("SHA256", Parse.SHA256Hash());
1311 else if (Parse.SHA1Hash() != "")
1312 ExpectedHash = HashString("SHA1", Parse.SHA1Hash());
1313 else
1314 ExpectedHash = HashString("MD5Sum", Parse.MD5Hash());
1315 if (PkgFile.empty() == true)
1316 return _error->Error(_("The package index files are corrupted. No Filename: "
1317 "field for package %s."),
1318 Version.ParentPkg().Name());
1319
1320 Desc.URI = Index->ArchiveURI(PkgFile);
1321 Desc.Description = Index->ArchiveInfo(Version);
1322 Desc.Owner = this;
1323 Desc.ShortDesc = Version.ParentPkg().Name();
1324
1325 // See if we already have the file. (Legacy filenames)
1326 FileSize = Version->Size;
1327 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
1328 struct stat Buf;
1329 if (stat(FinalFile.c_str(),&Buf) == 0)
1330 {
1331 // Make sure the size matches
1332 if ((unsigned)Buf.st_size == Version->Size)
1333 {
1334 Complete = true;
1335 Local = true;
1336 Status = StatDone;
1337 StoreFilename = DestFile = FinalFile;
1338 return true;
1339 }
1340
1341 /* Hmm, we have a file and its size does not match, this means it is
1342 an old style mismatched arch */
1343 unlink(FinalFile.c_str());
1344 }
1345
1346 // Check it again using the new style output filenames
1347 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
1348 if (stat(FinalFile.c_str(),&Buf) == 0)
1349 {
1350 // Make sure the size matches
1351 if ((unsigned)Buf.st_size == Version->Size)
1352 {
1353 Complete = true;
1354 Local = true;
1355 Status = StatDone;
1356 StoreFilename = DestFile = FinalFile;
1357 return true;
1358 }
1359
1360 /* Hmm, we have a file and its size does not match, this shouldnt
1361 happen.. */
1362 unlink(FinalFile.c_str());
1363 }
1364
1365 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
1366
1367 // Check the destination file
1368 if (stat(DestFile.c_str(),&Buf) == 0)
1369 {
1370 // Hmm, the partial file is too big, erase it
1371 if ((unsigned)Buf.st_size > Version->Size)
1372 unlink(DestFile.c_str());
1373 else
1374 PartialSize = Buf.st_size;
1375 }
1376
1377 // Create the item
1378 Local = false;
1379 Desc.URI = Index->ArchiveURI(PkgFile);
1380 Desc.Description = Index->ArchiveInfo(Version);
1381 Desc.Owner = this;
1382 Desc.ShortDesc = Version.ParentPkg().Name();
1383 QueueURI(Desc);
1384
1385 Vf++;
1386 return true;
1387 }
1388 return false;
1389 }
1390 /*}}}*/
1391 // AcqArchive::Done - Finished fetching /*{{{*/
1392 // ---------------------------------------------------------------------
1393 /* */
1394 void pkgAcqArchive::Done(string Message,unsigned long Size,string CalcHash,
1395 pkgAcquire::MethodConfig *Cfg)
1396 {
1397 Item::Done(Message,Size,CalcHash,Cfg);
1398
1399 // Check the size
1400 if (Size != Version->Size)
1401 {
1402 Status = StatError;
1403 ErrorText = _("Size mismatch");
1404 return;
1405 }
1406
1407 // Check the hash
1408 if(ExpectedHash.toStr() != CalcHash)
1409 {
1410 Status = StatError;
1411 ErrorText = _("Hash Sum mismatch");
1412 if(FileExists(DestFile))
1413 Rename(DestFile,DestFile + ".FAILED");
1414 return;
1415 }
1416
1417 // Grab the output filename
1418 string FileName = LookupTag(Message,"Filename");
1419 if (FileName.empty() == true)
1420 {
1421 Status = StatError;
1422 ErrorText = "Method gave a blank filename";
1423 return;
1424 }
1425
1426 Complete = true;
1427
1428 // Reference filename
1429 if (FileName != DestFile)
1430 {
1431 StoreFilename = DestFile = FileName;
1432 Local = true;
1433 return;
1434 }
1435
1436 // Done, move it into position
1437 string FinalFile = _config->FindDir("Dir::Cache::Archives");
1438 FinalFile += flNotDir(StoreFilename);
1439 Rename(DestFile,FinalFile);
1440
1441 StoreFilename = DestFile = FinalFile;
1442 Complete = true;
1443 }
1444 /*}}}*/
1445 // AcqArchive::Failed - Failure handler /*{{{*/
1446 // ---------------------------------------------------------------------
1447 /* Here we try other sources */
1448 void pkgAcqArchive::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1449 {
1450 ErrorText = LookupTag(Message,"Message");
1451
1452 /* We don't really want to retry on failed media swaps, this prevents
1453 that. An interesting observation is that permanent failures are not
1454 recorded. */
1455 if (Cnf->Removable == true &&
1456 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1457 {
1458 // Vf = Version.FileList();
1459 while (Vf.end() == false) Vf++;
1460 StoreFilename = string();
1461 Item::Failed(Message,Cnf);
1462 return;
1463 }
1464
1465 if (QueueNext() == false)
1466 {
1467 // This is the retry counter
1468 if (Retries != 0 &&
1469 Cnf->LocalOnly == false &&
1470 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1471 {
1472 Retries--;
1473 Vf = Version.FileList();
1474 if (QueueNext() == true)
1475 return;
1476 }
1477
1478 StoreFilename = string();
1479 Item::Failed(Message,Cnf);
1480 }
1481 }
1482 /*}}}*/
1483 // AcqArchive::IsTrusted - Determine whether this archive comes from a trusted source /*{{{*/
1484 // ---------------------------------------------------------------------
1485 bool pkgAcqArchive::IsTrusted()
1486 {
1487 return Trusted;
1488 }
1489 /*}}}*/
1490 // AcqArchive::Finished - Fetching has finished, tidy up /*{{{*/
1491 // ---------------------------------------------------------------------
1492 /* */
1493 void pkgAcqArchive::Finished()
1494 {
1495 if (Status == pkgAcquire::Item::StatDone &&
1496 Complete == true)
1497 return;
1498 StoreFilename = string();
1499 }
1500 /*}}}*/
1501 // AcqFile::pkgAcqFile - Constructor /*{{{*/
1502 // ---------------------------------------------------------------------
1503 /* The file is added to the queue */
1504 pkgAcqFile::pkgAcqFile(pkgAcquire *Owner,string URI,string Hash,
1505 unsigned long Size,string Dsc,string ShortDesc,
1506 const string &DestDir, const string &DestFilename) :
1507 Item(Owner), ExpectedHash(Hash)
1508 {
1509 Retries = _config->FindI("Acquire::Retries",0);
1510
1511 if(!DestFilename.empty())
1512 DestFile = DestFilename;
1513 else if(!DestDir.empty())
1514 DestFile = DestDir + "/" + flNotDir(URI);
1515 else
1516 DestFile = flNotDir(URI);
1517
1518 // Create the item
1519 Desc.URI = URI;
1520 Desc.Description = Dsc;
1521 Desc.Owner = this;
1522
1523 // Set the short description to the archive component
1524 Desc.ShortDesc = ShortDesc;
1525
1526 // Get the transfer sizes
1527 FileSize = Size;
1528 struct stat Buf;
1529 if (stat(DestFile.c_str(),&Buf) == 0)
1530 {
1531 // Hmm, the partial file is too big, erase it
1532 if ((unsigned)Buf.st_size > Size)
1533 unlink(DestFile.c_str());
1534 else
1535 PartialSize = Buf.st_size;
1536 }
1537
1538 QueueURI(Desc);
1539 }
1540 /*}}}*/
1541 // AcqFile::Done - Item downloaded OK /*{{{*/
1542 // ---------------------------------------------------------------------
1543 /* */
1544 void pkgAcqFile::Done(string Message,unsigned long Size,string CalcHash,
1545 pkgAcquire::MethodConfig *Cnf)
1546 {
1547 Item::Done(Message,Size,CalcHash,Cnf);
1548
1549 // Check the hash
1550 if(!ExpectedHash.empty() && ExpectedHash.toStr() != CalcHash)
1551 {
1552 Status = StatError;
1553 ErrorText = "Hash Sum mismatch";
1554 Rename(DestFile,DestFile + ".FAILED");
1555 return;
1556 }
1557
1558 string FileName = LookupTag(Message,"Filename");
1559 if (FileName.empty() == true)
1560 {
1561 Status = StatError;
1562 ErrorText = "Method gave a blank filename";
1563 return;
1564 }
1565
1566 Complete = true;
1567
1568 // The files timestamp matches
1569 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
1570 return;
1571
1572 // We have to copy it into place
1573 if (FileName != DestFile)
1574 {
1575 Local = true;
1576 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
1577 Cnf->Removable == true)
1578 {
1579 Desc.URI = "copy:" + FileName;
1580 QueueURI(Desc);
1581 return;
1582 }
1583
1584 // Erase the file if it is a symlink so we can overwrite it
1585 struct stat St;
1586 if (lstat(DestFile.c_str(),&St) == 0)
1587 {
1588 if (S_ISLNK(St.st_mode) != 0)
1589 unlink(DestFile.c_str());
1590 }
1591
1592 // Symlink the file
1593 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
1594 {
1595 ErrorText = "Link to " + DestFile + " failure ";
1596 Status = StatError;
1597 Complete = false;
1598 }
1599 }
1600 }
1601 /*}}}*/
1602 // AcqFile::Failed - Failure handler /*{{{*/
1603 // ---------------------------------------------------------------------
1604 /* Here we try other sources */
1605 void pkgAcqFile::Failed(string Message,pkgAcquire::MethodConfig *Cnf)
1606 {
1607 ErrorText = LookupTag(Message,"Message");
1608
1609 // This is the retry counter
1610 if (Retries != 0 &&
1611 Cnf->LocalOnly == false &&
1612 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
1613 {
1614 Retries--;
1615 QueueURI(Desc);
1616 return;
1617 }
1618
1619 Item::Failed(Message,Cnf);
1620 }
1621 /*}}}*/