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