Aleksander Machniak
2014-08-24 5f58127eae9ed8c54c190506e11af13e8ba57170
commit | author | age
b52166 1 <?php
TB 2
3 /*
4  +-----------------------------------------------------------------------+
5  | This file is part of the Roundcube Webmail client                     |
6  |                                                                       |
7  | Copyright (C) 2013, The Roundcube Dev Team                            |
8  |                                                                       |
9  | Licensed under the GNU General Public License version 3 or            |
10  | any later version with exceptions for skins & plugins.                |
11  | See the README file for a full license statement.                     |
12  |                                                                       |
13  | PURPOSE:                                                              |
14  |   Spellchecking backend implementation for afterthedeadline services  |
15  +-----------------------------------------------------------------------+
16  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
17  +-----------------------------------------------------------------------+
18 */
19
20 /**
21  * Spellchecking backend implementation to work with an After the Deadline service
22  * See http://www.afterthedeadline.com/ for more information
23  *
24  * @package    Framework
25  * @subpackage Utils
26  */
27 class rcube_spellcheck_atd extends rcube_spellcheck_engine
28 {
29     const SERVICE_HOST = 'service.afterthedeadline.com';
30     const SERVICE_PORT = 80;
31
32     private $matches = array();
33     private $content;
34     private $langhosts = array(
35         'fr' => 'fr.',
36         'de' => 'de.',
37         'pt' => 'pt.',
38         'es' => 'es.',
39     );
40
41     /**
c344b6 42      * Return a list of languages supported by this backend
TB 43      *
44      * @see rcube_spellcheck_engine::languages()
45      */
46     function languages()
47     {
48         $langs = array_values($this->langhosts);
49         $langs[] = 'en';
50         return $langs;
51     }
52
53     /**
b52166 54      * Set content and check spelling
TB 55      *
56      * @see rcube_spellcheck_engine::check()
57      */
58     function check($text)
59     {
60         $this->content = $text;
61
62         // spell check uri is configured
63         $rcube = rcube::get_instance();
64         $url = $rcube->config->get('spellcheck_uri');
65         $key = $rcube->config->get('spellcheck_atd_key');
66
67         if ($url) {
68             $a_uri = parse_url($url);
69             $ssl   = ($a_uri['scheme'] == 'https' || $a_uri['scheme'] == 'ssl');
70             $port  = $a_uri['port'] ? $a_uri['port'] : ($ssl ? 443 : 80);
71             $host  = ($ssl ? 'ssl://' : '') . $a_uri['host'];
72             $path  = $a_uri['path'] . ($a_uri['query'] ? '?'.$a_uri['query'] : '') . $this->lang;
73         }
74         else {
75             $host = self::SERVICE_HOST;
76             $port = self::SERVICE_PORT;
77             $path = '/checkDocument';
78
79             // prefix host for other languages than 'en'
80             $lang = substr($this->lang, 0, 2);
81             if ($this->langhosts[$lang])
82                 $host = $this->langhosts[$lang] . $host;
83         }
84
85         $postdata = 'data=' . urlencode($text);
86
87         if (!empty($key))
88             $postdata .= '&key=' . urlencode($key);
89
90         $response = $headers = '';
91         $in_header = true;
92         if ($fp = fsockopen($host, $port, $errno, $errstr, 30)) {
93             $out = "POST $path HTTP/1.0\r\n";
94             $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n";
95             $out .= "Content-Length: " . strlen($postdata) . "\r\n";
96             $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
97             $out .= "Connection: Close\r\n\r\n";
98             $out .= $postdata;
99             fwrite($fp, $out);
100
101             while (!feof($fp)) {
102                 if ($in_header) {
103                     $line = fgets($fp, 512);
104                     $headers .= $line;
105                     if (trim($line) == '')
106                         $in_header = false;
107                 }
108                 else {
109                     $response .= fgets($fp, 1024);
110                 }
111             }
112             fclose($fp);
113         }
114
115         // parse HTTP response headers
116         if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $headers, $m)) {
117             $http_status = $m[1];
118             if ($http_status != '200')
119                 $this->error = 'HTTP ' . $m[1] . $m[2];
120         }
121
122         if (!$response) {
123             $this->error = "Empty result from spelling engine";
124         }
125
126         try {
127             $result = new SimpleXMLElement($response);
128         }
129         catch (Exception $e) {
130             $thid->error = "Unexpected response from server: " . $store;
131             return array();
132         }
133
134         foreach ($result->error as $error) {
135             if (strval($error->type) == 'spelling') {
136                 $word = strval($error->string);
137
138                 // skip exceptions
139                 if ($this->dictionary->is_exception($word)) {
140                     continue;
141                 }
142
143                 $prefix = strval($error->precontext);
144                 $start = $prefix ? mb_strpos($text, $prefix) : 0;
145                 $pos = mb_strpos($text, $word, $start);
146                 $len = mb_strlen($word);
147                 $num = 0;
148
149                 $match = array($word, $pos, $len, null, array());
150                 foreach ($error->suggestions->option as $option) {
151                     $match[4][] = strval($option);
152                     if (++$num == self::MAX_SUGGESTIONS)
153                         break;
154                 }
155                 $matches[] = $match;
156             }
157         }
158
159         $this->matches = $matches;
160         return $matches;
161     }
162
163     /**
164      * Returns suggestions for the specified word
165      *
166      * @see rcube_spellcheck_engine::get_words()
167      */
168     function get_suggestions($word)
169     {
170         $matches = $word ? $this->check($word) : $this->matches;
171
172         if ($matches[0][4]) {
173             return $matches[0][4];
174         }
175
176         return array();
177     }
178
179     /**
180      * Returns misspelled words
181      *
182      * @see rcube_spellcheck_engine::get_suggestions()
183      */
184     function get_words($text = null)
185     {
186         if ($text) {
187             $matches = $this->check($text);
188         }
189         else {
190             $matches = $this->matches;
191             $text    = $this->content;
192         }
193
194         $result = array();
195
196         foreach ($matches as $m) {
197             $result[] = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET);
198         }
199
200         return $result;
201     }
202
203 }
204