You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
web/src/Service/FDroidRepoService.php

164 lines
5.1 KiB
PHP

<?php
namespace CubiStore\Web\Service;
use CubiStore\Web\Model\App;
use DI\Container;
use Doctrine\ORM\EntityManager;
use ZipArchive;
class FDroidRepoService
{
/**
* @var Container
*/
private $container;
/**
* FDroidRepoService constructor.
* @param Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
}
public function createIndexV1Array()
{
/** @var EntityManager $em */
$em = $this->container->get(EntityManager::class);
$apps = $em
->getRepository(App::class)
->findBy([
'status' => App::STATUS_APPROVED
]);
$appObjects = [];
$packageObjects = [];
foreach ($apps as $app) {
/** @var $app App */
$appObjects[] = [
"authorEmail" => "example@example.com",
"authorName" => "example",
"authorWebSite" => "https://example.org",
"categories" => [
'example'
],
"suggestedVersionCode" => $app->getReleases()->first()->getVersionCode(),
"name" => $app->getLabel(),
"added" => 0,
"packageName" => $app->getName(),
"lastUpdated" => 0,
"license" => "no"
];
$packages = [];
foreach ($app->getReleases() as $release) {
$packages[] = [
"added" => 0,
"apkName" => "/apk/" . $app->getName() . "/" . $release->getVersionCode() . ".apk",
"hash" => hash('sha256', $release->getApk()),
"hashType" => "sha256",
"minSdkVersion" => 4,
"packageName" => $app->getName(),
"sig" => "deadbeef",
"signer" => "deadbeef",
"size" => filesize($release->getApk()),
"targetSdkVersion" => 28,
"versionCode" => $release->getVersionCode(),
"versionName" => $release->getVersionName()
];
}
$packageObjects[$app->getName()] = $packages;
}
return [
'repo' => [
'timestamp' => time() * 1000,
'version' => 21,
'maxage' => 14,
'name' => $this->container->get('app.name'),
'description' => $this->container->get('app.description'),
'address' => "https://" . $this->container->get('app.domain') . '/repo'
],
'requests' => [
'install' => [],
'uninstall' => [],
],
"apps" => $appObjects,
"packages" => $packageObjects
];
}
public function createSigned($file, $contents)
{
$zip = new ZipArchive();
$zipPath = tempnam(sys_get_temp_dir(), 'zip');
$zip->open($zipPath, ZipArchive::CREATE);
$fileDigest = sha1($contents, true);
$fileHeader = 'Name: ' . $file . "\n";
$fileHeader .= 'SHA1-Digest: ' . base64_encode($fileDigest) . "\n\n";
$fileHeaderDigest = sha1($fileHeader, true);
$manifest = "Manifest-Version: 1.0\n";
$manifest .= "Created-By: CubiStore\n\n";
$manifestHeaderDigest = sha1($manifest, true);
$manifest .= $fileHeader;
$manifestDigest = sha1($manifest, true);
$fileManifest = "Signature-Version: 1.0\n";
$fileManifest .= "SHA1-Digest-Manifest-Main-Attributes: " . base64_encode($manifestHeaderDigest) . "\n";
$fileManifest .= "SHA1-Digest-Manifest: " . base64_encode($manifestDigest) . "\n";
$fileManifest .= "Created-By: CubiStore\n\n";
$fileManifest .= "Name: " . $file . "\n";
$fileManifest .= "SHA1-Digest: " . base64_encode($fileHeaderDigest) . "\n\n";
$zip->addFromString('META-INF/MANIFEST.MF', $manifest);
$zip->addFromString('META-INF/1.SF', $fileManifest);
$in = tempnam(sys_get_temp_dir(), 'repo');
file_put_contents($in, $fileManifest);
$out = tempnam(sys_get_temp_dir(), 'repo');
$success = openssl_pkcs7_sign(
$in,
$out,
'file://' . __DIR__ . '/../../certs/cert.pem',
'file://' . __DIR__ . '/../../certs/key.pem',
[],
PKCS7_BINARY | PKCS7_NOATTR
);
if (!$success) {
$error = '';
while ($line = openssl_error_string()) {
$error .= $line . "\n";
}
throw new \RuntimeException($error);
}
unlink($in);
$signed = file_get_contents($out);
unlink($out);
$contentOffset = strpos($signed, "\n\n");
$content = substr($signed, $contentOffset);
$base64 = str_replace("\n", "", $content);
$zip->addFromString('META-INF/1.RSA', base64_decode($base64));
$zip->addFromString($file, $contents);
$zip->close();
$zipContent = file_get_contents($zipPath);
unlink($zipPath);
return $zipContent;
}
}