source: trunk/server/www/app/models/validator.php @ 348

Last change on this file since 348 was 348, checked in by sander, 11 years ago

Use auto configuration for ODFToolkit Validator

File size: 9.7 KB
Line 
1<?php
2/**
3 * Officeshots.org - Test your office documents in different applications
4 * Copyright (C) 2009 Stichting Lone Wolves
5 * Written by Sander Marechal <s.marechal@jejik.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/**
22 * The Validator model
23 *
24 * This model is an ODF Validator, not to be confused with the Validation class used for validating model data
25 */
26class Validator extends AppModel
27{
28        const STATE_PENDING     = 1;
29        const STATE_VALID       = 2;
30        const STATE_INVALID     = 3;
31        const STATE_ERROR       = 4;
32
33        /** @var array Model behaviors */
34        public $actsAs = array(
35                'Containable',
36                'BeanStalk.Deferrable' => array('priority' => 2048),
37        );
38
39        /**
40         * @var string The parent models.
41         * This is a bit of a hack. They both refer to the same field but because the ID's are UUID they will never collide
42         * and the right parent model should be picked automatically.
43         * */
44        public $belongsTo = array(
45                'Request' => array('foreignKey' => 'parent_id'),
46                'Result' => array('foreignKey' => 'parent_id'),
47        );
48
49        /**
50         * Run the validator. Requires $this->id to be set.
51         */
52        public function run()
53        {
54                $this->log('Validating ID: ' . $this->id, LOG_DEBUG);
55
56                if (!$this->id) {
57                        return;
58                }
59
60                $this->read();
61                if ($this->data['Request']['id']) {
62                        $this->Request->id = $this->data['Request']['id'];
63                        $file = $this->Request->getPath();
64                        $destination = $this->data['Request']['path'];
65                } else {
66                        $this->Result->id = $this->data['Result']['id'];
67                        $file = $this->Result->getPath();
68                        $destination = $this->data['Result']['path'];
69                }
70
71                $className = $this->data['Validator']['name'] . 'Validator';
72                if (!$this->data['Validator']['name'] || !class_exists($className)) {
73                        $this->log('Invalid ODF Validator: ' . $this->data['Validator']['name'] . 'Validator');
74                        return;
75                }
76
77                $validator = new $className();
78                $validator->run($file);
79
80                // Save the validator result to file
81                $destination = FILES . $destination . DS . strtolower($this->data['Validator']['name']) . '-validator.' . $validator->ext;
82                file_put_contents($destination, $validator->response);
83
84                // Save the validator result to the satabase
85                $this->data['Validator']['state']    = $validator->state;
86                $this->data['Validator']['response'] = $validator->response;
87                $this->save();
88        }
89}
90
91/**
92 * Validate an ODF document using the Cyclone3 stand-alone validator
93 */
94class CycloneValidator extends Object
95{
96        /** @var int The validator's state */
97        public $state = Validator::STATE_PENDING;
98
99        /** @var string The validator's full response */
100        public $response = false;
101
102        /** @var string Path to validator.pl */
103        private $bin = false;
104
105        /** @var string file extension for the response */
106        public $ext = 'txt';
107
108        /**
109         * Run the validator
110         * @var string $path Full path to the file to validate
111         */
112        public function run($path)
113        {
114                // Make sure the validator is configured
115                $this->bin = Configure::read('Validator.Cyclone');
116                if (!$this->bin || !is_executable($this->bin)) {
117                        $this->state = Validator::STATE_ERROR;
118                        $this->response = sprintf('Cyclone validator "%s" not configured correctly.', $this->bin);
119                        $this->log($this->response);
120                        return;
121                }
122
123                // Make sure the file exists
124                if (!is_readable($path)) {
125                        $this->state = Validator::STATE_ERROR;
126                        $this->response = sprintf('File %s cannot be read.', $file);
127                        $this->log($this->response);
128                        return;
129                }
130
131                // Run Cyclone validator
132                $cwd = getcwd();
133                chdir(dirname($this->bin));
134
135                $spec = array(
136                        0 => array('pipe', 'r'), // STDIN
137                        1 => array('pipe', 'w'), // STDOUT
138                        2 => array('pipe', 'w')  // STDERR
139                );
140
141                $proc = proc_open($this->bin . ' "' . $path . '"', $spec, $pipes);
142                if (!is_resource($proc)) {
143                        $this->state = Validator::STATE_ERROR;
144                        $this->response = sprintf('Cyclone validator "%s" failed to execute.', $this->bin);
145                        $this->log($this->response);
146                        return;
147                }
148
149                $stdout = stream_get_contents($pipes[1]);
150                $stderr = stream_get_contents($pipes[2]);
151                fclose($pipes[0]);
152                fclose($pipes[1]);
153                fclose($pipes[2]);
154                proc_close($proc);
155
156                chdir($cwd);
157
158                // Parse the results
159                if (strpos($stdout, 'Result: VALID') != false) {
160                        $this->state = Validator::STATE_VALID;
161                        $this->log(sprintf('Cyclone: Validated %s: Valid', $path), LOG_DEBUG);
162                } elseif (strpos($stdout, 'Result: INVALID') !== false) {
163                        $this->state = Validator::STATE_INVALID;
164                        $this->log(sprintf('Cyclone: Validated %s: Invalid', $path), LOG_DEBUG);
165                } else {
166                        $this->state = Validator::STATE_ERROR;
167                        $this->log(sprintf("Cyclone: Validated %s: Error\nSTDOUT: %s\nSTDERR: %s", $path, $stdout, $stderr));
168                }
169
170                $this->response = $stdout;
171        }
172}
173
174/**
175 * Validate an ODF document using the Office-o-tron online validator
176 */
177class OfficeotronValidator extends Object
178{
179        /** @var int The validator's state */
180        public $state = Validator::STATE_PENDING;
181
182        /** @var string The validator's full response */
183        public $response = false;
184
185        /** @var string file extension for the response */
186        public $ext = 'html';
187
188        /**
189         * Run the validator
190         * @var string $path Full path to the file to validate
191         */
192        public function run($path)
193        {
194                // Make sure the validator is configured
195                $endpoint = Configure::read('Validator.Officeotron');
196                if (!$endpoint) {
197                        $this->state = Validator::STATE_ERROR;
198                        $this->response = sprintf('Officeotron validator "%s" not configured correctly.', $endpoint);
199                        $this->log($this->response);
200                        return;
201                }
202
203                // Make sure the file exists
204                if (!is_readable($path)) {
205                        $this->state = Validator::STATE_ERROR;
206                        $this->response = sprintf('File %s cannot be read.', $file);
207                        $this->log($this->response);
208                        return;
209                }
210
211                // POST the file to Office-o-tron and get a response
212                $curl = curl_init();
213                curl_setopt($curl, CURLOPT_URL, $endpoint);
214                curl_setopt($curl, CURLOPT_POST, true);
215                curl_setopt($curl, CURLOPT_POSTFIELDS, file_get_contents($path));
216                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
217                curl_setopt($curl, CURLOPT_TIMEOUT, 120);
218                $this->response = curl_exec($curl);
219                curl_close($curl);
220
221                // No response, an error or a timeout
222                if ($this->response === false) {
223                        $this->state = Validator::STATE_ERROR;
224                        $this->response = sprintf('Officeotron: Could not get a response from %s. Error %d: %s', $endpoint, curl_errno(), curl_error());
225                        $this->log($this->response);
226                        return;
227                }
228
229                if (preg_match('/Grand total count of validity errors: (\d+)/', $this->response, $match)) {
230                        if ($match[1] == 0) {
231                                $this->state = Validator::STATE_VALID;
232                                $this->log(sprintf('Officeotron: Validated %s: Valid', $path), LOG_DEBUG);
233                        } else {
234                                $this->state = Validator::STATE_INVALID;
235                                $this->log(sprintf('Officeotron: Validated %s: Invalid', $path), LOG_DEBUG);
236                        }
237                } else {
238                        $this->state = Validator::STATE_ERROR;
239                        $this->log(sprintf("Officeotron: Validated %s: Error:\n%s", $path, $this->response));
240                }
241        }
242}
243
244/**
245 * A validator for Sun's ODFToolkit Validator.
246 * NOTE: A bit ugly. It screen-scrapes the public online validator
247 */
248class ODFToolkitValidator extends Object
249{
250        /** @var int The validator's state */
251        public $state = Validator::STATE_PENDING;
252
253        /** @var string The validator's full response */
254        public $response = false;
255
256        /** @var string file extension for the response */
257        public $ext = 'txt';
258
259        /**
260         * Run the validator
261         * @var string $path Full path to the file to validate
262         */
263        public function run($path)
264        {
265                // Make sure the validator is configured
266                $endpoint = Configure::read('Validator.ODFToolkit');
267                if (!$endpoint) {
268                        $this->state = Validator::STATE_ERROR;
269                        $this->response = sprintf('ODFToolkit validator "%s" not configured correctly.', $endpoint);
270                        $this->log($this->response);
271                        return;
272                }
273
274                // Make sure the file exists
275                if (!is_readable($path)) {
276                        $this->state = Validator::STATE_ERROR;
277                        $this->response = sprintf('File %s cannot be read.', $file);
278                        $this->log($this->response);
279                        return;
280                }
281
282                // POST the file to Office-o-tron and get a response
283                $curl = curl_init();
284                curl_setopt($curl, CURLOPT_URL, $endpoint);
285                curl_setopt($curl, CURLOPT_POST, true);
286                curl_setopt($curl, CURLOPT_POSTFIELDS, array(
287                        'config' => 'auto',
288                        'mode' => 0,
289                        'loglevel' => 1,
290                        'File1' => '@' . $path,
291                ));
292                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
293                curl_setopt($curl, CURLOPT_TIMEOUT, 120);
294                $this->response = curl_exec($curl);
295                curl_close($curl);
296
297                // No response, an error or a timeout
298                if ($this->response === false) {
299                        $this->state = Validator::STATE_ERROR;
300                        $this->response = sprintf('ODFToolkit: Could not get a response from %s. Error %d: %s', $endpoint, curl_errno(), curl_error());
301                        $this->log($this->response);
302                        return;
303                }
304
305                // Parse the results
306                if (strpos($this->response, 'This file is valid') != false) {
307                        $this->state = Validator::STATE_VALID;
308                        $this->response = 'This file is valid';
309                        $this->log(sprintf('ODFToolkit: Validated %s: Valid', $path), LOG_DEBUG);
310                } elseif (strpos($this->response, 'This file is NOT valid') !== false) {
311                        $this->state = Validator::STATE_INVALID;
312                        preg_match('#<pre>(.*)</pre>#s', $this->response, $match);
313                        if (isset($match[1])) {
314                                $this->response = $match[1];
315                        }
316                        $this->log(sprintf('ODFToolkit: Validated %s: Invalid', $path), LOG_DEBUG);
317                } else {
318                        $this->state = Validator::STATE_ERROR;
319                        $this->log(sprintf("ODFToolkit: Validated %s: Error\nResponse: %s", $path, $this->response));
320                }
321        }
322}
323
324?>
Note: See TracBrowser for help on using the repository browser.