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