Completed
Push — master ( 5f5fca...2fa1e0 )
by Bjørn
12:28 queued 02:24
created

Wpc::getOptionDefinitionsExtra()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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