| | Author: Thomas Bruederli | +-----------------------------------------------------------------------+ */ /** * Spellchecking backend implementation to work with a Googiespell service */ class rcube_spellchecker_googie extends rcube_spellchecker_engine { public const GOOGIE_HOST = 'https://spell.roundcube.net'; private $content; /** * Return a list of languages supported by this backend * * @see rcube_spellchecker_engine::languages() */ #[Override] public function languages() { return [ 'am', 'ar', 'ar', 'bg', 'br', 'ca', 'cs', 'cy', 'da', 'de_CH', 'de_DE', 'el', 'en_GB', 'en_US', 'eo', 'es', 'et', 'eu', 'fa', 'fi', 'fr_FR', 'ga', 'gl', 'gl', 'he', 'hr', 'hu', 'hy', 'is', 'it', 'ku', 'lt', 'lv', 'nl', 'pl', 'pt_BR', 'pt_PT', 'ro', 'ru', 'sk', 'sl', 'sv', 'uk', ]; } /** * Set content and check spelling * * @see rcube_spellchecker_engine::check() */ #[Override] public function check($text) { $this->content = $text; if (empty($text)) { return true; } $this->matches = $matches = []; $rcube = rcube::get_instance(); $client = $rcube->get_http_client(); // spell check uri is configured $url = $rcube->config->get('spellcheck_uri'); if (!$url) { $url = self::GOOGIE_HOST . '/tbproxy/spell?lang='; } $url .= $this->lang; $url .= sprintf('&key=%06d', !empty($_SESSION['user_id']) ? $_SESSION['user_id'] : 0); $gtext = '' . '' . '' . htmlspecialchars($text, \ENT_QUOTES, RCUBE_CHARSET) . '' . ''; try { $response = $client->post($url, [ 'connect_timeout' => 5, // seconds 'headers' => [ 'User-Agent' => 'Roundcube Webmail/' . RCUBE_VERSION . ' (Googiespell Wrapper)', 'Content-type' => 'text/xml', ], 'body' => $gtext, ] ); } catch (Exception $e) { // Do nothing, the error set below should be logged by the caller } if (empty($response)) { $this->error = isset($e) ? $e->getMessage() : 'Spelling engine failure'; } elseif ($response->getStatusCode() != 200) { $this->error = 'HTTP ' . $response->getReasonPhrase(); } else { $response_body = $response->getBody(); if (preg_match('/error = "Error code {$m[1]} returned"; $this->error .= preg_match('/([^<]+)/', $response_body, $m) ? ': ' . html_entity_decode($m[1]) : ''; } preg_match_all('/([^<]*)<\/c>/', $response_body, $matches, \PREG_SET_ORDER); // skip exceptions (if appropriate options are enabled) foreach ($matches as $idx => $match) { $word = mb_substr($text, $match[1], $match[2], RCUBE_CHARSET); // skip exceptions if ($this->dictionary->is_exception($word)) { unset($matches[$idx]); } } } $this->matches = $matches; return count($this->matches) == 0; } /** * Returns suggestions for the specified word * * @see rcube_spellchecker_engine::get_words() */ #[Override] public function get_suggestions($word) { $this->check($word); if (!empty($this->matches[0][4])) { $suggestions = explode("\t", $this->matches[0][4]); if (count($suggestions) > self::MAX_SUGGESTIONS) { $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); } return $suggestions; } return []; } /** * Returns misspelled words * * @see rcube_spellchecker_engine::get_suggestions() */ #[Override] public function get_words($text = null) { if ($text) { $this->check($text); } else { $text = $this->content; } $result = []; foreach ($this->matches as $m) { $result[] = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET); } return $result; } }