<?php
// $Horde: imp/lib/Message.php,v 1.32.2.12 2003/03/12 17:09:10 slusarz Exp $

/**
 * The IMP_Message:: class contains all functions related to handling
 * messages within IMP, and should (right now this is a design goal,
 * not a reality) be usable outside IMP as well. Actions such as
 * moving, copying, and deleting messages are handled in here so that
 * code need not be repeated between mailbox, message, and other
 * pages.
 *
 * @author  Chris Hyde <chris@jeks.net>
 * @author  Chuck Hagenbuch <chuck@horde.org>
 * @version $Revision: 1.32.2.12 $
 * @since   IMP 2.3
 * @package imp
 */
class IMP_Message {

    /**
     * Returns the current page of messages based on index.
     *
     * @param $arr the whole array of message indices
     * @param $index the current starting point
     * @param $offset is set to the start of the returned section in the list
     * @param $beginIndex is set to the index of $index in the returned section
     *
     * @return the desired section of the array
     */
    function range($arr, $index, &$offset, &$beginIndex, &$num)
    {
        global $prefs;

        // store the original number of messages
        $num = count($arr);

        $count = max($prefs->getValue('max_msgs') * 3, 1000);
        $half = $count / 2;
        if ($index < $half) {
            $start = 0;
        } elseif (($index + $count) >= $num) {
            $start = max(0, $num - $count);
        } else {
            $start = $index - $half;
        }

        $end = min($start + $count, $num);
        $new = array_slice($arr, $start, ($end - $start));
        $offset = $start;
        $beginIndex = $index - $start - 1;

        return $new;
    }

    /**
     * Perform re-threading
     */
    function threadSort()
    {
        global $sorted, $indentLevel, $prefs, $imp;

        $sorted = array();
        $branchsOpen = array();
        $indentLevel = array();

        $ref_array = @imap_thread($imp['stream'], SE_UID);
        if (!is_array($ref_array)) {
            @imap_close($imp['stream']);
            IMP::authenticate();
            $ref_array = @imap_thread($imp['stream'], SE_UID);
            if (!is_array($ref_array)) {
                return array();
            }
        }
        foreach ($ref_array as $key => $val) {
            if (strpos($key, '.num')) {
                $branchsOpen[substr($key, 0, -4)] = 'Open';

                if ($val > 0) {
                    $indentLevel[$val] = count($branchsOpen);

                    if ($prefs->getValue('delhide')) {
                        $overview = imap_fetch_overview($imp['stream'], $val, FT_UID);
                        if (is_array($overview)) {
                            if ($overview[0]->deleted == 0) {
                                $sorted[] = $val;
                            }
                        }
                    } else {
                        $sorted[] = $val;
                    }
                }
            }
            if (strpos($key, '.branch')) {
                unset($branchsOpen[substr($key, 0, -7)]);
            }
        }
    }

