<?php
/**
 * $Horde: imp/spelling.php,v 2.54 2003/06/17 07:53:46 slusarz Exp $
 *
 * Copyright 1999-2003 Charles J. Hagenbuch <chuck@horde.org>
 * Copyright 1999-2003 Jon Parise <jon@horde.org>
 *
 * 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, "<font color=\"ff0000\">$error</font>", $pos, String::length($error));
    $message = String::substr($message, $start, $length + 28);

    return $message;
}

/* Fetch and clean form data. */
$f_opt = Horde::getFormData('opt');
$f_subs = Horde::getFormData('subs');
$f_oldword = Horde::getFormData('oldword');
$f_subtext = Horde::getFormData('subtext');
$f_wordoffset = Horde::getFormData('wordoffset');
$f_message = Text::wrap(Horde::getFormData('message', ''), $prefs->getValue('wrap_width'), "\n", NLS::getCharset());
if ($browser->hasQuirk('double_linebreak_textarea')) {
    $f_message = preg_replace('/(\r?\n){3}/', '$1', $f_message);
}
$f_oldmsg = Horde::getFormData('oldmsg');
$f_newmsg = Horde::getFormData('newmsg');
$f_to = Horde::getFormData('to');
$f_to_new = Horde::getFormData('to_new');
$f_to_list = Horde::getFormData('to_list');
$f_to_field = Horde::getFormData('to_field');
$f_cc = Horde::getFormData('cc');
$f_cc_new = Horde::getFormData('cc_new');
$f_cc_list = Horde::getFormData('cc_list');
$f_cc_field = Horde::getFormData('cc_field');
$f_bcc = Horde::getFormData('bcc');
$f_bcc_new = Horde::getFormData('bcc_new');
$f_bcc_list = Horde::getFormData('bcc_list');
$f_bcc_field = Horde::getFormData('bcc_field');

/* $f_ignoreall is an array - we need to unserialize the data. */
$ignoreall = array();
if (($f_ignoreall = Horde::getFormData('ignoreall'))) {
    $f_ignoreall = unserialize($f_ignoreall);
} else {
    $f_ignoreall = array();
}

if ($actionID == '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. */
            if ($realoffset > String::length($f_message)) {
                $realoffset = String::length($f_message) - 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;
            }
        }
    }
} else {
    $f_oldmsg = $f_message;

    /* Have to start another wordlist to incorporate into the spell
       check dictionary methinks. */
    $ignoreall = $ignore_list;
}

/* 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) &&
        array_key_exists('spelling', $nls) &&
        array_key_exists($language, $nls['spelling'])) {
        $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], '');
        }
    }
}
