/** * Creates a pkpass file * * @param Passbook\PassInterface $pass * @throws FileException If an IO error occurred * @return SplFileObject */ public function package(PassInterface $pass) { $pass->setPassTypeIdentifier($this->passTypeIdentifier); $pass->setTeamIdentifier($this->teamIdentifier); $pass->setOrganizationName($this->organizationName); // Serialize pass $json = self::serialize($pass); $outputPath = rtrim($this->getOutputPath(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; $passDir = $outputPath . $pass->getSerialNumber() . DIRECTORY_SEPARATOR; $passDirExists = file_exists($passDir); if ($passDirExists && !$this->isOverwrite()) { throw new FileException("Temporary pass directory already exists"); } elseif (!$passDirExists && !mkdir($passDir, 0777, true)) { throw new FileException("Couldn't create temporary pass directory"); } // Pass.json $passJSONFile = $passDir . 'pass.json'; file_put_contents($passJSONFile, $json); // Images /** @var \Passbook\Pass\Image $image */ foreach ($pass->getImages() as $image) { $fileName = $passDir . $image->getContext(); if ($image->isHighRetina()) { $fileName .= '@3x'; } else { if ($image->isRetina()) { $fileName .= '@2x'; } } $fileName .= '.' . $image->getExtension(); copy($image->getPathname(), $fileName); } // Localizations foreach ($pass->getLocalizations() as $localization) { // Create dir (LANGUAGE.lproj) $localizationDir = $passDir . $localization->getLanguage() . '.lproj' . DIRECTORY_SEPARATOR; mkdir($localizationDir, 0777, true); // pass.strings File (Format: "token" = "value") $localizationStringsFile = $localizationDir . 'pass.strings'; file_put_contents($localizationStringsFile, $localization->getStringsFileOutput()); // Localization images foreach ($localization->getImages() as $image) { $fileName = $localizationDir . $image->getContext(); if ($image->isHighRetina()) { $fileName .= '@3x'; } else { if ($image->isRetina()) { $fileName .= '@2x'; } } $fileName .= '.' . $image->getExtension(); copy($image->getPathname(), $fileName); } } // Manifest.json - recursove, also add files in sub directories $manifestJSONFile = $passDir . 'manifest.json'; $manifest = array(); $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($passDir), RecursiveIteratorIterator::SELF_FIRST); foreach ($files as $file) { // Ignore "." and ".." folders if (in_array(substr($file, strrpos($file, '/') + 1), array('.', '..'))) { continue; } // $filepath = realpath($file); if (is_file($filepath) === true) { $relativePathName = str_replace($passDir, '', $file->getPathname()); $manifest[$relativePathName] = sha1_file($filepath); } } file_put_contents($manifestJSONFile, json_encode($manifest, JSON_UNESCAPED_SLASHES)); // Signature $signatureFile = $passDir . 'signature'; $p12 = file_get_contents($this->p12->getRealPath()); $certs = array(); if (openssl_pkcs12_read($p12, $certs, $this->p12->getPassword()) == true) { $certdata = openssl_x509_read($certs['cert']); $privkey = openssl_pkey_get_private($certs['pkey'], $this->p12->getPassword()); openssl_pkcs7_sign($manifestJSONFile, $signatureFile, $certdata, $privkey, array(), PKCS7_BINARY | PKCS7_DETACHED, $this->wwdr->getRealPath()); // Get signature content $signature = @file_get_contents($signatureFile); // Check signature content if (!$signature) { throw new FileException("Couldn't read signature file."); } // Delimeters $begin = 'filename="smime.p7s"'; $end = '------'; // Convert signature $signature = substr($signature, strpos($signature, $begin) + strlen($begin)); $signature = substr($signature, 0, strpos($signature, $end)); $signature = base64_decode($signature); // Put new signature if (!file_put_contents($signatureFile, $signature)) { throw new FileException("Couldn't write signature file."); } } else { throw new FileException("Error reading certificate file"); } // Zip pass $zipFile = $outputPath . $pass->getSerialNumber() . self::PASS_EXTENSION; $this->zip($passDir, $zipFile); // Remove temporary pass directory $this->rrmdir($passDir); return new SplFileObject($zipFile); }
/** * Creates a pkpass file * * @param Passbook\PassInterface $pass * @throws FileException If an IO error occurred * @return SplFileObject */ public function package(PassInterface $pass) { $pass->setPassTypeIdentifier($this->passTypeIdentifier); $pass->setTeamIdentifier($this->teamIdentifier); $pass->setOrganizationName($this->organizationName); // Serialize pass $json = self::serialize($pass); $outputPath = rtrim($this->getOutputPath(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; $passDir = $outputPath . $pass->getSerialNumber() . DIRECTORY_SEPARATOR; $passDirExists = file_exists($passDir); if ($passDirExists && !$this->isOverwrite()) { throw new FileException("Temporary pass directory already exists"); } elseif (!$passDirExists && !mkdir($passDir, 0777, true)) { throw new FileException("Couldn't create temporary pass directory"); } // Pass.json $passJSONFile = $passDir . 'pass.json'; file_put_contents($passJSONFile, $json); // Images foreach ($pass->getImages() as $image) { $fileName = $passDir . $image->getContext(); if ($image->isRetina()) { $fileName .= '@2x'; } $fileName .= '.' . $image->getExtension(); copy($image->getPathname(), $fileName); } // Manifest.json $manifestJSONFile = $passDir . 'manifest.json'; $manifest = array(); foreach (scandir($passDir) as $file) { if ($file == '.' or $file == '..') { continue; } $manifest[$file] = sha1_file($passDir . $file); } file_put_contents($manifestJSONFile, json_encode($manifest)); // Signature $signatureFile = $passDir . 'signature'; $p12 = file_get_contents($this->p12->getRealPath()); $certs = array(); if (openssl_pkcs12_read($p12, $certs, $this->p12->getPassword()) == true) { $certdata = openssl_x509_read($certs['cert']); $privkey = openssl_pkey_get_private($certs['pkey'], $this->p12->getPassword()); openssl_pkcs7_sign($manifestJSONFile, $signatureFile, $certdata, $privkey, array(), PKCS7_BINARY | PKCS7_DETACHED, $this->wwdr->getRealPath()); // Get signature content $signature = @file_get_contents($signatureFile); // Check signature content if (!$signature) { throw new FileException("Couldn't read signature file."); } // Delimeters $begin = 'filename="smime.p7s"'; $end = '------'; // Convert signature $signature = substr($signature, strpos($signature, $begin) + strlen($begin)); $signature = substr($signature, 0, strpos($signature, $end)); $signature = base64_decode($signature); // Put new signature if (!file_put_contents($signatureFile, $signature)) { throw new FileException("Couldn't write signature file."); } } else { throw new FileException("Error reading certificate file"); } // Zip pass $zipFile = $outputPath . $pass->getSerialNumber() . self::PASS_EXTENSION; $zip = new ZipArchive(); if (!$zip->open($zipFile, $this->isOverwrite() ? ZIPARCHIVE::OVERWRITE : ZipArchive::CREATE)) { throw new FileException("Couldn't open zip file."); } if ($handle = opendir($passDir)) { while (false !== ($entry = readdir($handle))) { if ($entry == '.' or $entry == '..') { continue; } $zip->addFile($passDir . $entry, $entry); } closedir($handle); } else { throw new FileException("Error reading pass directory"); } $zip->close(); // Remove temporary pass directory $this->rrmdir($passDir); return new SplFileObject($zipFile); }