<?php

/**
 * @package Horde_Kolab
 *
 * $Horde: framework/Kolab/Kolab/IMAP.php,v 1.1 2007/03/09 13:16:14 jan Exp $
 */

/**
 * The Kolab_IMAP_Connection_cclient class connects to an IMAP server
 * using the IMAP functionality within PHP.
 *
 * $Horde: framework/Kolab/Kolab/IMAP.php,v 1.1 2007/03/09 13:16:14 jan Exp $
 *
 * Copyright 2007 The Horde Project (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Gunnar Wrobel <wrobel@pardus.de>
 * @author  Thomas Jarosch <thomas.jarosch@intra2net.com>
 * @package Horde_Kolab
 */
class Kolab_IMAP_Connection_cclient extends Kolab_IMAP_Connection {

    /**
     * Basic IMAP connection string
     *
     * @var string
     */
    var $_base_mbox;

    /**
     * IMAP connection string that includes the folder
     *
     * @var string
     */
    var $_mbox;

    /**
     * Connect to the IMAP server.
     *
     * @param string  $login     The user account name.
     * @param string  $password  The user password.
     * @param boolean $tls       Should TLS be used for the connection?
     *
     * @return mixed True in case the connection was opened
     * successfully, a PEAR error otherwise.
     */
    function connect($login, $password, $tls = false)
    {
        $options = "";
        if (!$tls)
            $options = "/notls";

        $this->_base_mbox = "{" . $this->_server . ":" . $this->_port 
                          . $options . "}";

        $this->_mbox = $this->_base_mbox;

        $result = @imap_open($this->_base_mbox, $login, $password, OP_HALFOPEN);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed to connect to server %s. Error was %s"), $this->_server, @imap_last_error()));
        }
        $this->_imap = $result;
        return $this->_imap;
    }

    /**
     * Disconnect from the IMAP server.
     *
     * @return mixed True in case the connection was closed
     * successfully, a PEAR error otherwise.
     */
    function disconnect()
    {
        $result = @imap_close($this->_imap);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed to disconnecting from server %s. Error was %s"), $this->_server, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Open the given folder.
     *
     * @param string $folder The folder to open
     *
     * @return mixed True in case the folder was opened successfully,
     * a PEAR error otherwise.
     */
    function select($folder)
    {
        $this->_mbox = $this->_base_mbox . $folder;
        $result = @imap_reopen($this->_imap, $this->_mbox);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed to open folder %s. Error was %s"), $folder, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Return the status of the current folder.
     *
     * @return array An array that contains 'uidvalidity' and
     * 'uidnext'
     */
    function status()
    {
        $status = @imap_status_current($this->_imap, SA_MESSAGES | SA_UIDVALIDITY | SA_UIDNEXT);
        if (!$status) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed to read the status of folder %s. Error was %s"), $this->_mbox, @imap_last_error()));
        }

        $result = array();
        $result["uidvalidity"] = $status->uidvalidity;
        $result["uidnext"] = $status->uidnext;

        return $result;
    }

    /**
     * Get the uids of the messages in this folder.
     *
     * @return mixed The message ids or a PEAR error in case of an
     * error
     */
    function getUids()
    {
        $uids = @imap_search($this->_imap, "UNDELETED", SE_UID);

        if (!is_array($uids)) {
            $uids = array();
        }

        return $uids;
    }

    /**
     * Search the current folder using the given list of search
     * criteria.
     *
     * @param string $search_list A list of search criteria
     *
     * @return mixed The list of matching message ids or a PEAR error
     * in case of an error.
     */
    function search($search_list)
    {
        $result = @imap_search($this->_imap, $search_list, SE_UID);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed to search folder %s with %s. Error was %s"), $this->_mbox, $search_list, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Search the headers of the messages. c-client does not allow
     * using "HEADER" as it is possible with Net/IMAP. So we need a
     * workaroung.
     *
     * @param string $field  The name of the header field
     * @param string $value  The value that field should match
     *
     * @return mixed The list of matching message ids or a PEAR error
     * in case of an error.
     */
    function searchHeaders($field, $value) 
    {
        $uids = $this->getUids();
        if (is_a($uids, 'PEAR_Error')) {
            return $uids;
        }
        
        $result = array();
        foreach ($uids as $uid) {
            $header = $this->getMessageHeader($uid, false);
            if (is_a($header, 'PEAR_Error')) {
                return $header;
            }
            $header_array = MIME_Structure::parseMIMEHeaders($header);
            if (isset($header_array[$field]) && $header_array[$field] == $value) {
                $result[] = $uid;
            }
        }
        return $result;
    }
    
    /**
     * Retrieve the message headers for a given message id.
     *
     * @param int     $uid            The message id
     * @param boolean $peek_for_body  Prefetch the body
     *
     * @return mixed The message header or a PEAR error in case of an
     * error.
     */
    function getMessageHeader($uid, $peek_for_body = true)
    {
        $flags = FT_UID;
        if ($peek_for_body)
            $flags |= FT_PREFETCHTEXT;

        $result = @imap_fetchheader($this->_imap, $uid, $flags);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed fetching header of message %s. Error was %s"), $uid, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Retrieve the message body for a given message id.
     *
     * @param int     $uid            The message id
     *
     * @return mixed The message body or a PEAR error in case of an
     * error.
     */
    function getMessageBody($uid)
    {
        $result = @imap_body($this->_imap, $uid, FT_UID);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed fetching body of message %s. Error was %s"), $uid, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Retrieve the full message text for a given message id.
     *
     * @param int     $uid            The message id
     *
     * @return mixed The message text or a PEAR error in case of an
     * error.
     */
    function getMessage($uid)
    {
        $header = $this->getMessageHeader($uid);
        if (is_a($header, 'PEAR_Error')) {
            return $header;
        }

        $body = $this->getMessageBody($uid);
        if (is_a($body, 'PEAR_Error')) {
            return $body;
        }

        return $header . $body;
    }

    /**
     * Retrieve a list of mailboxes on the server.
     *
     * @return mixed The list of mailboxes or a PEAR error in case of an
     * error.
     */
    function getMailboxes()
    {
        $result = @imap_list($this->_imap, $this->_base_mbox, '*');
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed listing folders in %s. Error was %s"), $this->_base_mbox, @imap_last_error()));
        }

	$folders = array();
	$server_len = strlen($this->_base_mbox);
	foreach ($result as $folder) {
            if (substr($folder, 0, $server_len) == $this->_base_mbox) {
	        $folders[] = substr($folder, $server_len);
            }
        }
        return $folders;
    }
    
    /**
     * Fetch the annotation on a folder.
     *
     * @param string  $entries        The entry to fetch.
     * @param string  $value          The specific value to fetch.
     * @param string  $mailbox_name   The name of the folder.
     *
     * @return mixed The annotation value or a PEAR error in case of
     * an error.
     */
    function getAnnotation($entries, $value, $mailbox_name)
    {
        $result = @imap_getannotation($this->_imap, $mailbox_name, $entries, $value);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed retrieving annotation from folder %s. Error was %s"), $mailbox_name, @imap_last_error()));
        }
        if (isset($result[$value])) {
            return $result[$value];
        }
	return '';
    }

    /**
     * Append a message to the current folder.
     *
     * @param string  $msg       The message to append
     *
     * @return mixed True or a PEAR error in case of an error.
     */
    function appendMessage($msg)
    {
        $result = @imap_append($this->_imap, $this->_mbox, $msg);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed appending message in folder %s. Error was %s"), $this->_mbox, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Copy a message to a new folder.
     *
     * @param int    $uid         IMAP message id
     * @param string $new_folder  Target folder
     *
     * @return mixed True or a PEAR error in case of an error.
     */
    function copyMessage($uid, $new_folder)
    {
        $result = @imap_mail_copy($this->_imap, $uid, $new_folder, CP_UID);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed copying message to folder %s. Error was %s"), $new_folder, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Move a message to a new folder.
     *
     * @param int    $uid         IMAP message id
     * @param string $new_folder  Target folder
     *
     * @return mixed True or a PEAR error in case of an error.
     */
    function moveMessage($uid, $new_folder)
    {
        $result = @imap_mail_move($this->_imap, $uid, $new_folder, CP_UID);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed moving message to folder %s. Error was %s"), $new_folder, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Delete messages from the current folder.
     *
     * @param int    $uids         IMAP message ids
     *
     * @return mixed True or a PEAR error in case of an error.
     */
    function deleteMessages($uids)
    {
        if (!is_array($uids)) {
            $uids = array($uids);
        }

        foreach($uids as $uid) {
            $result = @imap_delete($this->_imap, $uid, FT_UID);
            if (!$result) {
                return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed deleting message %s. Error was %s"), $uid, @imap_last_error()));
            }
        }
        return true;
    }

    /**
     * Undelete a message in the current folder.
     *
     * @param int    $uid         IMAP message id
     *
     * @return mixed True or a PEAR error in case of an error.
     */
    function undeleteMessages($uid)
    {
        $result = @imap_undelete($this->_imap, $uid, FT_UID);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed undeleting message %s. Error was %s"), $uid, @imap_last_error()));
        }
        return $result;
    }

    /**
     * Expunge messages in the current folder.
     *
     * @return mixed True or a PEAR error in case of an error.
     */
    function expunge()
    {
        $result = @imap_expunge($this->_imap);
        if (!$result) {
            return PEAR::raiseError(sprintf(_("Horde/Kolab/IMAP: Failed expunging messages in folder %s. Error was %s"), $this->_mbox, @imap_last_error()));
        }
        return $result;
    }
}