| 
<?phpnamespace ParagonIE\Certainty;
 
 use ParagonIE\Certainty\Exception\BundleException;
 use ParagonIE\Certainty\Exception\EncodingException;
 use ParagonIE\Certainty\Exception\FilesystemException;
 
 /**
 * Class Fetch
 * @package ParagonIE\Certainty
 */
 class Fetch
 {
 const CHECK_SIGNATURE_BY_DEFAULT = false;
 const CHECK_CHRONICLE_BY_DEFAULT = false;
 
 /**
 * @var string $dataDirectory
 */
 protected $dataDirectory = '';
 
 /**
 * Fetch constructor.
 *
 * You almost certainly want to use RemoteFetch instead.
 *
 * @param string $dataDir Where the certificates and configuration lives
 */
 public function __construct($dataDir = '')
 {
 if (!empty($dataDir) && \is_readable($dataDir)) {
 $this->dataDirectory = $dataDir;
 } else {
 $this->dataDirectory = \dirname(__DIR__) . '/data';
 }
 }
 
 /**
 * Get the latest bundle. Checks the SHA256 hash of the file versus what
 * is expected. Optionally checks the Ed25519 signature.
 *
 * @param bool|null $checkEd25519Signature Enforce Ed25519 signatures?
 * @param bool|null $checkChronicle        Require cert bundles be stored
 *                                         inside a Chronicle instance?
 * @return Bundle
 * @throws BundleException
 */
 public function getLatestBundle($checkEd25519Signature = null, $checkChronicle = null)
 {
 if (\is_null($checkEd25519Signature)) {
 $checkEd25519Signature = (bool) static::CHECK_SIGNATURE_BY_DEFAULT;
 }
 if (\is_null($checkChronicle)) {
 $checkChronicle = (bool) static::CHECK_CHRONICLE_BY_DEFAULT;
 }
 
 /** @var Bundle $bundle */
 foreach ($this->listBundles() as $bundle) {
 if ($bundle->hasCustom()) {
 $validator = $bundle->getValidator();
 } else {
 $validator = new Validator();
 }
 
 // If the SHA256 doesn't match, fail fast.
 if ($validator::checkSha256Sum($bundle)) {
 /** @var bool $valid */
 $valid = true;
 if ($checkEd25519Signature) {
 $valid = $valid && $validator::checkEd25519Signature($bundle);
 }
 if ($checkChronicle) {
 $valid = $valid && $validator::checkChronicleHash($bundle);
 }
 if ($valid) {
 return $bundle;
 }
 }
 }
 throw new BundleException('No valid bundles were found in the data directory.');
 }
 
 /**
 * Get an array of all of the Bundles, ordered most-recent to oldest.
 *
 * No validation is performed automatically.
 *
 * @param string $customValidator Fully-qualified class name for Validator
 * @return array<int, Bundle>
 */
 public function getAllBundles($customValidator = '')
 {
 return \array_values($this->listBundles($customValidator));
 }
 
 /**
 * List bundles
 *
 * @param string $customValidator Fully-qualified class name for Validator
 * @return array<int, Bundle>
 * @throws \Exception
 */
 protected function listBundles($customValidator = '')
 {
 if (!\file_exists($this->dataDirectory . '/ca-certs.json')) {
 throw new FilesystemException('ca-certs.json not found in data directory.');
 }
 if (!\is_readable($this->dataDirectory . '/ca-certs.json')) {
 throw new FilesystemException('ca-certs.json is not readable.');
 }
 $contents = \file_get_contents($this->dataDirectory . '/ca-certs.json');
 if (!\is_string($contents)) {
 throw new FilesystemException('ca-certs.json could not be read.');
 }
 $data = \json_decode($contents, true);
 if (!\is_array($data)) {
 throw new EncodingException('ca-certs.json is not a valid JSON file.');
 }
 $bundles = [];
 foreach ($data as $row) {
 if (!isset($row['date'], $row['file'], $row['sha256'], $row['signature'])) {
 // The necessary keys are not defined.
 continue;
 }
 $key = (int) (\preg_replace('/[^0-9]/', '', $row['date']) . '0000');
 while (isset($bundles[$key])) {
 ++$key;
 }
 $bundles[$key] = new Bundle(
 $this->dataDirectory . '/' . $row['file'],
 $row['sha256'],
 $row['signature'],
 !empty($row['custom']) ? $row['custom'] : $customValidator,
 isset($row['chronicle']) ? $row['chronicle'] : ''
 );
 }
 \krsort($bundles);
 return $bundles;
 }
 }
 
 |