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