Package::addWebsiteJson()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Notimatica\Driver\Apns;
4
5
use League\Flysystem\Filesystem;
6
use Notimatica\Driver\Project;
7
8
class Package
9
{
10
    const PACKAGE_FILENAME = 'safari-package.zip';
11
12
    /**
13
     * @var array
14
     */
15
    protected $website;
16
17
    /**
18
     * @var array
19
     */
20
    protected $icons = [
21
        'icon_16x16.png',
22
        '[email protected]',
23
        'icon_32x32.png',
24
        '[email protected]',
25
        'icon_128x128.png',
26
        '[email protected]',
27
    ];
28
29
    /**
30
     * @var Certificate
31
     */
32
    protected $certificate;
33
34
    /**
35
     * @var array
36
     */
37
    protected $manifest;
38
39
    /**
40
     * @var \ZipArchive
41
     */
42
    protected $zip;
43
44
    /**
45
     * @var Filesystem
46
     */
47
    private $storage;
48
49
    /**
50
     * Create a new Package.
51
     *
52
     * @param array $website
53
     * @param Certificate $certificate
54
     * @param Filesystem $storage
55
     */
56
    public function __construct($website, Certificate $certificate, Filesystem $storage)
57
    {
58
        $this->website = $website;
59
        $this->certificate = $certificate;
60
        $this->storage = $storage;
61
    }
62
63
    /**
64
     * Generate zip package.
65
     */
66
    public function generate()
67
    {
68
        $packagePath = $this->storage->getAdapter()->applyPathPrefix(static::PACKAGE_FILENAME);
69
70
        if ($this->storage->has(static::PACKAGE_FILENAME)) {
71
            return $packagePath;
72
        }
73
74
        $this->zip = new \ZipArchive();
75
        if ($this->zip->open($packagePath, \ZipArchive::CREATE) !== true) {
76
            return false;
77
        }
78
79
        $this->addWebsiteJson();
80
        $this->addIcons();
81
        $this->addManifest();
82
        $this->addSignature();
83
84
        return $this->zip->close() ? $packagePath : false;
85
    }
86
87
    /**
88
     * Add website.json.
89
     */
90
    protected function addWebsiteJson()
91
    {
92
        $this->addString('website.json', json_encode($this->website));
93
    }
94
95
    /**
96
     * Add icons.
97
     */
98
    protected function addIcons()
99
    {
100
        foreach ($this->icons as $file) {
101
            $this->addFile('icon.iconset/' . $file, $file);
102
        }
103
    }
104
105
    /**
106
     * Add string to package.
107
     *
108
     * @param string $name
109
     * @param string $string
110
     */
111
    protected function addString($name, $string)
112
    {
113
        $this->manifest[$name] = sha1($string);
114
        $this->zip->addFromString($name, $string);
115
    }
116
117
    /**
118
     * Add file to package.
119
     *
120
     * @param string $name
121
     * @param string $path
122
     */
123
    protected function addFile($name, $path)
124
    {
125
        $path = $this->storage->getAdapter()->applyPathPrefix($path);
126
        $this->manifest[$name] = sha1_file($path);
127
        $this->zip->addFile($path, $name);
128
    }
129
130
    /**
131
     * Add manifest.json.
132
     */
133
    protected function addManifest()
134
    {
135
        $this->zip->addFromString(
136
            'manifest.json',
137
            json_encode($this->manifest)
138
        );
139
    }
140
141
    /**
142
     * Add signature.
143
     */
144
    protected function addSignature()
145
    {
146
        // Load the push notification certificate
147
        $pkcs12 = $this->certificate->getP12Certificate();
148
        $password = $this->certificate->getPassword();
149
150
        $certs = [];
151
        if (! openssl_pkcs12_read($pkcs12, $certs, $password)) {
152
            throw new \RuntimeException(openssl_error_string());
153
        }
154
155
        $signaturePath = tempnam(sys_get_temp_dir(), '_sign');
156
        $manifestPath = tempnam(sys_get_temp_dir(), '_manifest');
157
        file_put_contents($manifestPath, json_encode($this->manifest));
158
159
        // Sign the manifest.json file with the private key from the certificate
160
        $certificateData = openssl_x509_read($certs['cert']);
161
        $privateKey = openssl_pkey_get_private($certs['pkey'], $password);
162
        openssl_pkcs7_sign($manifestPath, $signaturePath, $certificateData, $privateKey, [], PKCS7_BINARY | PKCS7_DETACHED);
163
164
        // Convert the signature from PEM to DER
165
        $signature_pem = file_get_contents($signaturePath);
166
        $matches = [];
167
        if (! preg_match('~Content-Disposition:[^\n]+\s*?([A-Za-z0-9+=/\r\n]+)\s*?-----~', $signature_pem, $matches)) {
168
            throw new \RuntimeException(openssl_error_string());
169
        }
170
171
        $this->zip->addFromString('signature', base64_decode($matches[1]));
172
    }
173
}
174