* Copyright 1999-2004 Jon Parise * * See the enclosed file COPYING for license information (GPL). If you * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. */ define('IMP_SPELL_CHANGE', 1); define('IMP_SPELL_CHANGE_ALL', 2); define('IMP_SPELL_IGNORE', 3); define('IMP_SPELL_IGNORE_ALL', 4); /* Base list of words to ignore. */ $ignore_list = array( 'com', 'cc', 'www', 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec', 'fwd', 'dns', 'http', 'ca', 'html', 'tm', 'mmunity', 'co', 'op', 'https', 'netscape', 'webmail', 'bcc', 'jpg', 'gif', 'email', 'tel', 'ie', 'eg' ); /** * Find the offset of a given word. * * @param string $message The text of the message. * @param string $word The word to find. * @param integer $start The offset. * * @return integer The offset of the word. */ function _findOffset($message, $word, $start) { $offset = $start; $pos = -1; /* If all things are right the word is at offset - 1 */ if ($start > 3) { $start -= 3; } else { $start = 0; } while (($pos == -1) && ($start >= 0)) { $pos = strpos($message, $word, $start); if (($pos == '') && !is_int($pos)) { $start--; $pos = -1; } } return $pos; } /** * Highlight in error in a given message. * * @param string $error The misspelled word. * @param string $message The text of the message. * @param integer $start The offset. * * @return string The string with the error highlighted. */ function _highlightError($error, $message, $offset) { $pos = strpos($message, $error, ((($offset - 1) > 0) ? ($offset - 1) : 0)); if (($pos - 15) > 0) { $start = $pos - 15; } else { $start = 0; } $length = String::length($error) + 30; $message = substr_replace($message, "$error", $pos, String::length($error)); $message = String::substr($message, $start, $length + 28); return $message; } /* Fetch and clean form data. */ $f_opt = Util::getFormData('opt'); $f_subs = Util::getFormData('subs'); $f_oldword = Util::getFormData('oldword'); $f_subtext = Util::getFormData('subtext'); $f_wordoffset = Util::getFormData('wordoffset'); $f_message = Util::getFormData('message', ''); $f_oldmsg = Util::getFormData('oldmsg'); $f_newmsg = Util::getFormData('newmsg'); $f_to = Util::getFormData('to'); $f_to_new = Util::getFormData('to_new'); $f_to_list = Util::getFormData('to_list'); $f_to_field = Util::getFormData('to_field'); $f_cc = Util::getFormData('cc'); $f_cc_new = Util::getFormData('cc_new'); $f_cc_list = Util::getFormData('cc_list'); $f_cc_field = Util::getFormData('cc_field'); $f_bcc = Util::getFormData('bcc'); $f_bcc_new = Util::getFormData('bcc_new'); $f_bcc_list = Util::getFormData('bcc_list'); $f_bcc_field = Util::getFormData('bcc_field'); /* $f_ignoreall is an array - we need to unserialize the data. */ $ignoreall = array(); if (($f_ignoreall = Util::getFormData('ignoreall'))) { $f_ignoreall = unserialize($f_ignoreall); } else { $f_ignoreall = array(); } switch ($actionID) { case 'spell_check_forward': for ($i = 0; $i < count($f_opt); $i++) { $skipword = false; /* If they have an word with no suggestions and they dont type in a replacement, ignore it. */ if (!empty($f_subtext[$i])) { $replacement = $f_subtext[$i]; } else { if ($f_subs[$i] == '0') { $ignoreall = $f_ignoreall; $ignoreall[] = String::lower($f_oldword[$i], true); $skipword = true; } else { $replacement = $f_subs[$i]; } } if (!$skipword) { $pos = -1; $realoffset = $f_wordoffset[$i]; /* Just in case things are whackily out. */ $msg_length = String::length($f_message); if ($realoffset > $msg_length) { $realoffset = $msg_length - 1; } $pos = _findOffset($f_message, $f_oldword[$i], $realoffset); switch ($f_opt[$i]) { case IMP_SPELL_IGNORE: $consume = $pos + String::length($f_oldword[$i]); $addition = String::substr($f_message, 0, $consume); $f_newmsg .= $addition; $f_message = String::substr($f_message, $consume); /* Adjust offsets, as they could be wildly out */ for ($msgnum = 0; $msgnum < count($f_wordoffset); $msgnum++) { $f_wordoffset[$msgnum] -= $consume; } break; case IMP_SPELL_IGNORE_ALL: $ignoreall = $f_ignoreall; $ignoreall[] = String::lower($f_oldword[$i], true); break; case IMP_SPELL_CHANGE_ALL: $f_message = str_replace($f_oldword[$i], $replacement, $f_message); break; case IMP_SPELL_CHANGE: if (!in_array(String::lower($f_oldword[$i], true), $f_ignoreall)) { /* Let's try and keep those offsets semi correct. */ $adjoffset = String::length($replacement) - String::length($f_oldword[$i]); for ($msgnum = 0; $msgnum < count($f_wordoffset); $msgnum++) { $f_wordoffset[$msgnum] += $adjoffset; } $tempmessage = String::substr($f_message, 0, $pos); $tempmessage .= $replacement; $tempmessage .= String::substr($f_message, $pos + String::length($f_oldword[$i])); $f_message = $tempmessage; } break; } } } break; default: $f_oldmsg = $f_message; /* Have to start another wordlist to incorporate into the spell check dictionary methinks. */ $ignoreall = $ignore_list; break; } /* Special treatment depending on language (quotes are not equally treated by ispell in english and in french). */ switch ($language) { case 'fr_FR': $tocheck = str_replace("'", "\\'", escapeShellCmd($f_message)); break; default: $tocheck = $f_message; break; } /* Save the message to a temporary file. */ $spellFile = Horde::getTempFile('spell'); $fp = fopen($spellFile, 'w'); fwrite($fp, $tocheck); fclose($fp); /* Run the actual spell check. */ if (empty($conf['utils']['spellchecker'])) { $notification->push(_("No spellchecking program configured."), 'horde.error'); } else { /* Retrieve any spelling options. */ $spell_opt = ''; if (isset($nls['spelling'][$language])) { $spell_opt = $nls['spelling'][$language]; } exec($conf['utils']['spellchecker'] . ' -a ' . $spell_opt . ' < ' . $spellFile, $warnings); } $msg = ''; for ($i = 0; $i < count($warnings); $i++) { if (substr($warnings[$i], 0, 1) == '&') { $parts = explode(': ', $warnings[$i]); $info = explode(' ', $parts[0]); if (preg_match('|^[A-Z]*$|', $info[1])) { $ignoreall[] = String::lower($info[1], true); } else { $error[] = array($info[1], $info[3], $parts[1]); } } if (preg_match('|^#|', $warnings[$i])) { $info = explode(' ', $warnings[$i]); if (preg_match('|^[A-Z]*$|', $info[1])) { $ignoreall[] = String::lower($info[1], true); } else { $error[] = array($info[1], $info[2], ''); } } } /* Generate a pretty word-wrapped version for displaying. */ $display_msg = htmlspecialchars($f_newmsg . $f_message); $display_msg = Text::wrap($display_msg, $prefs->getValue('wrap_width'), "\n", NLS::getCharset(), (Util::getFormData('reply_type') == 'reply')); if ($browser->hasQuirk('double_linebreak_textarea')) { $display_msg = preg_replace('/(\r?\n){3}/', '$1', $display_msg); }