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