]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-item.cc
b29de665b6273f22ae4581ed634434f3617bdba5
[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/tagfile.h>
26 #include <apt-pkg/metaindex.h>
27 #include <apt-pkg/acquire.h>
28 #include <apt-pkg/hashes.h>
29 #include <apt-pkg/indexfile.h>
30 #include <apt-pkg/pkgcache.h>
31 #include <apt-pkg/cacheiterators.h>
32 #include <apt-pkg/pkgrecords.h>
33 #include <apt-pkg/gpgv.h>
34
35 #include <algorithm>
36 #include <stddef.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <iostream>
40 #include <vector>
41 #include <sys/stat.h>
42 #include <unistd.h>
43 #include <errno.h>
44 #include <string>
45 #include <stdio.h>
46 #include <ctime>
47 #include <sstream>
48
49 #include <apti18n.h>
50 /*}}}*/
51
52 using namespace std;
53
54 static void printHashSumComparision(std::string const &URI, HashStringList const &Expected, HashStringList const &Actual) /*{{{*/
55 {
56 if (_config->FindB("Debug::Acquire::HashSumMismatch", false) == false)
57 return;
58 std::cerr << std::endl << URI << ":" << std::endl << " Expected Hash: " << std::endl;
59 for (HashStringList::const_iterator hs = Expected.begin(); hs != Expected.end(); ++hs)
60 std::cerr << "\t- " << hs->toStr() << std::endl;
61 std::cerr << " Actual Hash: " << std::endl;
62 for (HashStringList::const_iterator hs = Actual.begin(); hs != Actual.end(); ++hs)
63 std::cerr << "\t- " << hs->toStr() << std::endl;
64 }
65 /*}}}*/
66 static std::string GetPartialFileName(std::string const &file) /*{{{*/
67 {
68 std::string DestFile = _config->FindDir("Dir::State::lists") + "partial/";
69 DestFile += file;
70 return DestFile;
71 }
72 /*}}}*/
73 static std::string GetPartialFileNameFromURI(std::string const &uri) /*{{{*/
74 {
75 return GetPartialFileName(URItoFileName(uri));
76 }
77 /*}}}*/
78 static std::string GetFinalFileNameFromURI(std::string const &uri) /*{{{*/
79 {
80 return _config->FindDir("Dir::State::lists") + URItoFileName(uri);
81 }
82 /*}}}*/
83 static std::string GetKeepCompressedFileName(std::string file, IndexTarget const &Target)/*{{{*/
84 {
85 if (Target.KeepCompressed == false)
86 return file;
87
88 std::string const CompressionTypes = Target.Option(IndexTarget::COMPRESSIONTYPES);
89 if (CompressionTypes.empty() == false)
90 {
91 std::string const ext = CompressionTypes.substr(0, CompressionTypes.find(' '));
92 if (ext != "uncompressed")
93 file.append(".").append(ext);
94 }
95 return file;
96 }
97 /*}}}*/
98 static std::string GetCompressedFileName(IndexTarget const &Target, std::string const &Name, std::string const &Ext) /*{{{*/
99 {
100 if (Ext.empty() || Ext == "uncompressed")
101 return Name;
102
103 // do not reverify cdrom sources as apt-cdrom may rewrite the Packages
104 // file when its doing the indexcopy
105 if (Target.URI.substr(0,6) == "cdrom:")
106 return Name;
107
108 // adjust DestFile if its compressed on disk
109 if (Target.KeepCompressed == true)
110 return Name + '.' + Ext;
111 return Name;
112 }
113 /*}}}*/
114 static std::string GetMergeDiffsPatchFileName(std::string const &Final, std::string const &Patch)/*{{{*/
115 {
116 // rred expects the patch as $FinalFile.ed.$patchname.gz
117 return Final + ".ed." + Patch + ".gz";
118 }
119 /*}}}*/
120 static std::string GetDiffsPatchFileName(std::string const &Final) /*{{{*/
121 {
122 // rred expects the patch as $FinalFile.ed
123 return Final + ".ed";
124 }
125 /*}}}*/
126 static bool BootstrapPDiffWith(std::string const &PartialFile, std::string const &FinalFile, IndexTarget const &Target)/*{{{*/
127 {
128 // patching needs to be bootstrapped with the 'old' version
129 std::vector<std::string> types = VectorizeString(Target.Option(IndexTarget::COMPRESSIONTYPES), ' ');
130 auto typeItr = types.cbegin();
131 for (; typeItr != types.cend(); ++typeItr)
132 {
133 std::string Final = FinalFile;
134 if (*typeItr != "uncompressed")
135 Final.append(".").append(*typeItr);
136 if (RealFileExists(Final) == false)
137 continue;
138 std::string Partial = PartialFile;
139 if (*typeItr != "uncompressed")
140 Partial.append(".").append(*typeItr);
141 if (FileExists(Partial.c_str()) == true)
142 return true;
143 if (symlink(Final.c_str(), Partial.c_str()) != 0)
144 return false;
145 break;
146 }
147 return typeItr != types.cend();
148 }
149 /*}}}*/
150 static bool RemoveFile(char const * const Function, std::string const &FileName)/*{{{*/
151 {
152 if (FileName == "/dev/null")
153 return true;
154 errno = 0;
155 if (unlink(FileName.c_str()) != 0)
156 {
157 if (errno == ENOENT)
158 return true;
159 return _error->WarningE(Function, "Removal of file %s failed", FileName.c_str());
160 }
161 return true;
162 }
163 /*}}}*/
164
165 static bool MessageInsecureRepository(bool const isError, std::string const &msg)/*{{{*/
166 {
167 if (isError)
168 {
169 _error->Error("%s", msg.c_str());
170 _error->Notice("%s", _("Updating such a repository securily is impossible and therefore disabled by default."));
171 }
172 else
173 {
174 _error->Warning("%s", msg.c_str());
175 _error->Notice("%s", _("Data from such a repository can not be authenticated and is therefore potentially dangerous to use."));
176 }
177 _error->Notice("%s", _("See apt-secure(8) manpage for repository creation and user configuration details."));
178 return false;
179 }
180 static bool MessageInsecureRepository(bool const isError, char const * const msg, std::string const &repo)
181 {
182 std::string m;
183 strprintf(m, msg, repo.c_str());
184 return MessageInsecureRepository(isError, m);
185 }
186 /*}}}*/
187 static bool AllowInsecureRepositories(char const * const msg, std::string const &repo,/*{{{*/
188 metaIndex const * const MetaIndexParser, pkgAcqMetaClearSig * const TransactionManager, pkgAcquire::Item * const I)
189 {
190 if(MetaIndexParser->GetTrusted() == metaIndex::TRI_YES)
191 return true;
192
193 if (_config->FindB("Acquire::AllowInsecureRepositories") == true)
194 {
195 MessageInsecureRepository(false, msg, repo);
196 return true;
197 }
198
199 MessageInsecureRepository(true, msg, repo);
200 TransactionManager->AbortTransaction();
201 I->Status = pkgAcquire::Item::StatError;
202 return false;
203 }
204 /*}}}*/
205 static HashStringList GetExpectedHashesFromFor(metaIndex * const Parser, std::string const &MetaKey)/*{{{*/
206 {
207 if (Parser == NULL)
208 return HashStringList();
209 metaIndex::checkSum * const R = Parser->Lookup(MetaKey);
210 if (R == NULL)
211 return HashStringList();
212 return R->Hashes;
213 }
214 /*}}}*/
215
216 // all ::HashesRequired and ::GetExpectedHashes implementations /*{{{*/
217 /* ::GetExpectedHashes is abstract and has to be implemented by all subclasses.
218 It is best to implement it as broadly as possible, while ::HashesRequired defaults
219 to true and should be as restrictive as possible for false cases. Note that if
220 a hash is returned by ::GetExpectedHashes it must match. Only if it doesn't
221 ::HashesRequired is called to evaluate if its okay to have no hashes. */
222 APT_CONST bool pkgAcqTransactionItem::HashesRequired() const
223 {
224 /* signed repositories obviously have a parser and good hashes.
225 unsigned repositories, too, as even if we can't trust them for security,
226 we can at least trust them for integrity of the download itself.
227 Only repositories without a Release file can (obviously) not have
228 hashes – and they are very uncommon and strongly discouraged */
229 return TransactionManager->MetaIndexParser != NULL &&
230 TransactionManager->MetaIndexParser->GetLoadedSuccessfully() != metaIndex::TRI_UNSET;
231 }
232 HashStringList pkgAcqTransactionItem::GetExpectedHashes() const
233 {
234 return GetExpectedHashesFor(GetMetaKey());
235 }
236
237 APT_CONST bool pkgAcqMetaBase::HashesRequired() const
238 {
239 // Release and co have no hashes 'by design'.
240 return false;
241 }
242 HashStringList pkgAcqMetaBase::GetExpectedHashes() const
243 {
244 return HashStringList();
245 }
246
247 APT_CONST bool pkgAcqIndexDiffs::HashesRequired() const
248 {
249 /* We don't always have the diff of the downloaded pdiff file.
250 What we have for sure is hashes for the uncompressed file,
251 but rred uncompresses them on the fly while parsing, so not handled here.
252 Hashes are (also) checked while searching for (next) patch to apply. */
253 if (State == StateFetchDiff)
254 return available_patches[0].download_hashes.empty() == false;
255 return false;
256 }
257 HashStringList pkgAcqIndexDiffs::GetExpectedHashes() const
258 {
259 if (State == StateFetchDiff)
260 return available_patches[0].download_hashes;
261 return HashStringList();
262 }
263
264 APT_CONST bool pkgAcqIndexMergeDiffs::HashesRequired() const
265 {
266 /* @see #pkgAcqIndexDiffs::HashesRequired, with the difference that
267 we can check the rred result after all patches are applied as
268 we know the expected result rather than potentially apply more patches */
269 if (State == StateFetchDiff)
270 return patch.download_hashes.empty() == false;
271 return State == StateApplyDiff;
272 }
273 HashStringList pkgAcqIndexMergeDiffs::GetExpectedHashes() const
274 {
275 if (State == StateFetchDiff)
276 return patch.download_hashes;
277 else if (State == StateApplyDiff)
278 return GetExpectedHashesFor(Target.MetaKey);
279 return HashStringList();
280 }
281
282 APT_CONST bool pkgAcqArchive::HashesRequired() const
283 {
284 return LocalSource == false;
285 }
286 HashStringList pkgAcqArchive::GetExpectedHashes() const
287 {
288 // figured out while parsing the records
289 return ExpectedHashes;
290 }
291
292 APT_CONST bool pkgAcqFile::HashesRequired() const
293 {
294 // supplied as parameter at creation time, so the caller decides
295 return ExpectedHashes.usable();
296 }
297 HashStringList pkgAcqFile::GetExpectedHashes() const
298 {
299 return ExpectedHashes;
300 }
301 /*}}}*/
302 // Acquire::Item::QueueURI and specialisations from child classes /*{{{*/
303 bool pkgAcquire::Item::QueueURI(pkgAcquire::ItemDesc &Item)
304 {
305 Owner->Enqueue(Item);
306 return true;
307 }
308 /* The idea here is that an item isn't queued if it exists on disk and the
309 transition manager was a hit as this means that the files it contains
310 the checksums for can't be updated either (or they are and we are asking
311 for a hashsum mismatch to happen which helps nobody) */
312 bool pkgAcqTransactionItem::QueueURI(pkgAcquire::ItemDesc &Item)
313 {
314 std::string const FinalFile = GetFinalFilename();
315 if (TransactionManager != NULL && TransactionManager->IMSHit == true &&
316 FileExists(FinalFile) == true)
317 {
318 PartialFile = DestFile = FinalFile;
319 Status = StatDone;
320 return false;
321 }
322 return pkgAcquire::Item::QueueURI(Item);
323 }
324 /* The transition manager InRelease itself (or its older sisters-in-law
325 Release & Release.gpg) is always queued as this allows us to rerun gpgv
326 on it to verify that we aren't stalled with old files */
327 bool pkgAcqMetaBase::QueueURI(pkgAcquire::ItemDesc &Item)
328 {
329 return pkgAcquire::Item::QueueURI(Item);
330 }
331 /* the Diff/Index needs to queue also the up-to-date complete index file
332 to ensure that the list cleaner isn't eating it */
333 bool pkgAcqDiffIndex::QueueURI(pkgAcquire::ItemDesc &Item)
334 {
335 if (pkgAcqTransactionItem::QueueURI(Item) == true)
336 return true;
337 QueueOnIMSHit();
338 return false;
339 }
340 /*}}}*/
341 // Acquire::Item::GetFinalFilename and specialisations for child classes /*{{{*/
342 std::string pkgAcquire::Item::GetFinalFilename() const
343 {
344 return GetFinalFileNameFromURI(Desc.URI);
345 }
346 std::string pkgAcqDiffIndex::GetFinalFilename() const
347 {
348 // the logic we inherent from pkgAcqBaseIndex isn't what we need here
349 return pkgAcquire::Item::GetFinalFilename();
350 }
351 std::string pkgAcqIndex::GetFinalFilename() const
352 {
353 std::string const FinalFile = GetFinalFileNameFromURI(Target.URI);
354 return GetCompressedFileName(Target, FinalFile, CurrentCompressionExtension);
355 }
356 std::string pkgAcqMetaSig::GetFinalFilename() const
357 {
358 return GetFinalFileNameFromURI(Target.URI);
359 }
360 std::string pkgAcqBaseIndex::GetFinalFilename() const
361 {
362 return GetFinalFileNameFromURI(Target.URI);
363 }
364 std::string pkgAcqMetaBase::GetFinalFilename() const
365 {
366 return GetFinalFileNameFromURI(Target.URI);
367 }
368 std::string pkgAcqArchive::GetFinalFilename() const
369 {
370 return _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
371 }
372 /*}}}*/
373 // pkgAcqTransactionItem::GetMetaKey and specialisations for child classes /*{{{*/
374 std::string pkgAcqTransactionItem::GetMetaKey() const
375 {
376 return Target.MetaKey;
377 }
378 std::string pkgAcqIndex::GetMetaKey() const
379 {
380 if (Stage == STAGE_DECOMPRESS_AND_VERIFY || CurrentCompressionExtension == "uncompressed")
381 return Target.MetaKey;
382 return Target.MetaKey + "." + CurrentCompressionExtension;
383 }
384 std::string pkgAcqDiffIndex::GetMetaKey() const
385 {
386 return Target.MetaKey + ".diff/Index";
387 }
388 /*}}}*/
389 //pkgAcqTransactionItem::TransactionState and specialisations for child classes /*{{{*/
390 bool pkgAcqTransactionItem::TransactionState(TransactionStates const state)
391 {
392 bool const Debug = _config->FindB("Debug::Acquire::Transaction", false);
393 switch(state)
394 {
395 case TransactionAbort:
396 if(Debug == true)
397 std::clog << " Cancel: " << DestFile << std::endl;
398 if (Status == pkgAcquire::Item::StatIdle)
399 {
400 Status = pkgAcquire::Item::StatDone;
401 Dequeue();
402 }
403 break;
404 case TransactionCommit:
405 if(PartialFile != "")
406 {
407 if(Debug == true)
408 std::clog << "mv " << PartialFile << " -> "<< DestFile << " # " << DescURI() << std::endl;
409
410 Rename(PartialFile, DestFile);
411 } else {
412 if(Debug == true)
413 std::clog << "rm " << DestFile << " # " << DescURI() << std::endl;
414 RemoveFile("TransactionCommit", DestFile);
415 }
416 break;
417 }
418 return true;
419 }
420 bool pkgAcqMetaBase::TransactionState(TransactionStates const state)
421 {
422 // Do not remove InRelease on IMSHit of Release.gpg [yes, this is very edgecasey]
423 if (TransactionManager->IMSHit == false)
424 return pkgAcqTransactionItem::TransactionState(state);
425 return true;
426 }
427 bool pkgAcqIndex::TransactionState(TransactionStates const state)
428 {
429 if (pkgAcqTransactionItem::TransactionState(state) == false)
430 return false;
431
432 switch (state)
433 {
434 case TransactionAbort:
435 if (Stage == STAGE_DECOMPRESS_AND_VERIFY)
436 {
437 // keep the compressed file, but drop the decompressed
438 EraseFileName.clear();
439 if (PartialFile.empty() == false && flExtension(PartialFile) == "decomp")
440 RemoveFile("TransactionAbort", PartialFile);
441 }
442 break;
443 case TransactionCommit:
444 if (EraseFileName.empty() == false)
445 RemoveFile("TransactionCommit", EraseFileName);
446 break;
447 }
448 return true;
449 }
450 bool pkgAcqDiffIndex::TransactionState(TransactionStates const state)
451 {
452 if (pkgAcqTransactionItem::TransactionState(state) == false)
453 return false;
454
455 switch (state)
456 {
457 case TransactionCommit:
458 break;
459 case TransactionAbort:
460 std::string const Partial = GetPartialFileNameFromURI(Target.URI);
461 RemoveFile("TransactionAbort", Partial);
462 break;
463 }
464
465 return true;
466 }
467 /*}}}*/
468
469 class APT_HIDDEN NoActionItem : public pkgAcquire::Item /*{{{*/
470 /* The sole purpose of this class is having an item which does nothing to
471 reach its done state to prevent cleanup deleting the mentioned file.
472 Handy in cases in which we know we have the file already, like IMS-Hits. */
473 {
474 IndexTarget const Target;
475 public:
476 virtual std::string DescURI() const APT_OVERRIDE {return Target.URI;};
477 virtual HashStringList GetExpectedHashes() const APT_OVERRIDE {return HashStringList();};
478
479 NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target) :
480 pkgAcquire::Item(Owner), Target(Target)
481 {
482 Status = StatDone;
483 DestFile = GetFinalFileNameFromURI(Target.URI);
484 }
485 NoActionItem(pkgAcquire * const Owner, IndexTarget const &Target, std::string const &FinalFile) :
486 pkgAcquire::Item(Owner), Target(Target)
487 {
488 Status = StatDone;
489 DestFile = FinalFile;
490 }
491 };
492 /*}}}*/
493
494 // Acquire::Item::Item - Constructor /*{{{*/
495 APT_IGNORE_DEPRECATED_PUSH
496 pkgAcquire::Item::Item(pkgAcquire * const owner) :
497 FileSize(0), PartialSize(0), Mode(0), ID(0), Complete(false), Local(false),
498 QueueCounter(0), ExpectedAdditionalItems(0), Owner(owner), d(NULL)
499 {
500 Owner->Add(this);
501 Status = StatIdle;
502 }
503 APT_IGNORE_DEPRECATED_POP
504 /*}}}*/
505 // Acquire::Item::~Item - Destructor /*{{{*/
506 pkgAcquire::Item::~Item()
507 {
508 Owner->Remove(this);
509 }
510 /*}}}*/
511 std::string pkgAcquire::Item::Custom600Headers() const /*{{{*/
512 {
513 return std::string();
514 }
515 /*}}}*/
516 std::string pkgAcquire::Item::ShortDesc() const /*{{{*/
517 {
518 return DescURI();
519 }
520 /*}}}*/
521 APT_CONST void pkgAcquire::Item::Finished() /*{{{*/
522 {
523 }
524 /*}}}*/
525 APT_PURE pkgAcquire * pkgAcquire::Item::GetOwner() const /*{{{*/
526 {
527 return Owner;
528 }
529 /*}}}*/
530 APT_CONST pkgAcquire::ItemDesc &pkgAcquire::Item::GetItemDesc() /*{{{*/
531 {
532 return Desc;
533 }
534 /*}}}*/
535 APT_CONST bool pkgAcquire::Item::IsTrusted() const /*{{{*/
536 {
537 return false;
538 }
539 /*}}}*/
540 // Acquire::Item::Failed - Item failed to download /*{{{*/
541 // ---------------------------------------------------------------------
542 /* We return to an idle state if there are still other queues that could
543 fetch this object */
544 void pkgAcquire::Item::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
545 {
546 if(ErrorText.empty())
547 ErrorText = LookupTag(Message,"Message");
548 if (QueueCounter <= 1)
549 {
550 /* This indicates that the file is not available right now but might
551 be sometime later. If we do a retry cycle then this should be
552 retried [CDROMs] */
553 if (Cnf != NULL && Cnf->LocalOnly == true &&
554 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
555 {
556 Status = StatIdle;
557 Dequeue();
558 return;
559 }
560
561 switch (Status)
562 {
563 case StatIdle:
564 case StatFetching:
565 case StatDone:
566 Status = StatError;
567 break;
568 case StatAuthError:
569 case StatError:
570 case StatTransientNetworkError:
571 break;
572 }
573 Complete = false;
574 Dequeue();
575 }
576
577 string const FailReason = LookupTag(Message, "FailReason");
578 if (FailReason == "MaximumSizeExceeded")
579 RenameOnError(MaximumSizeExceeded);
580 else if (Status == StatAuthError)
581 RenameOnError(HashSumMismatch);
582
583 // report mirror failure back to LP if we actually use a mirror
584 if (FailReason.empty() == false)
585 ReportMirrorFailure(FailReason);
586 else
587 ReportMirrorFailure(ErrorText);
588
589 if (QueueCounter > 1)
590 Status = StatIdle;
591 }
592 /*}}}*/
593 // Acquire::Item::Start - Item has begun to download /*{{{*/
594 // ---------------------------------------------------------------------
595 /* Stash status and the file size. Note that setting Complete means
596 sub-phases of the acquire process such as decompresion are operating */
597 void pkgAcquire::Item::Start(string const &/*Message*/, unsigned long long const Size)
598 {
599 Status = StatFetching;
600 ErrorText.clear();
601 if (FileSize == 0 && Complete == false)
602 FileSize = Size;
603 }
604 /*}}}*/
605 // Acquire::Item::VerifyDone - check if Item was downloaded OK /*{{{*/
606 /* Note that hash-verification is 'hardcoded' in acquire-worker and has
607 * already passed if this method is called. */
608 bool pkgAcquire::Item::VerifyDone(std::string const &Message,
609 pkgAcquire::MethodConfig const * const /*Cnf*/)
610 {
611 std::string const FileName = LookupTag(Message,"Filename");
612 if (FileName.empty() == true)
613 {
614 Status = StatError;
615 ErrorText = "Method gave a blank filename";
616 return false;
617 }
618
619 return true;
620 }
621 /*}}}*/
622 // Acquire::Item::Done - Item downloaded OK /*{{{*/
623 void pkgAcquire::Item::Done(string const &/*Message*/, HashStringList const &Hashes,
624 pkgAcquire::MethodConfig const * const /*Cnf*/)
625 {
626 // We just downloaded something..
627 if (FileSize == 0)
628 {
629 unsigned long long const downloadedSize = Hashes.FileSize();
630 if (downloadedSize != 0)
631 {
632 FileSize = downloadedSize;
633 }
634 }
635 Status = StatDone;
636 ErrorText = string();
637 Owner->Dequeue(this);
638 }
639 /*}}}*/
640 // Acquire::Item::Rename - Rename a file /*{{{*/
641 // ---------------------------------------------------------------------
642 /* This helper function is used by a lot of item methods as their final
643 step */
644 bool pkgAcquire::Item::Rename(string const &From,string const &To)
645 {
646 if (From == To || rename(From.c_str(),To.c_str()) == 0)
647 return true;
648
649 std::string S;
650 strprintf(S, _("rename failed, %s (%s -> %s)."), strerror(errno),
651 From.c_str(),To.c_str());
652 Status = StatError;
653 if (ErrorText.empty())
654 ErrorText = S;
655 else
656 ErrorText = ErrorText + ": " + S;
657 return false;
658 }
659 /*}}}*/
660 void pkgAcquire::Item::Dequeue() /*{{{*/
661 {
662 Owner->Dequeue(this);
663 }
664 /*}}}*/
665 bool pkgAcquire::Item::RenameOnError(pkgAcquire::Item::RenameOnErrorState const error)/*{{{*/
666 {
667 if (RealFileExists(DestFile))
668 Rename(DestFile, DestFile + ".FAILED");
669
670 std::string errtext;
671 switch (error)
672 {
673 case HashSumMismatch:
674 errtext = _("Hash Sum mismatch");
675 Status = StatAuthError;
676 ReportMirrorFailure("HashChecksumFailure");
677 break;
678 case SizeMismatch:
679 errtext = _("Size mismatch");
680 Status = StatAuthError;
681 ReportMirrorFailure("SizeFailure");
682 break;
683 case InvalidFormat:
684 errtext = _("Invalid file format");
685 Status = StatError;
686 // do not report as usually its not the mirrors fault, but Portal/Proxy
687 break;
688 case SignatureError:
689 errtext = _("Signature error");
690 Status = StatError;
691 break;
692 case NotClearsigned:
693 strprintf(errtext, _("Clearsigned file isn't valid, got '%s' (does the network require authentication?)"), "NOSPLIT");
694 Status = StatAuthError;
695 break;
696 case MaximumSizeExceeded:
697 // the method is expected to report a good error for this
698 Status = StatError;
699 break;
700 case PDiffError:
701 // no handling here, done by callers
702 break;
703 }
704 if (ErrorText.empty())
705 ErrorText = errtext;
706 return false;
707 }
708 /*}}}*/
709 void pkgAcquire::Item::SetActiveSubprocess(const std::string &subprocess)/*{{{*/
710 {
711 ActiveSubprocess = subprocess;
712 APT_IGNORE_DEPRECATED(Mode = ActiveSubprocess.c_str();)
713 }
714 /*}}}*/
715 // Acquire::Item::ReportMirrorFailure /*{{{*/
716 void pkgAcquire::Item::ReportMirrorFailure(string const &FailCode)
717 {
718 // we only act if a mirror was used at all
719 if(UsedMirror.empty())
720 return;
721 #if 0
722 std::cerr << "\nReportMirrorFailure: "
723 << UsedMirror
724 << " Uri: " << DescURI()
725 << " FailCode: "
726 << FailCode << std::endl;
727 #endif
728 string report = _config->Find("Methods::Mirror::ProblemReporting",
729 "/usr/lib/apt/apt-report-mirror-failure");
730 if(!FileExists(report))
731 return;
732
733 std::vector<char const*> Args;
734 Args.push_back(report.c_str());
735 Args.push_back(UsedMirror.c_str());
736 Args.push_back(DescURI().c_str());
737 Args.push_back(FailCode.c_str());
738 Args.push_back(NULL);
739
740 pid_t pid = ExecFork();
741 if(pid < 0)
742 {
743 _error->Error("ReportMirrorFailure Fork failed");
744 return;
745 }
746 else if(pid == 0)
747 {
748 execvp(Args[0], (char**)Args.data());
749 std::cerr << "Could not exec " << Args[0] << std::endl;
750 _exit(100);
751 }
752 if(!ExecWait(pid, "report-mirror-failure"))
753 {
754 _error->Warning("Couldn't report problem to '%s'",
755 _config->Find("Methods::Mirror::ProblemReporting").c_str());
756 }
757 }
758 /*}}}*/
759 std::string pkgAcquire::Item::HashSum() const /*{{{*/
760 {
761 HashStringList const hashes = GetExpectedHashes();
762 HashString const * const hs = hashes.find(NULL);
763 return hs != NULL ? hs->toStr() : "";
764 }
765 /*}}}*/
766
767 pkgAcqTransactionItem::pkgAcqTransactionItem(pkgAcquire * const Owner, /*{{{*/
768 pkgAcqMetaClearSig * const transactionManager, IndexTarget const &target) :
769 pkgAcquire::Item(Owner), d(NULL), Target(target), TransactionManager(transactionManager)
770 {
771 if (TransactionManager != this)
772 TransactionManager->Add(this);
773 }
774 /*}}}*/
775 pkgAcqTransactionItem::~pkgAcqTransactionItem() /*{{{*/
776 {
777 }
778 /*}}}*/
779 HashStringList pkgAcqTransactionItem::GetExpectedHashesFor(std::string const &MetaKey) const /*{{{*/
780 {
781 return GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, MetaKey);
782 }
783 /*}}}*/
784
785 // AcqMetaBase - Constructor /*{{{*/
786 pkgAcqMetaBase::pkgAcqMetaBase(pkgAcquire * const Owner,
787 pkgAcqMetaClearSig * const TransactionManager,
788 std::vector<IndexTarget> const &IndexTargets,
789 IndexTarget const &DataTarget)
790 : pkgAcqTransactionItem(Owner, TransactionManager, DataTarget), d(NULL),
791 IndexTargets(IndexTargets),
792 AuthPass(false), IMSHit(false)
793 {
794 }
795 /*}}}*/
796 // AcqMetaBase::Add - Add a item to the current Transaction /*{{{*/
797 void pkgAcqMetaBase::Add(pkgAcqTransactionItem * const I)
798 {
799 Transaction.push_back(I);
800 }
801 /*}}}*/
802 // AcqMetaBase::AbortTransaction - Abort the current Transaction /*{{{*/
803 void pkgAcqMetaBase::AbortTransaction()
804 {
805 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
806 std::clog << "AbortTransaction: " << TransactionManager << std::endl;
807
808 // ensure the toplevel is in error state too
809 for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
810 I != Transaction.end(); ++I)
811 {
812 (*I)->TransactionState(TransactionAbort);
813 }
814 Transaction.clear();
815 }
816 /*}}}*/
817 // AcqMetaBase::TransactionHasError - Check for errors in Transaction /*{{{*/
818 APT_PURE bool pkgAcqMetaBase::TransactionHasError() const
819 {
820 for (std::vector<pkgAcqTransactionItem*>::const_iterator I = Transaction.begin();
821 I != Transaction.end(); ++I)
822 {
823 switch((*I)->Status) {
824 case StatDone: break;
825 case StatIdle: break;
826 case StatAuthError: return true;
827 case StatError: return true;
828 case StatTransientNetworkError: return true;
829 case StatFetching: break;
830 }
831 }
832 return false;
833 }
834 /*}}}*/
835 // AcqMetaBase::CommitTransaction - Commit a transaction /*{{{*/
836 void pkgAcqMetaBase::CommitTransaction()
837 {
838 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
839 std::clog << "CommitTransaction: " << this << std::endl;
840
841 // move new files into place *and* remove files that are not
842 // part of the transaction but are still on disk
843 for (std::vector<pkgAcqTransactionItem*>::iterator I = Transaction.begin();
844 I != Transaction.end(); ++I)
845 {
846 (*I)->TransactionState(TransactionCommit);
847 }
848 Transaction.clear();
849 }
850 /*}}}*/
851 // AcqMetaBase::TransactionStageCopy - Stage a file for copying /*{{{*/
852 void pkgAcqMetaBase::TransactionStageCopy(pkgAcqTransactionItem * const I,
853 const std::string &From,
854 const std::string &To)
855 {
856 I->PartialFile = From;
857 I->DestFile = To;
858 }
859 /*}}}*/
860 // AcqMetaBase::TransactionStageRemoval - Stage a file for removal /*{{{*/
861 void pkgAcqMetaBase::TransactionStageRemoval(pkgAcqTransactionItem * const I,
862 const std::string &FinalFile)
863 {
864 I->PartialFile = "";
865 I->DestFile = FinalFile;
866 }
867 /*}}}*/
868 // AcqMetaBase::GenerateAuthWarning - Check gpg authentication error /*{{{*/
869 bool pkgAcqMetaBase::CheckStopAuthentication(pkgAcquire::Item * const I, const std::string &Message)
870 {
871 // FIXME: this entire function can do now that we disallow going to
872 // a unauthenticated state and can cleanly rollback
873
874 string const Final = I->GetFinalFilename();
875 if(FileExists(Final))
876 {
877 I->Status = StatTransientNetworkError;
878 _error->Warning(_("An error occurred during the signature "
879 "verification. The repository is not updated "
880 "and the previous index files will be used. "
881 "GPG error: %s: %s"),
882 Desc.Description.c_str(),
883 LookupTag(Message,"Message").c_str());
884 RunScripts("APT::Update::Auth-Failure");
885 return true;
886 } else if (LookupTag(Message,"Message").find("NODATA") != string::npos) {
887 /* Invalid signature file, reject (LP: #346386) (Closes: #627642) */
888 _error->Error(_("GPG error: %s: %s"),
889 Desc.Description.c_str(),
890 LookupTag(Message,"Message").c_str());
891 I->Status = StatAuthError;
892 return true;
893 } else {
894 _error->Warning(_("GPG error: %s: %s"),
895 Desc.Description.c_str(),
896 LookupTag(Message,"Message").c_str());
897 }
898 // gpgv method failed
899 ReportMirrorFailure("GPGFailure");
900 return false;
901 }
902 /*}}}*/
903 // AcqMetaBase::Custom600Headers - Get header for AcqMetaBase /*{{{*/
904 // ---------------------------------------------------------------------
905 string pkgAcqMetaBase::Custom600Headers() const
906 {
907 std::string Header = "\nIndex-File: true";
908 std::string MaximumSize;
909 strprintf(MaximumSize, "\nMaximum-Size: %i",
910 _config->FindI("Acquire::MaxReleaseFileSize", 10*1000*1000));
911 Header += MaximumSize;
912
913 string const FinalFile = GetFinalFilename();
914 struct stat Buf;
915 if (stat(FinalFile.c_str(),&Buf) == 0)
916 Header += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
917
918 return Header;
919 }
920 /*}}}*/
921 // AcqMetaBase::QueueForSignatureVerify /*{{{*/
922 void pkgAcqMetaBase::QueueForSignatureVerify(pkgAcqTransactionItem * const I, std::string const &File, std::string const &Signature)
923 {
924 AuthPass = true;
925 I->Desc.URI = "gpgv:" + Signature;
926 I->DestFile = File;
927 QueueURI(I->Desc);
928 I->SetActiveSubprocess("gpgv");
929 }
930 /*}}}*/
931 // AcqMetaBase::CheckDownloadDone /*{{{*/
932 bool pkgAcqMetaBase::CheckDownloadDone(pkgAcqTransactionItem * const I, const std::string &Message, HashStringList const &Hashes) const
933 {
934 // We have just finished downloading a Release file (it is not
935 // verified yet)
936
937 std::string const FileName = LookupTag(Message,"Filename");
938 if (FileName != I->DestFile && RealFileExists(I->DestFile) == false)
939 {
940 I->Local = true;
941 I->Desc.URI = "copy:" + FileName;
942 I->QueueURI(I->Desc);
943 return false;
944 }
945
946 // make sure to verify against the right file on I-M-S hit
947 bool IMSHit = StringToBool(LookupTag(Message,"IMS-Hit"), false);
948 if (IMSHit == false && Hashes.usable())
949 {
950 // detect IMS-Hits servers haven't detected by Hash comparison
951 std::string const FinalFile = I->GetFinalFilename();
952 if (RealFileExists(FinalFile) && Hashes.VerifyFile(FinalFile) == true)
953 {
954 IMSHit = true;
955 RemoveFile("CheckDownloadDone", I->DestFile);
956 }
957 }
958
959 if(IMSHit == true)
960 {
961 // for simplicity, the transaction manager is always InRelease
962 // even if it doesn't exist.
963 if (TransactionManager != NULL)
964 TransactionManager->IMSHit = true;
965 I->PartialFile = I->DestFile = I->GetFinalFilename();
966 }
967
968 // set Item to complete as the remaining work is all local (verify etc)
969 I->Complete = true;
970
971 return true;
972 }
973 /*}}}*/
974 bool pkgAcqMetaBase::CheckAuthDone(string const &Message) /*{{{*/
975 {
976 // At this point, the gpgv method has succeeded, so there is a
977 // valid signature from a key in the trusted keyring. We
978 // perform additional verification of its contents, and use them
979 // to verify the indexes we are about to download
980
981 if (TransactionManager->IMSHit == false)
982 {
983 // open the last (In)Release if we have it
984 std::string const FinalFile = GetFinalFilename();
985 std::string FinalRelease;
986 std::string FinalInRelease;
987 if (APT::String::Endswith(FinalFile, "InRelease"))
988 {
989 FinalInRelease = FinalFile;
990 FinalRelease = FinalFile.substr(0, FinalFile.length() - strlen("InRelease")) + "Release";
991 }
992 else
993 {
994 FinalInRelease = FinalFile.substr(0, FinalFile.length() - strlen("Release")) + "InRelease";
995 FinalRelease = FinalFile;
996 }
997 if (RealFileExists(FinalInRelease) || RealFileExists(FinalRelease))
998 {
999 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
1000 if (TransactionManager->LastMetaIndexParser != NULL)
1001 {
1002 _error->PushToStack();
1003 if (RealFileExists(FinalInRelease))
1004 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
1005 else
1006 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
1007 // its unlikely to happen, but if what we have is bad ignore it
1008 if (_error->PendingError())
1009 {
1010 delete TransactionManager->LastMetaIndexParser;
1011 TransactionManager->LastMetaIndexParser = NULL;
1012 }
1013 _error->RevertToStack();
1014 }
1015 }
1016 }
1017
1018 if (TransactionManager->MetaIndexParser->Load(DestFile, &ErrorText) == false)
1019 {
1020 Status = StatAuthError;
1021 return false;
1022 }
1023
1024 if (!VerifyVendor(Message))
1025 {
1026 Status = StatAuthError;
1027 return false;
1028 }
1029
1030 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1031 std::cerr << "Signature verification succeeded: "
1032 << DestFile << std::endl;
1033
1034 // Download further indexes with verification
1035 QueueIndexes(true);
1036
1037 return true;
1038 }
1039 /*}}}*/
1040 void pkgAcqMetaBase::QueueIndexes(bool const verify) /*{{{*/
1041 {
1042 // at this point the real Items are loaded in the fetcher
1043 ExpectedAdditionalItems = 0;
1044
1045 bool metaBaseSupportsByHash = false;
1046 if (TransactionManager != NULL && TransactionManager->MetaIndexParser != NULL)
1047 metaBaseSupportsByHash = TransactionManager->MetaIndexParser->GetSupportsAcquireByHash();
1048
1049 for (std::vector <IndexTarget>::iterator Target = IndexTargets.begin();
1050 Target != IndexTargets.end();
1051 ++Target)
1052 {
1053 bool trypdiff = Target->OptionBool(IndexTarget::PDIFFS);
1054 if (verify == true)
1055 {
1056 if (TransactionManager->MetaIndexParser->Exists(Target->MetaKey) == false)
1057 {
1058 // optional targets that we do not have in the Release file are skipped
1059 if (Target->IsOptional)
1060 continue;
1061
1062 Status = StatAuthError;
1063 strprintf(ErrorText, _("Unable to find expected entry '%s' in Release file (Wrong sources.list entry or malformed file)"), Target->MetaKey.c_str());
1064 return;
1065 }
1066
1067 // autoselect the compression method
1068 std::vector<std::string> types = VectorizeString(Target->Option(IndexTarget::COMPRESSIONTYPES), ' ');
1069 types.erase(std::remove_if(types.begin(), types.end(), [&](std::string const &t) {
1070 if (t == "uncompressed")
1071 return TransactionManager->MetaIndexParser->Exists(Target->MetaKey) == false;
1072 std::string const MetaKey = Target->MetaKey + "." + t;
1073 return TransactionManager->MetaIndexParser->Exists(MetaKey) == false;
1074 }), types.end());
1075 if (types.empty() == false)
1076 {
1077 std::ostringstream os;
1078 // add the special compressiontype byhash first if supported
1079 std::string const useByHashConf = Target->Option(IndexTarget::BY_HASH);
1080 bool useByHash = false;
1081 if(useByHashConf == "force")
1082 useByHash = true;
1083 else
1084 useByHash = StringToBool(useByHashConf) == true && metaBaseSupportsByHash;
1085 if (useByHash == true)
1086 os << "by-hash ";
1087 std::copy(types.begin(), types.end()-1, std::ostream_iterator<std::string>(os, " "));
1088 os << *types.rbegin();
1089 Target->Options["COMPRESSIONTYPES"] = os.str();
1090 }
1091 else
1092 Target->Options["COMPRESSIONTYPES"].clear();
1093
1094 std::string filename = GetFinalFileNameFromURI(Target->URI);
1095 if (RealFileExists(filename) == false)
1096 {
1097 if (Target->KeepCompressed)
1098 {
1099 filename = GetKeepCompressedFileName(filename, *Target);
1100 if (RealFileExists(filename) == false)
1101 filename.clear();
1102 }
1103 else
1104 filename.clear();
1105 }
1106
1107 if (filename.empty() == false)
1108 {
1109 // if the Release file is a hit and we have an index it must be the current one
1110 if (TransactionManager->IMSHit == true)
1111 ;
1112 else if (TransactionManager->LastMetaIndexParser != NULL)
1113 {
1114 // see if the file changed since the last Release file
1115 // we use the uncompressed files as we might compress differently compared to the server,
1116 // so the hashes might not match, even if they contain the same data.
1117 HashStringList const newFile = GetExpectedHashesFromFor(TransactionManager->MetaIndexParser, Target->MetaKey);
1118 HashStringList const oldFile = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target->MetaKey);
1119 if (newFile != oldFile)
1120 filename.clear();
1121 }
1122 else
1123 filename.clear();
1124 }
1125 else
1126 trypdiff = false; // no file to patch
1127
1128 if (filename.empty() == false)
1129 {
1130 new NoActionItem(Owner, *Target, filename);
1131 std::string const idxfilename = GetFinalFileNameFromURI(Target->URI + ".diff/Index");
1132 if (FileExists(idxfilename))
1133 new NoActionItem(Owner, *Target, idxfilename);
1134 continue;
1135 }
1136
1137 // check if we have patches available
1138 trypdiff &= TransactionManager->MetaIndexParser->Exists(Target->MetaKey + ".diff/Index");
1139 }
1140 else
1141 {
1142 // if we have no file to patch, no point in trying
1143 std::string filename = GetFinalFileNameFromURI(Target->URI);
1144 if (RealFileExists(filename) == false)
1145 {
1146 if (Target->KeepCompressed)
1147 {
1148 filename = GetKeepCompressedFileName(filename, *Target);
1149 if (RealFileExists(filename) == false)
1150 filename.clear();
1151 }
1152 else
1153 filename.clear();
1154 }
1155 trypdiff &= (filename.empty() == false);
1156 }
1157
1158 // no point in patching from local sources
1159 if (trypdiff)
1160 {
1161 std::string const proto = Target->URI.substr(0, strlen("file:/"));
1162 if (proto == "file:/" || proto == "copy:/" || proto == "cdrom:")
1163 trypdiff = false;
1164 }
1165
1166 // Queue the Index file (Packages, Sources, Translation-$foo, …)
1167 if (trypdiff)
1168 new pkgAcqDiffIndex(Owner, TransactionManager, *Target);
1169 else
1170 new pkgAcqIndex(Owner, TransactionManager, *Target);
1171 }
1172 }
1173 /*}}}*/
1174 bool pkgAcqMetaBase::VerifyVendor(string const &Message) /*{{{*/
1175 {
1176 string::size_type pos;
1177
1178 // check for missing sigs (that where not fatal because otherwise we had
1179 // bombed earlier)
1180 string missingkeys;
1181 string msg = _("There is no public key available for the "
1182 "following key IDs:\n");
1183 pos = Message.find("NO_PUBKEY ");
1184 if (pos != std::string::npos)
1185 {
1186 string::size_type start = pos+strlen("NO_PUBKEY ");
1187 string Fingerprint = Message.substr(start, Message.find("\n")-start);
1188 missingkeys += (Fingerprint);
1189 }
1190 if(!missingkeys.empty())
1191 _error->Warning("%s", (msg + missingkeys).c_str());
1192
1193 string Transformed = TransactionManager->MetaIndexParser->GetExpectedDist();
1194
1195 if (Transformed == "../project/experimental")
1196 {
1197 Transformed = "experimental";
1198 }
1199
1200 pos = Transformed.rfind('/');
1201 if (pos != string::npos)
1202 {
1203 Transformed = Transformed.substr(0, pos);
1204 }
1205
1206 if (Transformed == ".")
1207 {
1208 Transformed = "";
1209 }
1210
1211 if (TransactionManager->MetaIndexParser->GetValidUntil() > 0)
1212 {
1213 time_t const invalid_since = time(NULL) - TransactionManager->MetaIndexParser->GetValidUntil();
1214 if (invalid_since > 0)
1215 {
1216 std::string errmsg;
1217 strprintf(errmsg,
1218 // TRANSLATOR: The first %s is the URL of the bad Release file, the second is
1219 // the time since then the file is invalid - formatted in the same way as in
1220 // the download progress display (e.g. 7d 3h 42min 1s)
1221 _("Release file for %s is expired (invalid since %s). "
1222 "Updates for this repository will not be applied."),
1223 Target.URI.c_str(), TimeToStr(invalid_since).c_str());
1224 if (ErrorText.empty())
1225 ErrorText = errmsg;
1226 return _error->Error("%s", errmsg.c_str());
1227 }
1228 }
1229
1230 /* Did we get a file older than what we have? This is a last minute IMS hit and doubles
1231 as a prevention of downgrading us to older (still valid) files */
1232 if (TransactionManager->IMSHit == false && TransactionManager->LastMetaIndexParser != NULL &&
1233 TransactionManager->LastMetaIndexParser->GetDate() > TransactionManager->MetaIndexParser->GetDate())
1234 {
1235 TransactionManager->IMSHit = true;
1236 RemoveFile("VerifyVendor", DestFile);
1237 PartialFile = DestFile = GetFinalFilename();
1238 // load the 'old' file in the 'new' one instead of flipping pointers as
1239 // the new one isn't owned by us, while the old one is so cleanup would be confused.
1240 TransactionManager->MetaIndexParser->swapLoad(TransactionManager->LastMetaIndexParser);
1241 delete TransactionManager->LastMetaIndexParser;
1242 TransactionManager->LastMetaIndexParser = NULL;
1243 }
1244
1245 if (_config->FindB("Debug::pkgAcquire::Auth", false))
1246 {
1247 std::cerr << "Got Codename: " << TransactionManager->MetaIndexParser->GetCodename() << std::endl;
1248 std::cerr << "Expecting Dist: " << TransactionManager->MetaIndexParser->GetExpectedDist() << std::endl;
1249 std::cerr << "Transformed Dist: " << Transformed << std::endl;
1250 }
1251
1252 if (TransactionManager->MetaIndexParser->CheckDist(Transformed) == false)
1253 {
1254 // This might become fatal one day
1255 // Status = StatAuthError;
1256 // ErrorText = "Conflicting distribution; expected "
1257 // + MetaIndexParser->GetExpectedDist() + " but got "
1258 // + MetaIndexParser->GetCodename();
1259 // return false;
1260 if (!Transformed.empty())
1261 {
1262 _error->Warning(_("Conflicting distribution: %s (expected %s but got %s)"),
1263 Desc.Description.c_str(),
1264 Transformed.c_str(),
1265 TransactionManager->MetaIndexParser->GetCodename().c_str());
1266 }
1267 }
1268
1269 return true;
1270 }
1271 /*}}}*/
1272 pkgAcqMetaBase::~pkgAcqMetaBase()
1273 {
1274 }
1275
1276 pkgAcqMetaClearSig::pkgAcqMetaClearSig(pkgAcquire * const Owner, /*{{{*/
1277 IndexTarget const &ClearsignedTarget,
1278 IndexTarget const &DetachedDataTarget, IndexTarget const &DetachedSigTarget,
1279 std::vector<IndexTarget> const &IndexTargets,
1280 metaIndex * const MetaIndexParser) :
1281 pkgAcqMetaIndex(Owner, this, ClearsignedTarget, DetachedSigTarget, IndexTargets),
1282 d(NULL), ClearsignedTarget(ClearsignedTarget),
1283 DetachedDataTarget(DetachedDataTarget),
1284 MetaIndexParser(MetaIndexParser), LastMetaIndexParser(NULL)
1285 {
1286 // index targets + (worst case:) Release/Release.gpg
1287 ExpectedAdditionalItems = IndexTargets.size() + 2;
1288 TransactionManager->Add(this);
1289 }
1290 /*}}}*/
1291 pkgAcqMetaClearSig::~pkgAcqMetaClearSig() /*{{{*/
1292 {
1293 if (LastMetaIndexParser != NULL)
1294 delete LastMetaIndexParser;
1295 }
1296 /*}}}*/
1297 // pkgAcqMetaClearSig::Custom600Headers - Insert custom request headers /*{{{*/
1298 string pkgAcqMetaClearSig::Custom600Headers() const
1299 {
1300 string Header = pkgAcqMetaBase::Custom600Headers();
1301 Header += "\nFail-Ignore: true";
1302 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1303 if (key.empty() == false)
1304 Header += "\nSigned-By: " + key;
1305
1306 return Header;
1307 }
1308 /*}}}*/
1309 bool pkgAcqMetaClearSig::VerifyDone(std::string const &Message, /*{{{*/
1310 pkgAcquire::MethodConfig const * const Cnf)
1311 {
1312 Item::VerifyDone(Message, Cnf);
1313
1314 if (FileExists(DestFile) && !StartsWithGPGClearTextSignature(DestFile))
1315 return RenameOnError(NotClearsigned);
1316
1317 return true;
1318 }
1319 /*}}}*/
1320 // pkgAcqMetaClearSig::Done - We got a file /*{{{*/
1321 void pkgAcqMetaClearSig::Done(std::string const &Message,
1322 HashStringList const &Hashes,
1323 pkgAcquire::MethodConfig const * const Cnf)
1324 {
1325 Item::Done(Message, Hashes, Cnf);
1326
1327 if(AuthPass == false)
1328 {
1329 if(CheckDownloadDone(this, Message, Hashes) == true)
1330 QueueForSignatureVerify(this, DestFile, DestFile);
1331 return;
1332 }
1333 else if(CheckAuthDone(Message) == true)
1334 {
1335 if (TransactionManager->IMSHit == false)
1336 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1337 else if (RealFileExists(GetFinalFilename()) == false)
1338 {
1339 // We got an InRelease file IMSHit, but we haven't one, which means
1340 // we had a valid Release/Release.gpg combo stepping in, which we have
1341 // to 'acquire' now to ensure list cleanup isn't removing them
1342 new NoActionItem(Owner, DetachedDataTarget);
1343 new NoActionItem(Owner, DetachedSigTarget);
1344 }
1345 }
1346 }
1347 /*}}}*/
1348 void pkgAcqMetaClearSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf) /*{{{*/
1349 {
1350 Item::Failed(Message, Cnf);
1351
1352 // we failed, we will not get additional items from this method
1353 ExpectedAdditionalItems = 0;
1354
1355 if (AuthPass == false)
1356 {
1357 if (Status == StatAuthError || Status == StatTransientNetworkError)
1358 {
1359 // if we expected a ClearTextSignature (InRelease) but got a network
1360 // error or got a file, but it wasn't valid, we end up here (see VerifyDone).
1361 // As these is usually called by web-portals we do not try Release/Release.gpg
1362 // as this is gonna fail anyway and instead abort our try (LP#346386)
1363 TransactionManager->AbortTransaction();
1364 return;
1365 }
1366
1367 // Queue the 'old' InRelease file for removal if we try Release.gpg
1368 // as otherwise the file will stay around and gives a false-auth
1369 // impression (CVE-2012-0214)
1370 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1371 Status = StatDone;
1372
1373 new pkgAcqMetaIndex(Owner, TransactionManager, DetachedDataTarget, DetachedSigTarget, IndexTargets);
1374 }
1375 else
1376 {
1377 if(CheckStopAuthentication(this, Message))
1378 return;
1379
1380 // No Release file was present, or verification failed, so fall
1381 // back to queueing Packages files without verification
1382 // only allow going further if the users explicitely wants it
1383 if(AllowInsecureRepositories(_("The repository '%s' is not signed."), ClearsignedTarget.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1384 {
1385 Status = StatDone;
1386
1387 /* InRelease files become Release files, otherwise
1388 * they would be considered as trusted later on */
1389 string const FinalRelease = GetFinalFileNameFromURI(DetachedDataTarget.URI);
1390 string const PartialRelease = GetPartialFileNameFromURI(DetachedDataTarget.URI);
1391 string const FinalReleasegpg = GetFinalFileNameFromURI(DetachedSigTarget.URI);
1392 string const FinalInRelease = GetFinalFilename();
1393 Rename(DestFile, PartialRelease);
1394 TransactionManager->TransactionStageCopy(this, PartialRelease, FinalRelease);
1395
1396 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1397 {
1398 // open the last Release if we have it
1399 if (TransactionManager->IMSHit == false)
1400 {
1401 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
1402 if (TransactionManager->LastMetaIndexParser != NULL)
1403 {
1404 _error->PushToStack();
1405 if (RealFileExists(FinalInRelease))
1406 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
1407 else
1408 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
1409 // its unlikely to happen, but if what we have is bad ignore it
1410 if (_error->PendingError())
1411 {
1412 delete TransactionManager->LastMetaIndexParser;
1413 TransactionManager->LastMetaIndexParser = NULL;
1414 }
1415 _error->RevertToStack();
1416 }
1417 }
1418 }
1419
1420 // we parse the indexes here because at this point the user wanted
1421 // a repository that may potentially harm him
1422 if (TransactionManager->MetaIndexParser->Load(PartialRelease, &ErrorText) == false || VerifyVendor(Message) == false)
1423 /* expired Release files are still a problem you need extra force for */;
1424 else
1425 QueueIndexes(true);
1426 }
1427 }
1428 }
1429 /*}}}*/
1430
1431 pkgAcqMetaIndex::pkgAcqMetaIndex(pkgAcquire * const Owner, /*{{{*/
1432 pkgAcqMetaClearSig * const TransactionManager,
1433 IndexTarget const &DataTarget,
1434 IndexTarget const &DetachedSigTarget,
1435 vector<IndexTarget> const &IndexTargets) :
1436 pkgAcqMetaBase(Owner, TransactionManager, IndexTargets, DataTarget), d(NULL),
1437 DetachedSigTarget(DetachedSigTarget)
1438 {
1439 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1440 std::clog << "New pkgAcqMetaIndex with TransactionManager "
1441 << this->TransactionManager << std::endl;
1442
1443 DestFile = GetPartialFileNameFromURI(DataTarget.URI);
1444
1445 // Create the item
1446 Desc.Description = DataTarget.Description;
1447 Desc.Owner = this;
1448 Desc.ShortDesc = DataTarget.ShortDesc;
1449 Desc.URI = DataTarget.URI;
1450
1451 // we expect more item
1452 ExpectedAdditionalItems = IndexTargets.size();
1453 QueueURI(Desc);
1454 }
1455 /*}}}*/
1456 void pkgAcqMetaIndex::Done(string const &Message, /*{{{*/
1457 HashStringList const &Hashes,
1458 pkgAcquire::MethodConfig const * const Cfg)
1459 {
1460 Item::Done(Message,Hashes,Cfg);
1461
1462 if(CheckDownloadDone(this, Message, Hashes))
1463 {
1464 // we have a Release file, now download the Signature, all further
1465 // verify/queue for additional downloads will be done in the
1466 // pkgAcqMetaSig::Done() code
1467 new pkgAcqMetaSig(Owner, TransactionManager, DetachedSigTarget, this);
1468 }
1469 }
1470 /*}}}*/
1471 // pkgAcqMetaIndex::Failed - no Release file present /*{{{*/
1472 void pkgAcqMetaIndex::Failed(string const &Message,
1473 pkgAcquire::MethodConfig const * const Cnf)
1474 {
1475 pkgAcquire::Item::Failed(Message, Cnf);
1476 Status = StatDone;
1477
1478 // No Release file was present so fall
1479 // back to queueing Packages files without verification
1480 // only allow going further if the users explicitely wants it
1481 if(AllowInsecureRepositories(_("The repository '%s' does not have a Release file."), Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1482 {
1483 // ensure old Release files are removed
1484 TransactionManager->TransactionStageRemoval(this, GetFinalFilename());
1485
1486 // queue without any kind of hashsum support
1487 QueueIndexes(false);
1488 }
1489 }
1490 /*}}}*/
1491 void pkgAcqMetaIndex::Finished() /*{{{*/
1492 {
1493 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1494 std::clog << "Finished: " << DestFile <<std::endl;
1495 if(TransactionManager != NULL &&
1496 TransactionManager->TransactionHasError() == false)
1497 TransactionManager->CommitTransaction();
1498 }
1499 /*}}}*/
1500 std::string pkgAcqMetaIndex::DescURI() const /*{{{*/
1501 {
1502 return Target.URI;
1503 }
1504 /*}}}*/
1505 pkgAcqMetaIndex::~pkgAcqMetaIndex() {}
1506
1507 // AcqMetaSig::AcqMetaSig - Constructor /*{{{*/
1508 pkgAcqMetaSig::pkgAcqMetaSig(pkgAcquire * const Owner,
1509 pkgAcqMetaClearSig * const TransactionManager,
1510 IndexTarget const &Target,
1511 pkgAcqMetaIndex * const MetaIndex) :
1512 pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL), MetaIndex(MetaIndex)
1513 {
1514 DestFile = GetPartialFileNameFromURI(Target.URI);
1515
1516 // remove any partial downloaded sig-file in partial/.
1517 // it may confuse proxies and is too small to warrant a
1518 // partial download anyway
1519 RemoveFile("pkgAcqMetaSig", DestFile);
1520
1521 // set the TransactionManager
1522 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
1523 std::clog << "New pkgAcqMetaSig with TransactionManager "
1524 << TransactionManager << std::endl;
1525
1526 // Create the item
1527 Desc.Description = Target.Description;
1528 Desc.Owner = this;
1529 Desc.ShortDesc = Target.ShortDesc;
1530 Desc.URI = Target.URI;
1531
1532 // If we got a hit for Release, we will get one for Release.gpg too (or obscure errors),
1533 // so we skip the download step and go instantly to verification
1534 if (TransactionManager->IMSHit == true && RealFileExists(GetFinalFilename()))
1535 {
1536 Complete = true;
1537 Status = StatDone;
1538 PartialFile = DestFile = GetFinalFilename();
1539 MetaIndexFileSignature = DestFile;
1540 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1541 }
1542 else
1543 QueueURI(Desc);
1544 }
1545 /*}}}*/
1546 pkgAcqMetaSig::~pkgAcqMetaSig() /*{{{*/
1547 {
1548 }
1549 /*}}}*/
1550 // pkgAcqMetaSig::Custom600Headers - Insert custom request headers /*{{{*/
1551 std::string pkgAcqMetaSig::Custom600Headers() const
1552 {
1553 std::string Header = pkgAcqTransactionItem::Custom600Headers();
1554 std::string const key = TransactionManager->MetaIndexParser->GetSignedBy();
1555 if (key.empty() == false)
1556 Header += "\nSigned-By: " + key;
1557 return Header;
1558 }
1559 /*}}}*/
1560 // AcqMetaSig::Done - The signature was downloaded/verified /*{{{*/
1561 void pkgAcqMetaSig::Done(string const &Message, HashStringList const &Hashes,
1562 pkgAcquire::MethodConfig const * const Cfg)
1563 {
1564 if (MetaIndexFileSignature.empty() == false)
1565 {
1566 DestFile = MetaIndexFileSignature;
1567 MetaIndexFileSignature.clear();
1568 }
1569 Item::Done(Message, Hashes, Cfg);
1570
1571 if(MetaIndex->AuthPass == false)
1572 {
1573 if(MetaIndex->CheckDownloadDone(this, Message, Hashes) == true)
1574 {
1575 // destfile will be modified to point to MetaIndexFile for the
1576 // gpgv method, so we need to save it here
1577 MetaIndexFileSignature = DestFile;
1578 MetaIndex->QueueForSignatureVerify(this, MetaIndex->DestFile, DestFile);
1579 }
1580 return;
1581 }
1582 else if(MetaIndex->CheckAuthDone(Message) == true)
1583 {
1584 if (TransactionManager->IMSHit == false)
1585 {
1586 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
1587 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1588 }
1589 }
1590 }
1591 /*}}}*/
1592 void pkgAcqMetaSig::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
1593 {
1594 Item::Failed(Message,Cnf);
1595
1596 // check if we need to fail at this point
1597 if (MetaIndex->AuthPass == true && MetaIndex->CheckStopAuthentication(this, Message))
1598 return;
1599
1600 string const FinalRelease = MetaIndex->GetFinalFilename();
1601 string const FinalReleasegpg = GetFinalFilename();
1602 string const FinalInRelease = TransactionManager->GetFinalFilename();
1603
1604 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1605 {
1606 std::string downgrade_msg;
1607 strprintf(downgrade_msg, _("The repository '%s' is no longer signed."),
1608 MetaIndex->Target.Description.c_str());
1609 if(_config->FindB("Acquire::AllowDowngradeToInsecureRepositories"))
1610 {
1611 // meh, the users wants to take risks (we still mark the packages
1612 // from this repository as unauthenticated)
1613 _error->Warning("%s", downgrade_msg.c_str());
1614 _error->Warning(_("This is normally not allowed, but the option "
1615 "Acquire::AllowDowngradeToInsecureRepositories was "
1616 "given to override it."));
1617 Status = StatDone;
1618 } else {
1619 MessageInsecureRepository(true, downgrade_msg);
1620 if (TransactionManager->IMSHit == false)
1621 Rename(MetaIndex->DestFile, MetaIndex->DestFile + ".FAILED");
1622 Item::Failed("Message: " + downgrade_msg, Cnf);
1623 TransactionManager->AbortTransaction();
1624 return;
1625 }
1626 }
1627
1628 // ensures that a Release.gpg file in the lists/ is removed by the transaction
1629 TransactionManager->TransactionStageRemoval(this, DestFile);
1630
1631 // only allow going further if the users explicitely wants it
1632 if (AllowInsecureRepositories(_("The repository '%s' is not signed."), MetaIndex->Target.Description, TransactionManager->MetaIndexParser, TransactionManager, this) == true)
1633 {
1634 if (RealFileExists(FinalReleasegpg) || RealFileExists(FinalInRelease))
1635 {
1636 // open the last Release if we have it
1637 if (TransactionManager->IMSHit == false)
1638 {
1639 TransactionManager->LastMetaIndexParser = TransactionManager->MetaIndexParser->UnloadedClone();
1640 if (TransactionManager->LastMetaIndexParser != NULL)
1641 {
1642 _error->PushToStack();
1643 if (RealFileExists(FinalInRelease))
1644 TransactionManager->LastMetaIndexParser->Load(FinalInRelease, NULL);
1645 else
1646 TransactionManager->LastMetaIndexParser->Load(FinalRelease, NULL);
1647 // its unlikely to happen, but if what we have is bad ignore it
1648 if (_error->PendingError())
1649 {
1650 delete TransactionManager->LastMetaIndexParser;
1651 TransactionManager->LastMetaIndexParser = NULL;
1652 }
1653 _error->RevertToStack();
1654 }
1655 }
1656 }
1657
1658 // we parse the indexes here because at this point the user wanted
1659 // a repository that may potentially harm him
1660 if (TransactionManager->MetaIndexParser->Load(MetaIndex->DestFile, &ErrorText) == false || MetaIndex->VerifyVendor(Message) == false)
1661 /* expired Release files are still a problem you need extra force for */;
1662 else
1663 MetaIndex->QueueIndexes(true);
1664
1665 TransactionManager->TransactionStageCopy(MetaIndex, MetaIndex->DestFile, MetaIndex->GetFinalFilename());
1666 }
1667
1668 // FIXME: this is used often (e.g. in pkgAcqIndexTrans) so refactor
1669 if (Cnf->LocalOnly == true ||
1670 StringToBool(LookupTag(Message,"Transient-Failure"),false) == false)
1671 {
1672 // Ignore this
1673 Status = StatDone;
1674 }
1675 }
1676 /*}}}*/
1677
1678
1679 // AcqBaseIndex - Constructor /*{{{*/
1680 pkgAcqBaseIndex::pkgAcqBaseIndex(pkgAcquire * const Owner,
1681 pkgAcqMetaClearSig * const TransactionManager,
1682 IndexTarget const &Target)
1683 : pkgAcqTransactionItem(Owner, TransactionManager, Target), d(NULL)
1684 {
1685 }
1686 /*}}}*/
1687 pkgAcqBaseIndex::~pkgAcqBaseIndex() {}
1688
1689 // AcqDiffIndex::AcqDiffIndex - Constructor /*{{{*/
1690 // ---------------------------------------------------------------------
1691 /* Get the DiffIndex file first and see if there are patches available
1692 * If so, create a pkgAcqIndexDiffs fetcher that will get and apply the
1693 * patches. If anything goes wrong in that process, it will fall back to
1694 * the original packages file
1695 */
1696 pkgAcqDiffIndex::pkgAcqDiffIndex(pkgAcquire * const Owner,
1697 pkgAcqMetaClearSig * const TransactionManager,
1698 IndexTarget const &Target)
1699 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), diffs(NULL)
1700 {
1701 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
1702
1703 Desc.Owner = this;
1704 Desc.Description = Target.Description + ".diff/Index";
1705 Desc.ShortDesc = Target.ShortDesc;
1706 Desc.URI = Target.URI + ".diff/Index";
1707
1708 DestFile = GetPartialFileNameFromURI(Desc.URI);
1709
1710 if(Debug)
1711 std::clog << "pkgAcqDiffIndex: " << Desc.URI << std::endl;
1712
1713 QueueURI(Desc);
1714 }
1715 /*}}}*/
1716 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
1717 // ---------------------------------------------------------------------
1718 /* The only header we use is the last-modified header. */
1719 string pkgAcqDiffIndex::Custom600Headers() const
1720 {
1721 string const Final = GetFinalFilename();
1722
1723 if(Debug)
1724 std::clog << "Custom600Header-IMS: " << Final << std::endl;
1725
1726 struct stat Buf;
1727 if (stat(Final.c_str(),&Buf) != 0)
1728 return "\nIndex-File: true";
1729
1730 return "\nIndex-File: true\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
1731 }
1732 /*}}}*/
1733 void pkgAcqDiffIndex::QueueOnIMSHit() const /*{{{*/
1734 {
1735 // list cleanup needs to know that this file as well as the already
1736 // present index is ours, so we create an empty diff to save it for us
1737 new pkgAcqIndexDiffs(Owner, TransactionManager, Target);
1738 }
1739 /*}}}*/
1740 bool pkgAcqDiffIndex::ParseDiffIndex(string const &IndexDiffFile) /*{{{*/
1741 {
1742 // failing here is fine: our caller will take care of trying to
1743 // get the complete file if patching fails
1744 if(Debug)
1745 std::clog << "pkgAcqDiffIndex::ParseIndexDiff() " << IndexDiffFile
1746 << std::endl;
1747
1748 FileFd Fd(IndexDiffFile,FileFd::ReadOnly);
1749 pkgTagFile TF(&Fd);
1750 if (Fd.IsOpen() == false || Fd.Failed())
1751 return false;
1752
1753 pkgTagSection Tags;
1754 if(unlikely(TF.Step(Tags) == false))
1755 return false;
1756
1757 HashStringList ServerHashes;
1758 unsigned long long ServerSize = 0;
1759
1760 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1761 {
1762 std::string tagname = *type;
1763 tagname.append("-Current");
1764 std::string const tmp = Tags.FindS(tagname.c_str());
1765 if (tmp.empty() == true)
1766 continue;
1767
1768 string hash;
1769 unsigned long long size;
1770 std::stringstream ss(tmp);
1771 ss >> hash >> size;
1772 if (unlikely(hash.empty() == true))
1773 continue;
1774 if (unlikely(ServerSize != 0 && ServerSize != size))
1775 continue;
1776 ServerHashes.push_back(HashString(*type, hash));
1777 ServerSize = size;
1778 }
1779
1780 if (ServerHashes.usable() == false)
1781 {
1782 if (Debug == true)
1783 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Did not find a good hashsum in the index" << std::endl;
1784 return false;
1785 }
1786
1787 std::string const CurrentPackagesFile = GetFinalFileNameFromURI(Target.URI);
1788 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
1789 if (TargetFileHashes.usable() == false || ServerHashes != TargetFileHashes)
1790 {
1791 if (Debug == true)
1792 {
1793 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": Index has different hashes than parser, probably older, so fail pdiffing" << std::endl;
1794 printHashSumComparision(CurrentPackagesFile, ServerHashes, TargetFileHashes);
1795 }
1796 return false;
1797 }
1798
1799 HashStringList LocalHashes;
1800 // try avoiding calculating the hash here as this is costly
1801 if (TransactionManager->LastMetaIndexParser != NULL)
1802 LocalHashes = GetExpectedHashesFromFor(TransactionManager->LastMetaIndexParser, Target.MetaKey);
1803 if (LocalHashes.usable() == false)
1804 {
1805 FileFd fd(CurrentPackagesFile, FileFd::ReadOnly, FileFd::Auto);
1806 Hashes LocalHashesCalc(ServerHashes);
1807 LocalHashesCalc.AddFD(fd);
1808 LocalHashes = LocalHashesCalc.GetHashStringList();
1809 }
1810
1811 if (ServerHashes == LocalHashes)
1812 {
1813 // we have the same sha1 as the server so we are done here
1814 if(Debug)
1815 std::clog << "pkgAcqDiffIndex: Package file " << CurrentPackagesFile << " is up-to-date" << std::endl;
1816 QueueOnIMSHit();
1817 return true;
1818 }
1819
1820 if(Debug)
1821 std::clog << "Server-Current: " << ServerHashes.find(NULL)->toStr() << " and we start at "
1822 << CurrentPackagesFile << " " << LocalHashes.FileSize() << " " << LocalHashes.find(NULL)->toStr() << std::endl;
1823
1824 // parse all of (provided) history
1825 vector<DiffInfo> available_patches;
1826 bool firstAcceptedHashes = true;
1827 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1828 {
1829 if (LocalHashes.find(*type) == NULL)
1830 continue;
1831
1832 std::string tagname = *type;
1833 tagname.append("-History");
1834 std::string const tmp = Tags.FindS(tagname.c_str());
1835 if (tmp.empty() == true)
1836 continue;
1837
1838 string hash, filename;
1839 unsigned long long size;
1840 std::stringstream ss(tmp);
1841
1842 while (ss >> hash >> size >> filename)
1843 {
1844 if (unlikely(hash.empty() == true || filename.empty() == true))
1845 continue;
1846
1847 // see if we have a record for this file already
1848 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1849 for (; cur != available_patches.end(); ++cur)
1850 {
1851 if (cur->file != filename)
1852 continue;
1853 cur->result_hashes.push_back(HashString(*type, hash));
1854 break;
1855 }
1856 if (cur != available_patches.end())
1857 continue;
1858 if (firstAcceptedHashes == true)
1859 {
1860 DiffInfo next;
1861 next.file = filename;
1862 next.result_hashes.push_back(HashString(*type, hash));
1863 next.result_hashes.FileSize(size);
1864 available_patches.push_back(next);
1865 }
1866 else
1867 {
1868 if (Debug == true)
1869 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1870 << " wasn't in the list for the first parsed hash! (history)" << std::endl;
1871 break;
1872 }
1873 }
1874 firstAcceptedHashes = false;
1875 }
1876
1877 if (unlikely(available_patches.empty() == true))
1878 {
1879 if (Debug)
1880 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1881 << "Couldn't find any patches for the patch series." << std::endl;
1882 return false;
1883 }
1884
1885 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1886 {
1887 if (LocalHashes.find(*type) == NULL)
1888 continue;
1889
1890 std::string tagname = *type;
1891 tagname.append("-Patches");
1892 std::string const tmp = Tags.FindS(tagname.c_str());
1893 if (tmp.empty() == true)
1894 continue;
1895
1896 string hash, filename;
1897 unsigned long long size;
1898 std::stringstream ss(tmp);
1899
1900 while (ss >> hash >> size >> filename)
1901 {
1902 if (unlikely(hash.empty() == true || filename.empty() == true))
1903 continue;
1904
1905 // see if we have a record for this file already
1906 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1907 for (; cur != available_patches.end(); ++cur)
1908 {
1909 if (cur->file != filename)
1910 continue;
1911 if (cur->patch_hashes.empty())
1912 cur->patch_hashes.FileSize(size);
1913 cur->patch_hashes.push_back(HashString(*type, hash));
1914 break;
1915 }
1916 if (cur != available_patches.end())
1917 continue;
1918 if (Debug == true)
1919 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1920 << " wasn't in the list for the first parsed hash! (patches)" << std::endl;
1921 break;
1922 }
1923 }
1924
1925 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
1926 {
1927 std::string tagname = *type;
1928 tagname.append("-Download");
1929 std::string const tmp = Tags.FindS(tagname.c_str());
1930 if (tmp.empty() == true)
1931 continue;
1932
1933 string hash, filename;
1934 unsigned long long size;
1935 std::stringstream ss(tmp);
1936
1937 // FIXME: all of pdiff supports only .gz compressed patches
1938 while (ss >> hash >> size >> filename)
1939 {
1940 if (unlikely(hash.empty() == true || filename.empty() == true))
1941 continue;
1942 if (unlikely(APT::String::Endswith(filename, ".gz") == false))
1943 continue;
1944 filename.erase(filename.length() - 3);
1945
1946 // see if we have a record for this file already
1947 std::vector<DiffInfo>::iterator cur = available_patches.begin();
1948 for (; cur != available_patches.end(); ++cur)
1949 {
1950 if (cur->file != filename)
1951 continue;
1952 if (cur->download_hashes.empty())
1953 cur->download_hashes.FileSize(size);
1954 cur->download_hashes.push_back(HashString(*type, hash));
1955 break;
1956 }
1957 if (cur != available_patches.end())
1958 continue;
1959 if (Debug == true)
1960 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": File " << filename
1961 << " wasn't in the list for the first parsed hash! (download)" << std::endl;
1962 break;
1963 }
1964 }
1965
1966
1967 bool foundStart = false;
1968 for (std::vector<DiffInfo>::iterator cur = available_patches.begin();
1969 cur != available_patches.end(); ++cur)
1970 {
1971 if (LocalHashes != cur->result_hashes)
1972 continue;
1973
1974 available_patches.erase(available_patches.begin(), cur);
1975 foundStart = true;
1976 break;
1977 }
1978
1979 if (foundStart == false || unlikely(available_patches.empty() == true))
1980 {
1981 if (Debug)
1982 std::clog << "pkgAcqDiffIndex: " << IndexDiffFile << ": "
1983 << "Couldn't find the start of the patch series." << std::endl;
1984 return false;
1985 }
1986
1987 // patching with too many files is rather slow compared to a fast download
1988 unsigned long const fileLimit = _config->FindI("Acquire::PDiffs::FileLimit", 0);
1989 if (fileLimit != 0 && fileLimit < available_patches.size())
1990 {
1991 if (Debug)
1992 std::clog << "Need " << available_patches.size() << " diffs (Limit is " << fileLimit
1993 << ") so fallback to complete download" << std::endl;
1994 return false;
1995 }
1996
1997 // calculate the size of all patches we have to get
1998 // note that all sizes are uncompressed, while we download compressed files
1999 unsigned long long patchesSize = 0;
2000 for (std::vector<DiffInfo>::const_iterator cur = available_patches.begin();
2001 cur != available_patches.end(); ++cur)
2002 patchesSize += cur->patch_hashes.FileSize();
2003 unsigned long long const sizeLimit = ServerSize * _config->FindI("Acquire::PDiffs::SizeLimit", 100);
2004 if (sizeLimit > 0 && (sizeLimit/100) < patchesSize)
2005 {
2006 if (Debug)
2007 std::clog << "Need " << patchesSize << " bytes (Limit is " << sizeLimit/100
2008 << ") so fallback to complete download" << std::endl;
2009 return false;
2010 }
2011
2012 // we have something, queue the diffs
2013 string::size_type const last_space = Description.rfind(" ");
2014 if(last_space != string::npos)
2015 Description.erase(last_space, Description.size()-last_space);
2016
2017 /* decide if we should download patches one by one or in one go:
2018 The first is good if the server merges patches, but many don't so client
2019 based merging can be attempt in which case the second is better.
2020 "bad things" will happen if patches are merged on the server,
2021 but client side merging is attempt as well */
2022 bool pdiff_merge = _config->FindB("Acquire::PDiffs::Merge", true);
2023 if (pdiff_merge == true)
2024 {
2025 // reprepro adds this flag if it has merged patches on the server
2026 std::string const precedence = Tags.FindS("X-Patch-Precedence");
2027 pdiff_merge = (precedence != "merged");
2028 }
2029
2030 if (pdiff_merge == false)
2031 new pkgAcqIndexDiffs(Owner, TransactionManager, Target, available_patches);
2032 else
2033 {
2034 diffs = new std::vector<pkgAcqIndexMergeDiffs*>(available_patches.size());
2035 for(size_t i = 0; i < available_patches.size(); ++i)
2036 (*diffs)[i] = new pkgAcqIndexMergeDiffs(Owner, TransactionManager,
2037 Target,
2038 available_patches[i],
2039 diffs);
2040 }
2041
2042 Complete = false;
2043 Status = StatDone;
2044 Dequeue();
2045 return true;
2046 }
2047 /*}}}*/
2048 void pkgAcqDiffIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2049 {
2050 Item::Failed(Message,Cnf);
2051 Status = StatDone;
2052
2053 if(Debug)
2054 std::clog << "pkgAcqDiffIndex failed: " << Desc.URI << " with " << Message << std::endl
2055 << "Falling back to normal index file acquire" << std::endl;
2056
2057 new pkgAcqIndex(Owner, TransactionManager, Target);
2058 }
2059 /*}}}*/
2060 void pkgAcqDiffIndex::Done(string const &Message,HashStringList const &Hashes, /*{{{*/
2061 pkgAcquire::MethodConfig const * const Cnf)
2062 {
2063 if(Debug)
2064 std::clog << "pkgAcqDiffIndex::Done(): " << Desc.URI << std::endl;
2065
2066 Item::Done(Message, Hashes, Cnf);
2067
2068 string const FinalFile = GetFinalFilename();
2069 if(StringToBool(LookupTag(Message,"IMS-Hit"),false))
2070 DestFile = FinalFile;
2071
2072 if(ParseDiffIndex(DestFile) == false)
2073 {
2074 Failed("Message: Couldn't parse pdiff index", Cnf);
2075 // queue for final move - this should happen even if we fail
2076 // while parsing (e.g. on sizelimit) and download the complete file.
2077 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2078 return;
2079 }
2080
2081 TransactionManager->TransactionStageCopy(this, DestFile, FinalFile);
2082
2083 Complete = true;
2084 Status = StatDone;
2085 Dequeue();
2086
2087 return;
2088 }
2089 /*}}}*/
2090 pkgAcqDiffIndex::~pkgAcqDiffIndex()
2091 {
2092 if (diffs != NULL)
2093 delete diffs;
2094 }
2095
2096 // AcqIndexDiffs::AcqIndexDiffs - Constructor /*{{{*/
2097 // ---------------------------------------------------------------------
2098 /* The package diff is added to the queue. one object is constructed
2099 * for each diff and the index
2100 */
2101 pkgAcqIndexDiffs::pkgAcqIndexDiffs(pkgAcquire * const Owner,
2102 pkgAcqMetaClearSig * const TransactionManager,
2103 IndexTarget const &Target,
2104 vector<DiffInfo> const &diffs)
2105 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2106 available_patches(diffs)
2107 {
2108 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2109
2110 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2111
2112 Desc.Owner = this;
2113 Description = Target.Description;
2114 Desc.ShortDesc = Target.ShortDesc;
2115
2116 if(available_patches.empty() == true)
2117 {
2118 // we are done (yeah!), check hashes against the final file
2119 DestFile = GetKeepCompressedFileName(GetFinalFileNameFromURI(Target.URI), Target);
2120 Finish(true);
2121 }
2122 else
2123 {
2124 if (BootstrapPDiffWith(GetPartialFileNameFromURI(Target.URI), GetFinalFilename(), Target) == false)
2125 {
2126 Failed("Bootstrapping of " + DestFile + " failed", NULL);
2127 return;
2128 }
2129
2130 // get the next diff
2131 State = StateFetchDiff;
2132 QueueNextDiff();
2133 }
2134 }
2135 /*}}}*/
2136 void pkgAcqIndexDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2137 {
2138 Item::Failed(Message,Cnf);
2139 Status = StatDone;
2140
2141 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2142 if(Debug)
2143 std::clog << "pkgAcqIndexDiffs failed: " << Desc.URI << " with " << Message << std::endl
2144 << "Falling back to normal index file acquire " << std::endl;
2145 RenameOnError(PDiffError);
2146 std::string const patchname = GetDiffsPatchFileName(DestFile);
2147 if (RealFileExists(patchname))
2148 rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
2149 new pkgAcqIndex(Owner, TransactionManager, Target);
2150 Finish();
2151 }
2152 /*}}}*/
2153 // Finish - helper that cleans the item out of the fetcher queue /*{{{*/
2154 void pkgAcqIndexDiffs::Finish(bool allDone)
2155 {
2156 if(Debug)
2157 std::clog << "pkgAcqIndexDiffs::Finish(): "
2158 << allDone << " "
2159 << Desc.URI << std::endl;
2160
2161 // we restore the original name, this is required, otherwise
2162 // the file will be cleaned
2163 if(allDone)
2164 {
2165 std::string Final = GetFinalFilename();
2166 if (Target.KeepCompressed)
2167 {
2168 std::string const ext = flExtension(DestFile);
2169 if (ext.empty() == false)
2170 Final.append(".").append(ext);
2171 }
2172 TransactionManager->TransactionStageCopy(this, DestFile, Final);
2173
2174 // this is for the "real" finish
2175 Complete = true;
2176 Status = StatDone;
2177 Dequeue();
2178 if(Debug)
2179 std::clog << "\n\nallDone: " << DestFile << "\n" << std::endl;
2180 return;
2181 }
2182 else
2183 DestFile.clear();
2184
2185 if(Debug)
2186 std::clog << "Finishing: " << Desc.URI << std::endl;
2187 Complete = false;
2188 Status = StatDone;
2189 Dequeue();
2190 return;
2191 }
2192 /*}}}*/
2193 bool pkgAcqIndexDiffs::QueueNextDiff() /*{{{*/
2194 {
2195 // calc sha1 of the just patched file
2196 std::string const FinalFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2197 if(!FileExists(FinalFile))
2198 {
2199 Failed("Message: No FinalFile " + FinalFile + " available", NULL);
2200 return false;
2201 }
2202
2203 FileFd fd(FinalFile, FileFd::ReadOnly, FileFd::Extension);
2204 Hashes LocalHashesCalc;
2205 LocalHashesCalc.AddFD(fd);
2206 HashStringList const LocalHashes = LocalHashesCalc.GetHashStringList();
2207
2208 if(Debug)
2209 std::clog << "QueueNextDiff: " << FinalFile << " (" << LocalHashes.find(NULL)->toStr() << ")" << std::endl;
2210
2211 HashStringList const TargetFileHashes = GetExpectedHashesFor(Target.MetaKey);
2212 if (unlikely(LocalHashes.usable() == false || TargetFileHashes.usable() == false))
2213 {
2214 Failed("Local/Expected hashes are not usable", NULL);
2215 return false;
2216 }
2217
2218
2219 // final file reached before all patches are applied
2220 if(LocalHashes == TargetFileHashes)
2221 {
2222 Finish(true);
2223 return true;
2224 }
2225
2226 // remove all patches until the next matching patch is found
2227 // this requires the Index file to be ordered
2228 for(vector<DiffInfo>::iterator I = available_patches.begin();
2229 available_patches.empty() == false &&
2230 I != available_patches.end() &&
2231 I->result_hashes != LocalHashes;
2232 ++I)
2233 {
2234 available_patches.erase(I);
2235 }
2236
2237 // error checking and falling back if no patch was found
2238 if(available_patches.empty() == true)
2239 {
2240 Failed("No patches left to reach target", NULL);
2241 return false;
2242 }
2243
2244 // queue the right diff
2245 Desc.URI = Target.URI + ".diff/" + available_patches[0].file + ".gz";
2246 Desc.Description = Description + " " + available_patches[0].file + string(".pdiff");
2247 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + available_patches[0].file), Target);
2248
2249 if(Debug)
2250 std::clog << "pkgAcqIndexDiffs::QueueNextDiff(): " << Desc.URI << std::endl;
2251
2252 QueueURI(Desc);
2253
2254 return true;
2255 }
2256 /*}}}*/
2257 void pkgAcqIndexDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2258 pkgAcquire::MethodConfig const * const Cnf)
2259 {
2260 if(Debug)
2261 std::clog << "pkgAcqIndexDiffs::Done(): " << Desc.URI << std::endl;
2262
2263 Item::Done(Message, Hashes, Cnf);
2264
2265 std::string const FinalFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2266 std::string const PatchFile = GetDiffsPatchFileName(FinalFile);
2267
2268 // success in downloading a diff, enter ApplyDiff state
2269 if(State == StateFetchDiff)
2270 {
2271 Rename(DestFile, PatchFile);
2272
2273 if(Debug)
2274 std::clog << "Sending to rred method: " << FinalFile << std::endl;
2275
2276 State = StateApplyDiff;
2277 Local = true;
2278 Desc.URI = "rred:" + FinalFile;
2279 QueueURI(Desc);
2280 SetActiveSubprocess("rred");
2281 return;
2282 }
2283
2284 // success in download/apply a diff, queue next (if needed)
2285 if(State == StateApplyDiff)
2286 {
2287 // remove the just applied patch
2288 available_patches.erase(available_patches.begin());
2289 RemoveFile("pkgAcqIndexDiffs::Done", PatchFile);
2290
2291 // move into place
2292 if(Debug)
2293 {
2294 std::clog << "Moving patched file in place: " << std::endl
2295 << DestFile << " -> " << FinalFile << std::endl;
2296 }
2297 Rename(DestFile,FinalFile);
2298 chmod(FinalFile.c_str(),0644);
2299
2300 // see if there is more to download
2301 if(available_patches.empty() == false) {
2302 new pkgAcqIndexDiffs(Owner, TransactionManager, Target,
2303 available_patches);
2304 return Finish();
2305 } else
2306 // update
2307 DestFile = FinalFile;
2308 return Finish(true);
2309 }
2310 }
2311 /*}}}*/
2312 std::string pkgAcqIndexDiffs::Custom600Headers() const /*{{{*/
2313 {
2314 if(State != StateApplyDiff)
2315 return pkgAcqBaseIndex::Custom600Headers();
2316 std::ostringstream patchhashes;
2317 HashStringList const ExpectedHashes = available_patches[0].patch_hashes;
2318 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2319 patchhashes << "\nPatch-0-" << hs->HashType() << "-Hash: " << hs->HashValue();
2320 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2321 return patchhashes.str();
2322 }
2323 /*}}}*/
2324 pkgAcqIndexDiffs::~pkgAcqIndexDiffs() {}
2325
2326 // AcqIndexMergeDiffs::AcqIndexMergeDiffs - Constructor /*{{{*/
2327 pkgAcqIndexMergeDiffs::pkgAcqIndexMergeDiffs(pkgAcquire * const Owner,
2328 pkgAcqMetaClearSig * const TransactionManager,
2329 IndexTarget const &Target,
2330 DiffInfo const &patch,
2331 std::vector<pkgAcqIndexMergeDiffs*> const * const allPatches)
2332 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL),
2333 patch(patch), allPatches(allPatches), State(StateFetchDiff)
2334 {
2335 Debug = _config->FindB("Debug::pkgAcquire::Diffs",false);
2336
2337 Desc.Owner = this;
2338 Description = Target.Description;
2339 Desc.ShortDesc = Target.ShortDesc;
2340
2341 Desc.URI = Target.URI + ".diff/" + patch.file + ".gz";
2342 Desc.Description = Description + " " + patch.file + string(".pdiff");
2343
2344 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI + ".diff/" + patch.file), Target);
2345
2346 if(Debug)
2347 std::clog << "pkgAcqIndexMergeDiffs: " << Desc.URI << std::endl;
2348
2349 QueueURI(Desc);
2350 }
2351 /*}}}*/
2352 void pkgAcqIndexMergeDiffs::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)/*{{{*/
2353 {
2354 if(Debug)
2355 std::clog << "pkgAcqIndexMergeDiffs failed: " << Desc.URI << " with " << Message << std::endl;
2356
2357 Item::Failed(Message,Cnf);
2358 Status = StatDone;
2359
2360 // check if we are the first to fail, otherwise we are done here
2361 State = StateDoneDiff;
2362 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2363 I != allPatches->end(); ++I)
2364 if ((*I)->State == StateErrorDiff)
2365 return;
2366
2367 // first failure means we should fallback
2368 State = StateErrorDiff;
2369 if (Debug)
2370 std::clog << "Falling back to normal index file acquire" << std::endl;
2371 DestFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2372 RenameOnError(PDiffError);
2373 std::string const patchname = GetMergeDiffsPatchFileName(DestFile, patch.file);
2374 if (RealFileExists(patchname))
2375 rename(patchname.c_str(), std::string(patchname + ".FAILED").c_str());
2376 new pkgAcqIndex(Owner, TransactionManager, Target);
2377 DestFile.clear();
2378 }
2379 /*}}}*/
2380 void pkgAcqIndexMergeDiffs::Done(string const &Message, HashStringList const &Hashes, /*{{{*/
2381 pkgAcquire::MethodConfig const * const Cnf)
2382 {
2383 if(Debug)
2384 std::clog << "pkgAcqIndexMergeDiffs::Done(): " << Desc.URI << std::endl;
2385
2386 Item::Done(Message, Hashes, Cnf);
2387
2388 std::string const UncompressedFinalFile = GetPartialFileNameFromURI(Target.URI);
2389 std::string const FinalFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2390 if (State == StateFetchDiff)
2391 {
2392 Rename(DestFile, GetMergeDiffsPatchFileName(FinalFile, patch.file));
2393
2394 // check if this is the last completed diff
2395 State = StateDoneDiff;
2396 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2397 I != allPatches->end(); ++I)
2398 if ((*I)->State != StateDoneDiff)
2399 {
2400 if(Debug)
2401 std::clog << "Not the last done diff in the batch: " << Desc.URI << std::endl;
2402 return;
2403 }
2404
2405 // this is the last completed diff, so we are ready to apply now
2406 State = StateApplyDiff;
2407
2408 if (BootstrapPDiffWith(UncompressedFinalFile, GetFinalFilename(), Target) == false)
2409 {
2410 Failed("Bootstrapping of " + DestFile + " failed", NULL);
2411 return;
2412 }
2413
2414 if(Debug)
2415 std::clog << "Sending to rred method: " << FinalFile << std::endl;
2416
2417 Local = true;
2418 Desc.URI = "rred:" + FinalFile;
2419 QueueURI(Desc);
2420 SetActiveSubprocess("rred");
2421 return;
2422 }
2423 // success in download/apply all diffs, clean up
2424 else if (State == StateApplyDiff)
2425 {
2426 // move the result into place
2427 std::string const Final = GetKeepCompressedFileName(GetFinalFilename(), Target);
2428 if(Debug)
2429 std::clog << "Queue patched file in place: " << std::endl
2430 << DestFile << " -> " << Final << std::endl;
2431
2432 // queue for copy by the transaction manager
2433 TransactionManager->TransactionStageCopy(this, DestFile, Final);
2434
2435 // ensure the ed's are gone regardless of list-cleanup
2436 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2437 I != allPatches->end(); ++I)
2438 {
2439 std::string const PartialFile = GetKeepCompressedFileName(GetPartialFileNameFromURI(Target.URI), Target);
2440 std::string const patch = GetMergeDiffsPatchFileName(PartialFile, (*I)->patch.file);
2441 RemoveFile("pkgAcqIndexMergeDiffs::Done", patch);
2442 }
2443 RemoveFile("pkgAcqIndexMergeDiffs::Done", FinalFile);
2444
2445 // all set and done
2446 Complete = true;
2447 if(Debug)
2448 std::clog << "allDone: " << DestFile << "\n" << std::endl;
2449 }
2450 }
2451 /*}}}*/
2452 std::string pkgAcqIndexMergeDiffs::Custom600Headers() const /*{{{*/
2453 {
2454 if(State != StateApplyDiff)
2455 return pkgAcqBaseIndex::Custom600Headers();
2456 std::ostringstream patchhashes;
2457 unsigned int seen_patches = 0;
2458 for (std::vector<pkgAcqIndexMergeDiffs *>::const_iterator I = allPatches->begin();
2459 I != allPatches->end(); ++I)
2460 {
2461 HashStringList const ExpectedHashes = (*I)->patch.patch_hashes;
2462 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
2463 patchhashes << "\nPatch-" << seen_patches << "-" << hs->HashType() << "-Hash: " << hs->HashValue();
2464 ++seen_patches;
2465 }
2466 patchhashes << pkgAcqBaseIndex::Custom600Headers();
2467 return patchhashes.str();
2468 }
2469 /*}}}*/
2470 pkgAcqIndexMergeDiffs::~pkgAcqIndexMergeDiffs() {}
2471
2472 // AcqIndex::AcqIndex - Constructor /*{{{*/
2473 pkgAcqIndex::pkgAcqIndex(pkgAcquire * const Owner,
2474 pkgAcqMetaClearSig * const TransactionManager,
2475 IndexTarget const &Target)
2476 : pkgAcqBaseIndex(Owner, TransactionManager, Target), d(NULL), Stage(STAGE_DOWNLOAD),
2477 CompressionExtensions(Target.Option(IndexTarget::COMPRESSIONTYPES))
2478 {
2479 Init(Target.URI, Target.Description, Target.ShortDesc);
2480
2481 if(_config->FindB("Debug::Acquire::Transaction", false) == true)
2482 std::clog << "New pkgIndex with TransactionManager "
2483 << TransactionManager << std::endl;
2484 }
2485 /*}}}*/
2486 // AcqIndex::Init - defered Constructor /*{{{*/
2487 static void NextCompressionExtension(std::string &CurrentCompressionExtension, std::string &CompressionExtensions, bool const preview)
2488 {
2489 size_t const nextExt = CompressionExtensions.find(' ');
2490 if (nextExt == std::string::npos)
2491 {
2492 CurrentCompressionExtension = CompressionExtensions;
2493 if (preview == false)
2494 CompressionExtensions.clear();
2495 }
2496 else
2497 {
2498 CurrentCompressionExtension = CompressionExtensions.substr(0, nextExt);
2499 if (preview == false)
2500 CompressionExtensions = CompressionExtensions.substr(nextExt+1);
2501 }
2502 }
2503 void pkgAcqIndex::Init(string const &URI, string const &URIDesc,
2504 string const &ShortDesc)
2505 {
2506 Stage = STAGE_DOWNLOAD;
2507
2508 DestFile = GetPartialFileNameFromURI(URI);
2509 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, false);
2510
2511 if (CurrentCompressionExtension == "uncompressed")
2512 {
2513 Desc.URI = URI;
2514 }
2515 else if (CurrentCompressionExtension == "by-hash")
2516 {
2517 NextCompressionExtension(CurrentCompressionExtension, CompressionExtensions, true);
2518 if(unlikely(TransactionManager->MetaIndexParser == NULL || CurrentCompressionExtension.empty()))
2519 return;
2520 if (CurrentCompressionExtension != "uncompressed")
2521 {
2522 Desc.URI = URI + '.' + CurrentCompressionExtension;
2523 DestFile = DestFile + '.' + CurrentCompressionExtension;
2524 }
2525
2526 HashStringList const Hashes = GetExpectedHashes();
2527 HashString const * const TargetHash = Hashes.find(NULL);
2528 if (unlikely(TargetHash == nullptr))
2529 return;
2530 std::string const ByHash = "/by-hash/" + TargetHash->HashType() + "/" + TargetHash->HashValue();
2531 size_t const trailing_slash = Desc.URI.find_last_of("/");
2532 if (unlikely(trailing_slash == std::string::npos))
2533 return;
2534 Desc.URI = Desc.URI.replace(
2535 trailing_slash,
2536 Desc.URI.substr(trailing_slash+1).size()+1,
2537 ByHash);
2538 }
2539 else if (unlikely(CurrentCompressionExtension.empty()))
2540 return;
2541 else
2542 {
2543 Desc.URI = URI + '.' + CurrentCompressionExtension;
2544 DestFile = DestFile + '.' + CurrentCompressionExtension;
2545 }
2546
2547
2548 Desc.Description = URIDesc;
2549 Desc.Owner = this;
2550 Desc.ShortDesc = ShortDesc;
2551
2552 QueueURI(Desc);
2553 }
2554 /*}}}*/
2555 // AcqIndex::Custom600Headers - Insert custom request headers /*{{{*/
2556 // ---------------------------------------------------------------------
2557 /* The only header we use is the last-modified header. */
2558 string pkgAcqIndex::Custom600Headers() const
2559 {
2560 string Final = GetFinalFilename();
2561
2562 string msg = "\nIndex-File: true";
2563 struct stat Buf;
2564 if (stat(Final.c_str(),&Buf) == 0)
2565 msg += "\nLast-Modified: " + TimeRFC1123(Buf.st_mtime);
2566
2567 if(Target.IsOptional)
2568 msg += "\nFail-Ignore: true";
2569
2570 return msg;
2571 }
2572 /*}}}*/
2573 // AcqIndex::Failed - getting the indexfile failed /*{{{*/
2574 void pkgAcqIndex::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2575 {
2576 Item::Failed(Message,Cnf);
2577
2578 // authorisation matches will not be fixed by other compression types
2579 if (Status != StatAuthError)
2580 {
2581 if (CompressionExtensions.empty() == false)
2582 {
2583 Init(Target.URI, Desc.Description, Desc.ShortDesc);
2584 Status = StatIdle;
2585 return;
2586 }
2587 }
2588
2589 if(Target.IsOptional && GetExpectedHashes().empty() && Stage == STAGE_DOWNLOAD)
2590 Status = StatDone;
2591 else
2592 TransactionManager->AbortTransaction();
2593 }
2594 /*}}}*/
2595 // AcqIndex::ReverifyAfterIMS - Reverify index after an ims-hit /*{{{*/
2596 void pkgAcqIndex::ReverifyAfterIMS()
2597 {
2598 // update destfile to *not* include the compression extension when doing
2599 // a reverify (as its uncompressed on disk already)
2600 DestFile = GetCompressedFileName(Target, GetPartialFileNameFromURI(Target.URI), CurrentCompressionExtension);
2601
2602 // copy FinalFile into partial/ so that we check the hash again
2603 string FinalFile = GetFinalFilename();
2604 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2605 Desc.URI = "copy:" + FinalFile;
2606 QueueURI(Desc);
2607 }
2608 /*}}}*/
2609 // AcqIndex::Done - Finished a fetch /*{{{*/
2610 // ---------------------------------------------------------------------
2611 /* This goes through a number of states.. On the initial fetch the
2612 method could possibly return an alternate filename which points
2613 to the uncompressed version of the file. If this is so the file
2614 is copied into the partial directory. In all other cases the file
2615 is decompressed with a compressed uri. */
2616 void pkgAcqIndex::Done(string const &Message,
2617 HashStringList const &Hashes,
2618 pkgAcquire::MethodConfig const * const Cfg)
2619 {
2620 Item::Done(Message,Hashes,Cfg);
2621
2622 switch(Stage)
2623 {
2624 case STAGE_DOWNLOAD:
2625 StageDownloadDone(Message, Hashes, Cfg);
2626 break;
2627 case STAGE_DECOMPRESS_AND_VERIFY:
2628 StageDecompressDone(Message, Hashes, Cfg);
2629 break;
2630 }
2631 }
2632 /*}}}*/
2633 // AcqIndex::StageDownloadDone - Queue for decompress and verify /*{{{*/
2634 void pkgAcqIndex::StageDownloadDone(string const &Message, HashStringList const &,
2635 pkgAcquire::MethodConfig const * const)
2636 {
2637 Complete = true;
2638
2639 // Handle the unzipd case
2640 std::string FileName = LookupTag(Message,"Alt-Filename");
2641 if (FileName.empty() == false)
2642 {
2643 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2644 Local = true;
2645 DestFile += ".decomp";
2646 Desc.URI = "copy:" + FileName;
2647 QueueURI(Desc);
2648 SetActiveSubprocess("copy");
2649 return;
2650 }
2651 FileName = LookupTag(Message,"Filename");
2652
2653 // Methods like e.g. "file:" will give us a (compressed) FileName that is
2654 // not the "DestFile" we set, in this case we uncompress from the local file
2655 if (FileName != DestFile && RealFileExists(DestFile) == false)
2656 {
2657 Local = true;
2658 if (Target.KeepCompressed == true)
2659 {
2660 // but if we don't keep the uncompress we copy the compressed file first
2661 Stage = STAGE_DOWNLOAD;
2662 Desc.URI = "copy:" + FileName;
2663 QueueURI(Desc);
2664 SetActiveSubprocess("copy");
2665 return;
2666 }
2667 }
2668 else
2669 EraseFileName = FileName;
2670
2671 // we need to verify the file against the current Release file again
2672 // on if-modfied-since hit to avoid a stale attack against us
2673 if(StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
2674 {
2675 // The files timestamp matches, reverify by copy into partial/
2676 EraseFileName = "";
2677 ReverifyAfterIMS();
2678 return;
2679 }
2680
2681 // get the binary name for your used compression type
2682 string decompProg;
2683 if(CurrentCompressionExtension == "uncompressed")
2684 decompProg = "copy";
2685 else
2686 decompProg = _config->Find(string("Acquire::CompressionTypes::").append(CurrentCompressionExtension),"");
2687 if(decompProg.empty() == true)
2688 {
2689 _error->Error("Unsupported extension: %s", CurrentCompressionExtension.c_str());
2690 return;
2691 }
2692
2693 if (Target.KeepCompressed == true)
2694 {
2695 DestFile = "/dev/null";
2696 EraseFileName.clear();
2697 }
2698 else
2699 DestFile += ".decomp";
2700
2701 // queue uri for the next stage
2702 Stage = STAGE_DECOMPRESS_AND_VERIFY;
2703 Desc.URI = decompProg + ":" + FileName;
2704 QueueURI(Desc);
2705 SetActiveSubprocess(decompProg);
2706 }
2707 /*}}}*/
2708 // AcqIndex::StageDecompressDone - Final verification /*{{{*/
2709 void pkgAcqIndex::StageDecompressDone(string const &,
2710 HashStringList const &,
2711 pkgAcquire::MethodConfig const * const)
2712 {
2713 if (Target.KeepCompressed == true && DestFile == "/dev/null")
2714 DestFile = GetPartialFileNameFromURI(Target.URI + '.' + CurrentCompressionExtension);
2715
2716 // Done, queue for rename on transaction finished
2717 TransactionManager->TransactionStageCopy(this, DestFile, GetFinalFilename());
2718 return;
2719 }
2720 /*}}}*/
2721 pkgAcqIndex::~pkgAcqIndex() {}
2722
2723
2724 // AcqArchive::AcqArchive - Constructor /*{{{*/
2725 // ---------------------------------------------------------------------
2726 /* This just sets up the initial fetch environment and queues the first
2727 possibilitiy */
2728 pkgAcqArchive::pkgAcqArchive(pkgAcquire * const Owner,pkgSourceList * const Sources,
2729 pkgRecords * const Recs,pkgCache::VerIterator const &Version,
2730 string &StoreFilename) :
2731 Item(Owner), d(NULL), LocalSource(false), Version(Version), Sources(Sources), Recs(Recs),
2732 StoreFilename(StoreFilename), Vf(Version.FileList()),
2733 Trusted(false)
2734 {
2735 Retries = _config->FindI("Acquire::Retries",0);
2736
2737 if (Version.Arch() == 0)
2738 {
2739 _error->Error(_("I wasn't able to locate a file for the %s package. "
2740 "This might mean you need to manually fix this package. "
2741 "(due to missing arch)"),
2742 Version.ParentPkg().FullName().c_str());
2743 return;
2744 }
2745
2746 /* We need to find a filename to determine the extension. We make the
2747 assumption here that all the available sources for this version share
2748 the same extension.. */
2749 // Skip not source sources, they do not have file fields.
2750 for (; Vf.end() == false; ++Vf)
2751 {
2752 if (Vf.File().Flagged(pkgCache::Flag::NotSource))
2753 continue;
2754 break;
2755 }
2756
2757 // Does not really matter here.. we are going to fail out below
2758 if (Vf.end() != true)
2759 {
2760 // If this fails to get a file name we will bomb out below.
2761 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2762 if (_error->PendingError() == true)
2763 return;
2764
2765 // Generate the final file name as: package_version_arch.foo
2766 StoreFilename = QuoteString(Version.ParentPkg().Name(),"_:") + '_' +
2767 QuoteString(Version.VerStr(),"_:") + '_' +
2768 QuoteString(Version.Arch(),"_:.") +
2769 "." + flExtension(Parse.FileName());
2770 }
2771
2772 // check if we have one trusted source for the package. if so, switch
2773 // to "TrustedOnly" mode - but only if not in AllowUnauthenticated mode
2774 bool const allowUnauth = _config->FindB("APT::Get::AllowUnauthenticated", false);
2775 bool const debugAuth = _config->FindB("Debug::pkgAcquire::Auth", false);
2776 bool seenUntrusted = false;
2777 for (pkgCache::VerFileIterator i = Version.FileList(); i.end() == false; ++i)
2778 {
2779 pkgIndexFile *Index;
2780 if (Sources->FindIndex(i.File(),Index) == false)
2781 continue;
2782
2783 if (debugAuth == true)
2784 std::cerr << "Checking index: " << Index->Describe()
2785 << "(Trusted=" << Index->IsTrusted() << ")" << std::endl;
2786
2787 if (Index->IsTrusted() == true)
2788 {
2789 Trusted = true;
2790 if (allowUnauth == false)
2791 break;
2792 }
2793 else
2794 seenUntrusted = true;
2795 }
2796
2797 // "allow-unauthenticated" restores apts old fetching behaviour
2798 // that means that e.g. unauthenticated file:// uris are higher
2799 // priority than authenticated http:// uris
2800 if (allowUnauth == true && seenUntrusted == true)
2801 Trusted = false;
2802
2803 // Select a source
2804 if (QueueNext() == false && _error->PendingError() == false)
2805 _error->Error(_("Can't find a source to download version '%s' of '%s'"),
2806 Version.VerStr(), Version.ParentPkg().FullName(false).c_str());
2807 }
2808 /*}}}*/
2809 // AcqArchive::QueueNext - Queue the next file source /*{{{*/
2810 // ---------------------------------------------------------------------
2811 /* This queues the next available file version for download. It checks if
2812 the archive is already available in the cache and stashs the MD5 for
2813 checking later. */
2814 bool pkgAcqArchive::QueueNext()
2815 {
2816 for (; Vf.end() == false; ++Vf)
2817 {
2818 pkgCache::PkgFileIterator const PkgF = Vf.File();
2819 // Ignore not source sources
2820 if (PkgF.Flagged(pkgCache::Flag::NotSource))
2821 continue;
2822
2823 // Try to cross match against the source list
2824 pkgIndexFile *Index;
2825 if (Sources->FindIndex(PkgF, Index) == false)
2826 continue;
2827 LocalSource = PkgF.Flagged(pkgCache::Flag::LocalSource);
2828
2829 // only try to get a trusted package from another source if that source
2830 // is also trusted
2831 if(Trusted && !Index->IsTrusted())
2832 continue;
2833
2834 // Grab the text package record
2835 pkgRecords::Parser &Parse = Recs->Lookup(Vf);
2836 if (_error->PendingError() == true)
2837 return false;
2838
2839 string PkgFile = Parse.FileName();
2840 ExpectedHashes = Parse.Hashes();
2841
2842 if (PkgFile.empty() == true)
2843 return _error->Error(_("The package index files are corrupted. No Filename: "
2844 "field for package %s."),
2845 Version.ParentPkg().Name());
2846
2847 Desc.URI = Index->ArchiveURI(PkgFile);
2848 Desc.Description = Index->ArchiveInfo(Version);
2849 Desc.Owner = this;
2850 Desc.ShortDesc = Version.ParentPkg().FullName(true);
2851
2852 // See if we already have the file. (Legacy filenames)
2853 FileSize = Version->Size;
2854 string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(PkgFile);
2855 struct stat Buf;
2856 if (stat(FinalFile.c_str(),&Buf) == 0)
2857 {
2858 // Make sure the size matches
2859 if ((unsigned long long)Buf.st_size == Version->Size)
2860 {
2861 Complete = true;
2862 Local = true;
2863 Status = StatDone;
2864 StoreFilename = DestFile = FinalFile;
2865 return true;
2866 }
2867
2868 /* Hmm, we have a file and its size does not match, this means it is
2869 an old style mismatched arch */
2870 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
2871 }
2872
2873 // Check it again using the new style output filenames
2874 FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(StoreFilename);
2875 if (stat(FinalFile.c_str(),&Buf) == 0)
2876 {
2877 // Make sure the size matches
2878 if ((unsigned long long)Buf.st_size == Version->Size)
2879 {
2880 Complete = true;
2881 Local = true;
2882 Status = StatDone;
2883 StoreFilename = DestFile = FinalFile;
2884 return true;
2885 }
2886
2887 /* Hmm, we have a file and its size does not match, this shouldn't
2888 happen.. */
2889 RemoveFile("pkgAcqArchive::QueueNext", FinalFile);
2890 }
2891
2892 DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + flNotDir(StoreFilename);
2893
2894 // Check the destination file
2895 if (stat(DestFile.c_str(),&Buf) == 0)
2896 {
2897 // Hmm, the partial file is too big, erase it
2898 if ((unsigned long long)Buf.st_size > Version->Size)
2899 RemoveFile("pkgAcqArchive::QueueNext", DestFile);
2900 else
2901 PartialSize = Buf.st_size;
2902 }
2903
2904 // Disables download of archives - useful if no real installation follows,
2905 // e.g. if we are just interested in proposed installation order
2906 if (_config->FindB("Debug::pkgAcqArchive::NoQueue", false) == true)
2907 {
2908 Complete = true;
2909 Local = true;
2910 Status = StatDone;
2911 StoreFilename = DestFile = FinalFile;
2912 return true;
2913 }
2914
2915 // Create the item
2916 Local = false;
2917 QueueURI(Desc);
2918
2919 ++Vf;
2920 return true;
2921 }
2922 return false;
2923 }
2924 /*}}}*/
2925 // AcqArchive::Done - Finished fetching /*{{{*/
2926 // ---------------------------------------------------------------------
2927 /* */
2928 void pkgAcqArchive::Done(string const &Message, HashStringList const &Hashes,
2929 pkgAcquire::MethodConfig const * const Cfg)
2930 {
2931 Item::Done(Message, Hashes, Cfg);
2932
2933 // Grab the output filename
2934 std::string const FileName = LookupTag(Message,"Filename");
2935 if (DestFile != FileName && RealFileExists(DestFile) == false)
2936 {
2937 StoreFilename = DestFile = FileName;
2938 Local = true;
2939 Complete = true;
2940 return;
2941 }
2942
2943 // Done, move it into position
2944 string const FinalFile = GetFinalFilename();
2945 Rename(DestFile,FinalFile);
2946 StoreFilename = DestFile = FinalFile;
2947 Complete = true;
2948 }
2949 /*}}}*/
2950 // AcqArchive::Failed - Failure handler /*{{{*/
2951 // ---------------------------------------------------------------------
2952 /* Here we try other sources */
2953 void pkgAcqArchive::Failed(string const &Message,pkgAcquire::MethodConfig const * const Cnf)
2954 {
2955 Item::Failed(Message,Cnf);
2956
2957 /* We don't really want to retry on failed media swaps, this prevents
2958 that. An interesting observation is that permanent failures are not
2959 recorded. */
2960 if (Cnf->Removable == true &&
2961 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2962 {
2963 // Vf = Version.FileList();
2964 while (Vf.end() == false) ++Vf;
2965 StoreFilename = string();
2966 return;
2967 }
2968
2969 Status = StatIdle;
2970 if (QueueNext() == false)
2971 {
2972 // This is the retry counter
2973 if (Retries != 0 &&
2974 Cnf->LocalOnly == false &&
2975 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
2976 {
2977 Retries--;
2978 Vf = Version.FileList();
2979 if (QueueNext() == true)
2980 return;
2981 }
2982
2983 StoreFilename = string();
2984 Status = StatError;
2985 }
2986 }
2987 /*}}}*/
2988 APT_PURE bool pkgAcqArchive::IsTrusted() const /*{{{*/
2989 {
2990 return Trusted;
2991 }
2992 /*}}}*/
2993 void pkgAcqArchive::Finished() /*{{{*/
2994 {
2995 if (Status == pkgAcquire::Item::StatDone &&
2996 Complete == true)
2997 return;
2998 StoreFilename = string();
2999 }
3000 /*}}}*/
3001 std::string pkgAcqArchive::DescURI() const /*{{{*/
3002 {
3003 return Desc.URI;
3004 }
3005 /*}}}*/
3006 std::string pkgAcqArchive::ShortDesc() const /*{{{*/
3007 {
3008 return Desc.ShortDesc;
3009 }
3010 /*}}}*/
3011 pkgAcqArchive::~pkgAcqArchive() {}
3012
3013 // AcqChangelog::pkgAcqChangelog - Constructors /*{{{*/
3014 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::VerIterator const &Ver,
3015 std::string const &DestDir, std::string const &DestFilename) :
3016 pkgAcquire::Item(Owner), d(NULL), SrcName(Ver.SourcePkgName()), SrcVersion(Ver.SourceVerStr())
3017 {
3018 Desc.URI = URI(Ver);
3019 Init(DestDir, DestFilename);
3020 }
3021 // some parameters are char* here as they come likely from char* interfaces – which can also return NULL
3022 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner, pkgCache::RlsFileIterator const &RlsFile,
3023 char const * const Component, char const * const SrcName, char const * const SrcVersion,
3024 const string &DestDir, const string &DestFilename) :
3025 pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion)
3026 {
3027 Desc.URI = URI(RlsFile, Component, SrcName, SrcVersion);
3028 Init(DestDir, DestFilename);
3029 }
3030 pkgAcqChangelog::pkgAcqChangelog(pkgAcquire * const Owner,
3031 std::string const &URI, char const * const SrcName, char const * const SrcVersion,
3032 const string &DestDir, const string &DestFilename) :
3033 pkgAcquire::Item(Owner), d(NULL), SrcName(SrcName), SrcVersion(SrcVersion)
3034 {
3035 Desc.URI = URI;
3036 Init(DestDir, DestFilename);
3037 }
3038 void pkgAcqChangelog::Init(std::string const &DestDir, std::string const &DestFilename)
3039 {
3040 if (Desc.URI.empty())
3041 {
3042 Status = StatError;
3043 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3044 strprintf(ErrorText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3045 // Let the error message print something sensible rather than "Failed to fetch /"
3046 if (DestFilename.empty())
3047 DestFile = SrcName + ".changelog";
3048 else
3049 DestFile = DestFilename;
3050 Desc.URI = "changelog:/" + DestFile;
3051 return;
3052 }
3053
3054 if (DestDir.empty())
3055 {
3056 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
3057 std::string const systemTemp = GetTempDir(SandboxUser);
3058 char tmpname[100];
3059 snprintf(tmpname, sizeof(tmpname), "%s/apt-changelog-XXXXXX", systemTemp.c_str());
3060 if (NULL == mkdtemp(tmpname))
3061 {
3062 _error->Errno("mkdtemp", "mkdtemp failed in changelog acquire of %s %s", SrcName.c_str(), SrcVersion.c_str());
3063 Status = StatError;
3064 return;
3065 }
3066 DestFile = TemporaryDirectory = tmpname;
3067
3068 ChangeOwnerAndPermissionOfFile("Item::QueueURI", DestFile.c_str(),
3069 SandboxUser.c_str(), "root", 0700);
3070 }
3071 else
3072 DestFile = DestDir;
3073
3074 if (DestFilename.empty())
3075 DestFile = flCombine(DestFile, SrcName + ".changelog");
3076 else
3077 DestFile = flCombine(DestFile, DestFilename);
3078
3079 Desc.ShortDesc = "Changelog";
3080 strprintf(Desc.Description, "%s %s %s Changelog", URI::SiteOnly(Desc.URI).c_str(), SrcName.c_str(), SrcVersion.c_str());
3081 Desc.Owner = this;
3082 QueueURI(Desc);
3083 }
3084 /*}}}*/
3085 std::string pkgAcqChangelog::URI(pkgCache::VerIterator const &Ver) /*{{{*/
3086 {
3087 char const * const SrcName = Ver.SourcePkgName();
3088 char const * const SrcVersion = Ver.SourceVerStr();
3089 pkgCache::PkgFileIterator PkgFile;
3090 // find the first source for this version which promises a changelog
3091 for (pkgCache::VerFileIterator VF = Ver.FileList(); VF.end() == false; ++VF)
3092 {
3093 pkgCache::PkgFileIterator const PF = VF.File();
3094 if (PF.Flagged(pkgCache::Flag::NotSource) || PF->Release == 0)
3095 continue;
3096 PkgFile = PF;
3097 pkgCache::RlsFileIterator const RF = PF.ReleaseFile();
3098 std::string const uri = URI(RF, PF.Component(), SrcName, SrcVersion);
3099 if (uri.empty())
3100 continue;
3101 return uri;
3102 }
3103 return "";
3104 }
3105 std::string pkgAcqChangelog::URITemplate(pkgCache::RlsFileIterator const &Rls)
3106 {
3107 if (Rls.end() == true || (Rls->Label == 0 && Rls->Origin == 0))
3108 return "";
3109 std::string const serverConfig = "Acquire::Changelogs::URI";
3110 std::string server;
3111 #define APT_EMPTY_SERVER \
3112 if (server.empty() == false) \
3113 { \
3114 if (server != "no") \
3115 return server; \
3116 return ""; \
3117 }
3118 #define APT_CHECK_SERVER(X, Y) \
3119 if (Rls->X != 0) \
3120 { \
3121 std::string const specialServerConfig = serverConfig + "::" + Y + #X + "::" + Rls.X(); \
3122 server = _config->Find(specialServerConfig); \
3123 APT_EMPTY_SERVER \
3124 }
3125 // this way e.g. Debian-Security can fallback to Debian
3126 APT_CHECK_SERVER(Label, "Override::")
3127 APT_CHECK_SERVER(Origin, "Override::")
3128
3129 if (RealFileExists(Rls.FileName()))
3130 {
3131 _error->PushToStack();
3132 FileFd rf;
3133 /* This can be costly. A caller wanting to get millions of URIs might
3134 want to do this on its own once and use Override settings.
3135 We don't do this here as Origin/Label are not as unique as they
3136 should be so this could produce request order-dependent anomalies */
3137 if (OpenMaybeClearSignedFile(Rls.FileName(), rf) == true)
3138 {
3139 pkgTagFile TagFile(&rf, rf.Size());
3140 pkgTagSection Section;
3141 if (TagFile.Step(Section) == true)
3142 server = Section.FindS("Changelogs");
3143 }
3144 _error->RevertToStack();
3145 APT_EMPTY_SERVER
3146 }
3147
3148 APT_CHECK_SERVER(Label, "")
3149 APT_CHECK_SERVER(Origin, "")
3150 #undef APT_CHECK_SERVER
3151 #undef APT_EMPTY_SERVER
3152 return "";
3153 }
3154 std::string pkgAcqChangelog::URI(pkgCache::RlsFileIterator const &Rls,
3155 char const * const Component, char const * const SrcName,
3156 char const * const SrcVersion)
3157 {
3158 return URI(URITemplate(Rls), Component, SrcName, SrcVersion);
3159 }
3160 std::string pkgAcqChangelog::URI(std::string const &Template,
3161 char const * const Component, char const * const SrcName,
3162 char const * const SrcVersion)
3163 {
3164 if (Template.find("CHANGEPATH") == std::string::npos)
3165 return "";
3166
3167 // the path is: COMPONENT/SRC/SRCNAME/SRCNAME_SRCVER, e.g. main/a/apt/1.1 or contrib/liba/libapt/2.0
3168 std::string Src = SrcName;
3169 std::string path = APT::String::Startswith(SrcName, "lib") ? Src.substr(0, 4) : Src.substr(0,1);
3170 path.append("/").append(Src).append("/");
3171 path.append(Src).append("_").append(StripEpoch(SrcVersion));
3172 // we omit component for releases without one (= flat-style repositories)
3173 if (Component != NULL && strlen(Component) != 0)
3174 path = std::string(Component) + "/" + path;
3175
3176 return SubstVar(Template, "CHANGEPATH", path);
3177 }
3178 /*}}}*/
3179 // AcqChangelog::Failed - Failure handler /*{{{*/
3180 void pkgAcqChangelog::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3181 {
3182 Item::Failed(Message,Cnf);
3183
3184 std::string errText;
3185 // TRANSLATOR: %s=%s is sourcename=sourceversion, e.g. apt=1.1
3186 strprintf(errText, _("Changelog unavailable for %s=%s"), SrcName.c_str(), SrcVersion.c_str());
3187
3188 // Error is probably something techy like 404 Not Found
3189 if (ErrorText.empty())
3190 ErrorText = errText;
3191 else
3192 ErrorText = errText + " (" + ErrorText + ")";
3193 return;
3194 }
3195 /*}}}*/
3196 // AcqChangelog::Done - Item downloaded OK /*{{{*/
3197 void pkgAcqChangelog::Done(string const &Message,HashStringList const &CalcHashes,
3198 pkgAcquire::MethodConfig const * const Cnf)
3199 {
3200 Item::Done(Message,CalcHashes,Cnf);
3201
3202 Complete = true;
3203 }
3204 /*}}}*/
3205 pkgAcqChangelog::~pkgAcqChangelog() /*{{{*/
3206 {
3207 if (TemporaryDirectory.empty() == false)
3208 {
3209 RemoveFile("~pkgAcqChangelog", DestFile);
3210 rmdir(TemporaryDirectory.c_str());
3211 }
3212 }
3213 /*}}}*/
3214
3215 // AcqFile::pkgAcqFile - Constructor /*{{{*/
3216 pkgAcqFile::pkgAcqFile(pkgAcquire * const Owner,string const &URI, HashStringList const &Hashes,
3217 unsigned long long const Size,string const &Dsc,string const &ShortDesc,
3218 const string &DestDir, const string &DestFilename,
3219 bool const IsIndexFile) :
3220 Item(Owner), d(NULL), IsIndexFile(IsIndexFile), ExpectedHashes(Hashes)
3221 {
3222 Retries = _config->FindI("Acquire::Retries",0);
3223
3224 if(!DestFilename.empty())
3225 DestFile = DestFilename;
3226 else if(!DestDir.empty())
3227 DestFile = DestDir + "/" + flNotDir(URI);
3228 else
3229 DestFile = flNotDir(URI);
3230
3231 // Create the item
3232 Desc.URI = URI;
3233 Desc.Description = Dsc;
3234 Desc.Owner = this;
3235
3236 // Set the short description to the archive component
3237 Desc.ShortDesc = ShortDesc;
3238
3239 // Get the transfer sizes
3240 FileSize = Size;
3241 struct stat Buf;
3242 if (stat(DestFile.c_str(),&Buf) == 0)
3243 {
3244 // Hmm, the partial file is too big, erase it
3245 if ((Size > 0) && (unsigned long long)Buf.st_size > Size)
3246 RemoveFile("pkgAcqFile", DestFile);
3247 else
3248 PartialSize = Buf.st_size;
3249 }
3250
3251 QueueURI(Desc);
3252 }
3253 /*}}}*/
3254 // AcqFile::Done - Item downloaded OK /*{{{*/
3255 void pkgAcqFile::Done(string const &Message,HashStringList const &CalcHashes,
3256 pkgAcquire::MethodConfig const * const Cnf)
3257 {
3258 Item::Done(Message,CalcHashes,Cnf);
3259
3260 std::string const FileName = LookupTag(Message,"Filename");
3261 Complete = true;
3262
3263 // The files timestamp matches
3264 if (StringToBool(LookupTag(Message,"IMS-Hit"),false) == true)
3265 return;
3266
3267 // We have to copy it into place
3268 if (RealFileExists(DestFile.c_str()) == false)
3269 {
3270 Local = true;
3271 if (_config->FindB("Acquire::Source-Symlinks",true) == false ||
3272 Cnf->Removable == true)
3273 {
3274 Desc.URI = "copy:" + FileName;
3275 QueueURI(Desc);
3276 return;
3277 }
3278
3279 // Erase the file if it is a symlink so we can overwrite it
3280 struct stat St;
3281 if (lstat(DestFile.c_str(),&St) == 0)
3282 {
3283 if (S_ISLNK(St.st_mode) != 0)
3284 RemoveFile("pkgAcqFile::Done", DestFile);
3285 }
3286
3287 // Symlink the file
3288 if (symlink(FileName.c_str(),DestFile.c_str()) != 0)
3289 {
3290 _error->PushToStack();
3291 _error->Errno("pkgAcqFile::Done", "Symlinking file %s failed", DestFile.c_str());
3292 std::stringstream msg;
3293 _error->DumpErrors(msg, GlobalError::DEBUG, false);
3294 _error->RevertToStack();
3295 ErrorText = msg.str();
3296 Status = StatError;
3297 Complete = false;
3298 }
3299 }
3300 }
3301 /*}}}*/
3302 // AcqFile::Failed - Failure handler /*{{{*/
3303 // ---------------------------------------------------------------------
3304 /* Here we try other sources */
3305 void pkgAcqFile::Failed(string const &Message, pkgAcquire::MethodConfig const * const Cnf)
3306 {
3307 Item::Failed(Message,Cnf);
3308
3309 // This is the retry counter
3310 if (Retries != 0 &&
3311 Cnf->LocalOnly == false &&
3312 StringToBool(LookupTag(Message,"Transient-Failure"),false) == true)
3313 {
3314 --Retries;
3315 QueueURI(Desc);
3316 Status = StatIdle;
3317 return;
3318 }
3319
3320 }
3321 /*}}}*/
3322 string pkgAcqFile::Custom600Headers() const /*{{{*/
3323 {
3324 if (IsIndexFile)
3325 return "\nIndex-File: true";
3326 return "";
3327 }
3328 /*}}}*/
3329 pkgAcqFile::~pkgAcqFile() {}