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