Passed
Push — master ( 1ada49...6f88dc )
by Bjørn
02:29
created

Wpc::getOptionDefinitionsExtra()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace WebPConvert\Convert\Converters;
4
5
use WebPConvert\Convert\BaseConverters\AbstractCloudCurlConverter;
6
use WebPConvert\Convert\Exceptions\ConversionFailedException;
7
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
8
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
9
10
class Wpc extends AbstractCloudCurlConverter
11
{
12
    protected function getOptionDefinitionsExtra()
13
    {
14
        return [
15
            ['api-version', 'number', 0],                     /* Can currently be 0 or 1 */
16
            ['secret', 'string', 'my dog is white', true],    /* only in api v.0 */
17
            ['api-key', 'string', 'my dog is white', true],   /* new in api v.1 (renamed 'secret' to 'api-key') */
18
            ['url', 'string', '', true, true],
19
            ['crypt-api-key-in-transfer', 'boolean', false],  /* new in api v.1 */
20
        ];
21
    }
22
23
    private static function createRandomSaltForBlowfish()
24
    {
25
        $salt = '';
26
        $validCharsForSalt = array_merge(
27
            range('A', 'Z'),
28
            range('a', 'z'),
29
            range('0', '9'),
30
            ['.', '/']
31
        );
32
33
        for ($i=0; $i<22; $i++) {
34
            $salt .= $validCharsForSalt[array_rand($validCharsForSalt)];
35
        }
36
        return $salt;
37
    }
38
39
    protected function doActualConvert()
40
    {
41
        $options = $this->options;
42
43
        $apiVersion = $options['api-version'];
44
45
        if (!function_exists('curl_file_create')) {
46
            throw new SystemRequirementsNotMetException(
47
                'Required curl_file_create() PHP function is not available (requires PHP > 5.5).'
48
            );
49
        }
50
51
        if ($apiVersion == 0) {
52
            if (!empty($options['secret'])) {
53
                // if secret is set, we need md5() and md5_file() functions
54
                if (!function_exists('md5')) {
55
                    throw new ConverterNotOperationalException(
56
                        'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
57
                        'contents. ' .
58
                        'But the required md5() PHP function is not available.'
59
                    );
60
                }
61
                if (!function_exists('md5_file')) {
62
                    throw new ConverterNotOperationalException(
63
                        'A secret has been set, which requires us to create a md5 hash from the secret and the file ' .
64
                        'contents. But the required md5_file() PHP function is not available.'
65
                    );
66
                }
67
            }
68
        }
69
70
        if ($apiVersion == 1) {
71
        /*
72
                if (count($options['web-services']) == 0) {
73
                    throw new SystemRequirementsNotMetException('No remote host has been set up');
74
                }*/
75
        }
76
77
        if ($options['url'] == '') {
78
            throw new ConverterNotOperationalException(
79
                'Missing URL. You must install Webp Convert Cloud Service on a server, ' .
80
                'or the WebP Express plugin for Wordpress - and supply the url.'
81
            );
82
        }
83
84
        // Got some code here:
85
        // https://coderwall.com/p/v4ps1a/send-a-file-via-post-with-curl-and-php
86
87
        $ch = self::initCurl();
88
89
        $optionsToSend = $options;
90
91
        if ($this->isQualityDetectionRequiredButFailing()) {
92
            // quality was set to "auto", but we could not meassure the quality of the jpeg locally
93
            // Ask the cloud service to do it, rather than using what we came up with.
94
            $optionsToSend['quality'] = 'auto';
95
        } else {
96
            $optionsToSend['quality'] = $this->getCalculatedQuality();
97
        }
98
99
        unset($optionsToSend['converters']);
100
        unset($optionsToSend['secret']);
101
102
        $postData = [
103
            'file' => curl_file_create($this->source),
104
            'options' => json_encode($optionsToSend),
105
            'servername' => (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '')
106
        ];
107
108
        if ($apiVersion == 0) {
109
            $postData['hash'] = md5(md5_file($this->source) . $options['secret']);
110
        }
111
112
        if ($apiVersion == 1) {
113
            $apiKey = $options['api-key'];
114
115
            if ($options['crypt-api-key-in-transfer']) {
116
                if (CRYPT_BLOWFISH == 1) {
117
                    $salt = self::createRandomSaltForBlowfish();
118
                    $postData['salt'] = $salt;
119
120
                    // Strip off the first 28 characters (the first 6 are always "$2y$10$". The next 22 is the salt)
121
                    $postData['api-key-crypted'] = substr(crypt($apiKey, '$2y$10$' . $salt . '$'), 28);
122
                } else {
123
                    if (!function_exists('crypt')) {
124
                        throw new ConverterNotOperationalException(
125
                            'Configured to crypt the api-key, but crypt() function is not available.'
126
                        );
127
                    } else {
128
                        throw new ConverterNotOperationalException(
129
                            'Configured to crypt the api-key. ' .
130
                            'That requires Blowfish encryption, which is not available on your current setup.'
131
                        );
132
                    }
133
                }
134
            } else {
135
                $postData['api-key'] = $apiKey;
136
            }
137
        }
138
139
140
        // Try one host at the time
141
        // TODO: shuffle the array first
142
        /*
143
        foreach ($options['web-services'] as $webService) {
144
145
        }
146
        */
147
148
149
        curl_setopt_array($ch, [
150
            CURLOPT_URL => $options['url'],
151
            CURLOPT_POST => 1,
152
            CURLOPT_POSTFIELDS => $postData,
153
            CURLOPT_BINARYTRANSFER => true,
154
            CURLOPT_RETURNTRANSFER => true,
155
            CURLOPT_HEADER => false,
156
            CURLOPT_SSL_VERIFYPEER => false
157
        ]);
158
159
        $response = curl_exec($ch);
160
        if (curl_errno($ch)) {
161
            throw new ConverterNotOperationalException('Curl error:' . curl_error($ch));
162
        }
163
164
        // Check if we got a 404
165
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
166
        if ($httpCode == 404) {
167
            curl_close($ch);
168
            throw new ConversionFailedException(
169
                'WPC was not found at the specified URL - we got a 404 response.'
170
            );
171
        }
172
173
        // The WPC cloud service either returns an image or an error message
174
        // Images has application/octet-stream.
175
        // Verify that we got an image back.
176
        if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
177
            curl_close($ch);
178
179
            if (substr($response, 0, 1) == '{') {
180
                $responseObj = json_decode($response, true);
181
                if (isset($responseObj['errorCode'])) {
182
                    switch ($responseObj['errorCode']) {
183
                        case 0:
184
                            throw new ConverterNotOperationalException(
185
                                'There are problems with the server setup: "' .
186
                                $responseObj['errorMessage'] . '"'
187
                            );
188
                        case 1:
189
                            throw new ConverterNotOperationalException(
190
                                'Access denied. ' . $responseObj['errorMessage']
191
                            );
192
                        default:
193
                            throw new ConversionFailedException(
194
                                'Conversion failed: "' . $responseObj['errorMessage'] . '"'
195
                            );
196
                    }
197
                }
198
            }
199
200
            // WPC 0.1 returns 'failed![error messag]' when conversion fails. Handle that.
201
            if (substr($response, 0, 7) == 'failed!') {
202
                throw new ConversionFailedException(
203
                    'WPC failed converting image: "' . substr($response, 7) . '"'
204
                );
205
            }
206
207
            if (empty($response)) {
208
                $errorMsg = 'Error: Unexpected result. We got nothing back. HTTP CODE: ' . $httpCode;
209
                throw new ConversionFailedException($errorMsg);
210
            } else {
211
                $errorMsg = 'Error: Unexpected result. We did not receive an image. We received: "';
212
                $errorMsg .= str_replace("\r", '', str_replace("\n", '', htmlentities(substr($response, 0, 400))));
213
                throw new ConversionFailedException($errorMsg . '..."');
214
            }
215
            //throw new ConverterNotOperationalException($response);
216
        }
217
218
        $success = @file_put_contents($this->destination, $response);
219
        curl_close($ch);
220
221
        if (!$success) {
222
            throw new ConversionFailedException('Error saving file. Check file permissions');
223
        }
224
        /*
225
                $curlOptions = [
226
                    'api_key' => $options['key'],
227
                    'webp' => '1',
228
                    'file' => curl_file_create($this->source),
229
                    'domain' => $_SERVER['HTTP_HOST'],
230
                    'quality' => $options['quality'],
231
                    'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
232
                ];
233
234
                curl_setopt_array($ch, [
235
                    CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
236
                    CURLOPT_HTTPHEADER => [
237
                        'User-Agent: WebPConvert',
238
                        'Accept: image/*'
239
                    ],
240
                    CURLOPT_POSTFIELDS => $curlOptions,
241
                    CURLOPT_BINARYTRANSFER => true,
242
                    CURLOPT_RETURNTRANSFER => true,
243
                    CURLOPT_HEADER => false,
244
                    CURLOPT_SSL_VERIFYPEER => false
245
                ]);*/
246
    }
247
}
248