    /**
     * Copies or moves an array of messages to a new folder. Handles
     * use of the '**search' folder and the Trash folder.
     *
     * @param $targetMbox the mailbox to move/copy messages to
     * @param $msgs the array of messages to be copied or moved
     * @param $action either MOVE_MESSAGES or COPY_MESSAGES
     */
    function copy($targetMbox, $msgs, $action)
    {
        global $imp, $prefs;

        $return_value = true;
        $serverstring = IMP::serverString();
        $nummsgs = count($msgs);

        switch ($action) {
        case MOVE_MESSAGES:
            $imap_flags = CP_UID | CP_MOVE;
            $message = _("There was an error moving messages to \"%s\". This is what the server said");
            break;
        case COPY_MESSAGES:
            $imap_flags = CP_UID;
            $message = _("There was an error copying messages to \"%s\". This is what the server said");
            break;
        }

        if ($imp['mailbox'] == '**search') {
            $current_folder = '';
            $msg_list = '';
            for ($i = 0; $i < $nummsgs; $i++) {
                /* In search we have msgnum:folder e.g. 12:Sent */
                $msginfo = explode(':', $msgs[$i]);
                $msg_num = $msginfo[0];
                $msg_folder = $msginfo[1];
                if ($current_folder != $msg_folder) {
                    if ($msg_list != '') {
                        if (!imap_mail_copy($imp['stream'], $msg_list, $targetMbox, $imap_flags)) {
                            Horde::raiseMessage(sprintf($message, IMP::displayFolder($targetMbox)) .
                                                ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                            $return_value = false;
                        }
                        if ($prefs->getValue('use_trash') && $action == MOVE_MESSAGES)
                            imap_expunge($imp['stream']);
                    }
                    $msg_list = $msg_num;
                    imap_reopen($imp['stream'], $serverstring . $msg_folder);
                } else {
                    $msg_list .= ',' . $msg_num;
                }
                $current_folder = $msg_folder;
            }
            /* Tidy up the last few that haven't been copied/moved yet. */
            if ($msg_list != '') {
                if (!imap_mail_copy($imp['stream'], $msg_list, $targetMbox, $imap_flags)) {
                    Horde::raiseMessage(sprintf($message, IMP::displayFolder($targetMbox)) .
                                        ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                    $return_value = false;
                }
                if ($prefs->getValue('use_trash') && $action == MOVE_MESSAGES) {
                    imap_expunge($imp['stream']);
                }
            }
        } else {
            $msg_list = implode(',', $msgs);
            if (!imap_mail_copy($imp['stream'], $msg_list, $targetMbox, $imap_flags)) {
                Horde::raiseMessage(sprintf($message, IMP::displayFolder($targetMbox)) .
                                    ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                $return_value = false;
            }
            if ($prefs->getValue('use_trash') && $action == MOVE_MESSAGES) {
                imap_expunge($imp['stream']);
            }
        }

        return $return_value;
    }

    /**
     * Deletes a set of messages, taking into account whether or not a
     * Trash folder is being used.
     *
     * @param mixed $indices An array of messages to be deleted, or a single message number.
     */
    function delete($indices)
    {
        global $imp, $prefs;

        $return_value = true;
        $serverstring = IMP::serverString();
        $trash_folder = IMP::addPreambleString($prefs->getValue('trash_folder'));

        if (is_array($indices) && $imp['mailbox'] == '**search') {
            $old_msg_folder = ' ';
            for ($j = 0; $j < count($indices); $j++) {
                $parts = explode(':', $indices[$j]);
                $msg = $parts[0];
                $msg_folder = $parts[1];

                /* If the folder we are deleting from has changed. */
                if ($msg_folder != $old_msg_folder) {
                    if ($j > 0)    {
                        /* We're changing folder, we've marked a bunch
                         * of messages deleted, or we've moved them to
                         * the trash. */
                        if ($prefs->getValue('use_trash')) {
                            imap_expunge($imp['stream']);
                        }
                    }
                    $old_msg_folder = $msg_folder;
                    imap_reopen($imp['stream'], $serverstring . $msg_folder);
                }
                if ($imp['stream']) {
                    if ($prefs->getValue('use_trash') && $imp['thismailbox'] != $trash_folder) {
                        include_once IMP_BASE . '/lib/Folder.php';
                        if (!IMP_Folder::exists($imp['stream'], $trash_folder)) {
                            IMP_Folder::create($imp['stream'], $trash_folder, $prefs->getValue('subscribe'));
                        }
                        if (!imap_mail_copy($imp['stream'], $msg, $trash_folder, CP_UID | CP_MOVE)) {
                            Horde::raiseMessage(sprintf(_("There was an error moving messages to the trash. This is what the server said")) .
                                                ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                            $return_value = false;
                        }
                    } else {
                        if (!imap_delete($imp['stream'], $msg, FT_UID)) {
                            Horde::raiseMessage(sprintf(_("There was an error deleting messages from the folder \"%s\". This is what the server said"),
                                                        IMP::displayFolder($msg_folder)) . ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                            $return_value = false;
                        }
                    }
                }
            }
            if ($prefs->getValue('use_trash')) {
                imap_expunge($imp['stream']);
            }
        } else {
            if (is_array($indices)) {
                $msg_list = implode(',', $indices);
            } else {
                $msg_list = $indices;
            }
            if ($prefs->getValue('use_trash')) {
                /* Then move it to the trash folder, and expunge from the original folder */
                if ($imp['thismailbox'] != $trash_folder) {
                    include_once IMP_BASE . '/lib/Folder.php';
                    if (!IMP_Folder::exists($imp['stream'], $trash_folder)) {
                        IMP_Folder::create($imp['stream'], $trash_folder, $prefs->getValue('subscribe'));
                    }
                    if (!@imap_mail_copy($imp['stream'], $msg_list, $trash_folder, CP_UID | CP_MOVE)) {
                        Horde::raiseMessage(sprintf(_("There was an error moving messages to the trash. This is what the server said")) .
                                            ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                        $return_value = false;
                    }
                } else {
                    if (!@imap_delete($imp['stream'], $msg_list, FT_UID)) {
                        Horde::raiseMessage(sprintf(_("There was an error deleting messages. This is what the server said")) .
                                            ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                        $return_value = false;
                    }
                }
                @imap_expunge($imp['stream']);
            } else {
                if (!@imap_delete($imp['stream'], $msg_list, FT_UID)) {
                    Horde::raiseMessage(sprintf(_("There was an error deleting messages. This is what the server said")) .
                                        ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                    $return_value = false;
                }
                if ($imp['base_protocol'] == 'pop3') {
                    @imap_expunge($imp['stream']);
                    @imap_close($imp['stream']);
                    IMP::authenticate();
                }
            }
        }

        return $return_value;
    }

    /**
     * Undeletes a set of messages.
     *
     * @param mixed $indices An array of messages to be deleted, or a single message number.
     */
    function undelete($indices)
    {
        global $imp;

        $return_value = true;
        $serverstring = IMP::serverString();

        if (is_array($indices) && $imp['mailbox'] == '**search') {
            $current_folder = '';
            for ($i = 0; $i < count($indices); $i++ ) {
                $msginfo = explode(':',$indices[$i]);
                $msg_num = $msginfo[0];
                $msg_folder = $msginfo[1];
                if ($current_folder != $msg_folder) {
                    imap_reopen($imp['stream'], $serverstring . $msg_folder);
                }
                if ($imp['stream'] && !imap_undelete($imp['stream'], $msg_num, FT_UID)) {
                    Horde::raiseMessage(sprintf(_("There was an error deleting messages in the folder \"%s\". This is what the server said"),
                                                IMP::displayFolder($msg_folder)) . ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                    $return_value = false;
                }
                $current_folder = $msg_folder;
            }
        } else {
            if (is_array($indices)) {
                $message_list = implode(',', $indices);
            } else {
                $message_list = $indices;
            }
            if (!imap_undelete($imp['stream'], $message_list, FT_UID)) {
                Horde::raiseMessage(sprintf(_("There was an error undeleting messages. This is what the server said")) .
                                    ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                $return_value = false;
            }
        }

        return $return_value;
    }

    /**
     * Sets or clears a given flag on an array of messages. Handles
     * use of the '**search' folder.
     *
     * Valid flags are:
     *   \\SEEN
     *   \\FLAGGED
     *   \\ANSWERED
     *   \\DELETED
     *   \\DRAFT
     *
     * @param $flag the imap flag to set or clear
     * @param $indices the array of messages to be flagged
     * @param boolean $action true: set the flag; false: clear the flag
     */
    function flag($flag, $indices, $action = true)
    {
        global $imp;

        $return_value = true;
        $nummsgs = count($indices);
        $serverstring = IMP::serverString();

        if ($action) {
            $function = 'imap_setflag_full';
        } else {
            $function = 'imap_clearflag_full';
        }

        if ($imp['mailbox'] == '**search') {
            $current_folder = '';
            $msg_list = '';
            for ($i = 0; $i < $nummsgs; $i++) {
                /* In search we have msgnum:folder e.g. 12:Sent */
                $msginfo = explode(':', $indices[$i]);
                $msg_num = $msginfo[0];
                $msg_folder = $msginfo[1];
                if ($current_folder != $msg_folder) {
                    if ($msg_list != '') {
                        if (!$function($imp['stream'], $msg_list, $flag, ST_UID)) {
                            Horde::raiseMessage(sprintf(_("There was an error flagging messages in the folder \"%s\". This is what the server said"),
                                                        IMP::displayFolder($current_folder)) . ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                            $return_value = false;
                        }
                    }
                    $msg_list = $msg_num;
                    imap_reopen($imp['stream'], $serverstring . $msg_folder);
                } else {
                    $msg_list .= ',' . $msg_num;
                }
                $current_folder = $msg_folder;
            }
            /* Tidy up the last few that haven't been copied/moved yet. */
            if ($msg_list != '') {
                if (!$function($imp['stream'], $msg_list, $flag, ST_UID)) {
                    Horde::raiseMessage(sprintf(_("There was an error flagging messages in the folder \"%s\". This is what the server said"),
                                                IMP::displayFolder($current_folder)) . ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                    $return_value = false;
                }
            }
        } else {
            $msg_list = implode(',', $indices);
            if (!$function($imp['stream'], $msg_list, $flag, ST_UID)) {
                Horde::raiseMessage(sprintf(_("There was an error flagging messages. This is what the server said")) .
                                    ': <i>' . imap_last_error() . '</i>', HORDE_ERROR);
                $return_value = false;
            }
        }

        return $return_value;
    }

}
?>
