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

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

Fixed ODF-Tookit validator

File size: 9.6 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        /**
34         * @var string The parent models.
35         * 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
36         * and the right parent model should be picked automatically.
37         * */
38        public $belongsTo = array(
39                'Request' => array('foreignKey' => 'parent_id'),
40                'Result' => array('foreignKey' => 'parent_id'),
41        );
42
43        public $actsAs = array('Containable');
44
45        public function run()
46        {
47                $this->log('Validating ID: ' . $this->id, LOG_DEBUG);
48
49                if (!$this->id) {
50                        return;
51                }
52
53                $this->read();
54                if ($this->data['Request']['id']) {
55                        $this->Request->id = $this->data['Request']['id'];
56                        $file = $this->Request->getPath();
57                        $destination = $this->data['Request']['path'];
58                } else {
59                        $this->Result->id = $this->data['Result']['id'];
60                        $file = $this->Result->getPath();
61                        $destination = $this->data['Result']['path'];
62                }
63
64                $className = $this->data['Validator']['name'] . 'Validator';
65                if (!$this->data['Validator']['name'] || !class_exists($className)) {
66                        $this->log('Invalid ODF Validator: ' . $this->data['Validator']['name'] . 'Validator');
67                        return;
68                }
69
70                $validator = new $className();
71                $validator->run($file);
72
73                // Save the validator result to file
74                $destination = FILES . $destination . DS . strtolower($this->data['Validator']['name']) . '-validator.' . $validator->ext;
75                file_put_contents($destination, $validator->response);
76
77                // Save the validator result to the satabase
78                $this->data['Validator']['state']    = $validator->state;
79                $this->data['Validator']['response'] = $validator->response;
80                $this->save();
81        }
82}
83
84/**
85 * Validate an ODF document using the Cyclone3 stand-alone validator
86 */
87class CycloneValidator extends Object
88{
89        /** @var int The validator's state */
90        public $state = Validator::STATE_PENDING;
91
92        /** @var string The validator's full response */
93        public $response = false;
94
95        /** @var string Path to validator.pl */
96        private $bin = false;
97
98        /** @var string file extension for the response */
99        public $ext = 'txt';
100
101        /**
102         * Run the validator
103         * @var string $path Full path to the file to validate
104         */
105        public function run($path)
106        {
107                // Make sure the validator is configured
108                $this->bin = Configure::read('Validator.Cyclone');
109                if (!$this->bin || !is_executable($this->bin)) {
110                        $this->state = Validator::STATE_ERROR;
111                        $this->response = sprintf('Cyclone validator "%s" not configured correctly.', $this->bin);
112                        $this->log($this->response);
113                        return;
114                }
115
116                // Make sure the file exists
117                if (!is_readable($path)) {
118                        $this->state = Validator::STATE_ERROR;
119                        $this->response = sprintf('File %s cannot be read.', $file);
120                        $this->log($this->response);
121                        return;
122                }
123
124                // Run Cyclone validator
125                $cwd = getcwd();
126                chdir(dirname($this->bin));
127
128                $spec = array(
129                        0 => array('pipe', 'r'), // STDIN
130                        1 => array('pipe', 'w'), // STDOUT
131                        2 => array('pipe', 'w')  // STDERR
132                );
133
134                $proc = proc_open($this->bin . ' "' . $path . '"', $spec, $pipes);
135                if (!is_resource($proc)) {
136                        $this->state = Validator::STATE_ERROR;
137                        $this->response = sprintf('Cyclone validator "%s" failed to execute.', $this->bin);
138                        $this->log($this->response);
139                        return;
140                }
141
142                $stdout = stream_get_contents($pipes[1]);
143                $stderr = stream_get_contents($pipes[2]);
144                fclose($pipes[0]);
145                fclose($pipes[1]);
146                fclose($pipes[2]);
147                proc_close($proc);
148
149                chdir($cwd);
150
151                // Parse the results
152                if (strpos($stdout, 'Result: VALID') != false) {
153                        $this->state = Validator::STATE_VALID;
154                        $this->log(sprintf('Cyclone: Validated %s: Valid', $path), LOG_DEBUG);
155                } elseif (strpos($stdout, 'Result: INVALID') !== false) {
156                        $this->state = Validator::STATE_INVALID;
157                        $this->log(sprintf('Cyclone: Validated %s: Invalid', $path), LOG_DEBUG);
158                } else {
159                        $this->state = Validator::STATE_ERROR;
160                        $this->log(sprintf("Cyclone: Validated %s: Error\nSTDOUT: %s\nSTDERR: %s", $path, $stdout, $stderr));
161                }
162
163                $this->response = $stdout;
164        }
165}
166
167/**
168 * Validate an ODF document using the Office-o-tron online validator
169 */
170class OfficeotronValidator extends Object
171{
172        /** @var int The validator's state */
173        public $state = Validator::STATE_PENDING;
174
175        /** @var string The validator's full response */
176        public $response = false;
177
178        /** @var string file extension for the response */
179        public $ext = 'html';
180
181        /**
182         * Run the validator
183         * @var string $path Full path to the file to validate
184         */
185        public function run($path)
186        {
187                // Make sure the validator is configured
188                $endpoint = Configure::read('Validator.Officeotron');
189                if (!$endpoint) {
190                        $this->state = Validator::STATE_ERROR;
191                        $this->response = sprintf('Officeotron validator "%s" not configured correctly.', $endpoint);
192                        $this->log($this->response);
193                        return;
194                }
195
196                // Make sure the file exists
197                if (!is_readable($path)) {
198                        $this->state = Validator::STATE_ERROR;
199                        $this->response = sprintf('File %s cannot be read.', $file);
200                        $this->log($this->response);
201                        return;
202                }
203
204                // POST the file to Office-o-tron and get a response
205                $curl = curl_init();
206                curl_setopt($curl, CURLOPT_URL, $endpoint);
207                curl_setopt($curl, CURLOPT_POST, true);
208                curl_setopt($curl, CURLOPT_POSTFIELDS, file_get_contents($path));
209                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
210                curl_setopt($curl, CURLOPT_TIMEOUT, 30);
211                $this->response = curl_exec($curl);
212                curl_close($curl);
213
214                // No response, an error or a timeout
215                if ($this->response === false) {
216                        $this->state = Validator::STATE_ERROR;
217                        $this->response = sprintf('Officeotron: Could not get a response from %s. Error %d: %s', $endpoint, curl_errno(), curl_error());
218                        $this->log($this->response);
219                        return;
220                }
221
222                if (preg_match('/Grand total count of validity errors: (\d+)/', $this->response, $match)) {
223                        if ($match[1] == 0) {
224                                $this->state = Validator::STATE_VALID;
225                                $this->log(sprintf('Officeotron: Validated %s: Valid', $path), LOG_DEBUG);
226                        } else {
227                                $this->state = Validator::STATE_INVALID;
228                                $this->log(sprintf('Officeotron: Validated %s: Invalid', $path), LOG_DEBUG);
229                        }
230                } else {
231                        $this->state = Validator::STATE_ERROR;
232                        $this->log(sprintf("Officeotron: Validated %s: Error:\n%s", $path, $this->response));
233                }
234        }
235}
236
237/**
238 * A validator for Sun's ODFToolkit Validator.
239 * NOTE: A bit ugly. It screen-scrapes the public online validator
240 */
241class ODFToolkitValidator extends Object
242{
243        /** @var int The validator's state */
244        public $state = Validator::STATE_PENDING;
245
246        /** @var string The validator's full response */
247        public $response = false;
248
249        /** @var string file extension for the response */
250        public $ext = 'txt';
251
252        /**
253         * Run the validator
254         * @var string $path Full path to the file to validate
255         */
256        public function run($path)
257        {
258                // Make sure the validator is configured
259                $endpoint = Configure::read('Validator.ODFToolkit');
260                if (!$endpoint) {
261                        $this->state = Validator::STATE_ERROR;
262                        $this->response = sprintf('ODFToolkit validator "%s" not configured correctly.', $endpoint);
263                        $this->log($this->response);
264                        return;
265                }
266
267                // Make sure the file exists
268                if (!is_readable($path)) {
269                        $this->state = Validator::STATE_ERROR;
270                        $this->response = sprintf('File %s cannot be read.', $file);
271                        $this->log($this->response);
272                        return;
273                }
274
275                // POST the file to Office-o-tron and get a response
276                $curl = curl_init();
277                curl_setopt($curl, CURLOPT_URL, $endpoint);
278                curl_setopt($curl, CURLOPT_POST, true);
279                curl_setopt($curl, CURLOPT_POSTFIELDS, array(
280                        'config' => '1.2',
281                        'mode' => 0,
282                        'loglevel' => 1,
283                        'File1' => '@' . $path,
284                ));
285                curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
286                curl_setopt($curl, CURLOPT_TIMEOUT, 30);
287                $this->response = curl_exec($curl);
288                curl_close($curl);
289
290                // No response, an error or a timeout
291                if ($this->response === false) {
292                        $this->state = Validator::STATE_ERROR;
293                        $this->response = sprintf('ODFToolkit: Could not get a response from %s. Error %d: %s', $endpoint, curl_errno(), curl_error());
294                        $this->log($this->response);
295                        return;
296                }
297
298                // Parse the results
299                if (strpos($this->response, 'This file is valid') != false) {
300                        $this->state = Validator::STATE_VALID;
301                        $this->response = 'This file is valid';
302                        $this->log(sprintf('ODFToolkit: Validated %s: Valid', $path), LOG_DEBUG);
303                } elseif (strpos($this->response, 'This file is NOT valid') !== false) {
304                        $this->state = Validator::STATE_INVALID;
305                        preg_match('#<pre>(.*)</pre>#s', $this->response, $match);
306                        if (isset($match[1])) {
307                                $this->response = $match[1];
308                        }
309                        $this->log(sprintf('ODFToolkit: Validated %s: Invalid', $path), LOG_DEBUG);
310                } else {
311                        $this->state = Validator::STATE_ERROR;
312                        $this->log(sprintf("ODFToolkit: Validated %s: Error\nResponse: %s", $path, $this->response));
313                }
314        }
315}
316
317?>
Note: See TracBrowser for help on using the repository browser.