<?php
/**
 * Copyright 2005-2017 Horde LLC (http://www.horde.org/)
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.horde.org/licenses/gpl.
 *
 * @category  Horde
 * @copyright 2005-2017 Horde LLC
 * @license   http://www.horde.org/licenses/gpl GPL
 * @package   IMP
 */

/**
 * Message viewing action for AJAX application handler.
 *
 * @author    Michael Slusarz <slusarz@horde.org>
 * @category  Horde
 * @copyright 2005-2017 Horde LLC
 * @license   http://www.horde.org/licenses/gpl GPL
 * @package   IMP
 */
class IMP_Ajax_Application_ShowMessage
{
    /**
     * Contents object.
     *
     * @var IMP_Contents
     */
    protected $_contents;

    /**
     * Envelope object.
     *
     * @var Horde_Imap_Client_Data_Envelope
     */
    protected $_envelope;

    /**
     * Indices object.
     *
     * @var IMP_Indices
     */
    protected $_indices;

    /**
     * Don't seen seen flag?
     *
     * @var boolean
     */
    protected $_peek;

    /**
     * Constructor.
     *
     * @param IMP_Indices $indices  The index of the message.
     * @param boolean $peek         Don't set seen flag?
     */
    public function __construct(IMP_Indices $indices, $peek = false)
    {
        global $injector;

        /* Get envelope/header information. We don't use flags in this
         * view. */
        try {
            list($mbox, $uid) = $indices->getSingle();
            if (!$uid) {
                throw new Exception();
            }

            $query = new Horde_Imap_Client_Fetch_Query();
            $query->envelope();

            $imp_imap = $mbox->imp_imap;
            $imp_imap->openMailbox($mbox, Horde_Imap_Client::OPEN_READWRITE);

            $ret = $imp_imap->fetch($mbox, $query, array(
                'ids' => $imp_imap->getIdsOb($uid)
            ));

            if (!($ob = $ret->first())) {
                throw new Exception();
            }

            $imp_contents = $injector->getInstance('IMP_Factory_Contents')->create($indices);
        } catch (Exception $e) {
            throw new IMP_Exception(_("Requested message not found."));
        }

        $this->_contents = $imp_contents;
        $this->_envelope = $ob->getEnvelope();
        $this->_indices = $indices;
        $this->_peek = $peek;
    }

    /**
     * Create the object used to display the message.
     *
     * @param array $args  Configuration parameters:
     *   - headers: (array) The headers desired in the returned headers array
     *              (only used with non-preview view).
     *   - preview: (boolean) Is this the preview view?
     *
     * @return array  Array with the following keys:
     *   - atc_download: The download all link
     *   - atc_label: The label to use for Attachments
     *   - atc_list: The list (HTML code) of attachments
     *   - bcc (FULL): The Bcc addresses
     *   - cc: The CC addresses
     *   - from: The From addresses
     *   - headers (FULL): An array of headers (not including basic headers)
     *   - js: Javascript code to run on display
     *   - list_info (FULL): List information.
     *   - localdate (PREVIEW): The date formatted to the user's timezone
     *   - msgtext: The text of the message
     *   - onepart: True if message only contains one part.
     *   - replyTo (FULL): The Reply-to addresses
     *   - save_as: The save link
     *   - subject: The subject
     *   - subjectlink: The subject with linked URLs/email addresses (defaults
     *                  to 'subject')
     *   - title (FULL): The title of the page
     *   - to: The To addresses
     *
     * @throws IMP_Exception
     */
    public function showMessage($args)
    {
        global $injector, $page_output, $prefs, $registry, $session;

        $preview = !empty($args['preview']);

        $result = array(
            'js' => array()
        );

        /* Set the current time zone. */
        $registry->setTimeZone();

        $mime_headers = $this->_peek
            ? $this->_contents->getHeader()
            : $this->_contents->getHeaderAndMarkAsSeen();

        $headers = array();
        $imp_ui = $injector->getInstance('IMP_Message_Ui');

        /* Develop the list of Headers to display now. Deal with the 'basic'
         * header information first since there are various manipulations
         * done to them. */
        $basic_headers = $imp_ui->basicHeaders();
        if (empty($args['headers'])) {
            $args['headers'] = array('from', 'date', 'to', 'cc', 'bcc');
        }

        $headers_list = array_intersect_key($basic_headers, array_flip($args['headers']));

        /* Build From/To/Cc/Bcc/Reply-To links. */
        foreach (array('from', 'to', 'cc', 'bcc', 'reply-to') as $val) {
            if (isset($headers_list[$val]) &&
                (!$preview || ($val != 'reply-to'))) {
                if ($tmp = $this->getAddressHeader($val)) {
                    $result[$val] = $tmp;
                }
                if ($preview) {
                    unset($headers_list[$val]);
                }
            }
        }

        /* Build the rest of the headers. */
        foreach ($headers_list as $head => $str) {
            if ($val = $mime_headers->getValue($head)) {
                if ($head == 'date') {
                    /* Add local time to date header. */
                    $val = htmlspecialchars($imp_ui->getLocalTime($this->_envelope->date));
                    if ($preview) {
                        $result['localdate'] = $val;
                    }
                } elseif (!$preview) {
                    $val = htmlspecialchars($val);
                }

                if (!$preview) {
                    $headers[$head] = array('id' => Horde_String::ucfirst($head), 'name' => $str, 'value' => $val);
                }
            }
        }

        if (empty($result['reply-to']) ||
            ($result['from']['addr'][0]['b'] == $result['reply-to']['addr'][0]['b'])) {
            unset($result['reply-to'], $headers['reply-to']);
        }

        /* JS requires camelized name for reply-to. */
        if (!$preview && isset($headers['reply-to'])) {
            $result['replyTo'] = $result['reply-to'];
            $headers['reply-to']['id'] = 'ReplyTo';
            unset($result['reply-to']);
        }

        /* Maillog information. */
        $ajax_queue = $injector->getInstance('IMP_Ajax_Queue');
        $ajax_queue->maillog($this->_indices);

        if (!$preview) {
            /* Display the user-specified headers for the current identity. */
            $user_hdrs = $imp_ui->getUserHeaders();
            foreach ($user_hdrs as $user_hdr) {
                $user_val = $mime_headers->getValue($user_hdr);
                if (!empty($user_val)) {
                    if (!is_array($user_val)) {
                        $user_val = array($user_val);
                    }
                    foreach ($user_val as $val) {
                        $headers[] = array(
                            'name' => $user_hdr,
                            'value' => htmlspecialchars($val)
                        );
                    }
                }
            }
            $result['headers'] = array_values($headers);
        }

        /* Process the subject. */
        $subject = $mime_headers->getValue('subject');
        if ($subject) {
            $text_filter = $injector->getInstance('Horde_Core_Factory_TextFilter');
            $filtered_subject = preg_replace("/\b[ \t\r\n]+\b/", ' ', IMP::filterText($subject));

            $result['subject'] = $text_filter->filter($filtered_subject, 'text2html', array(
                'parselevel' => Horde_Text_Filter_Text2html::NOHTML
            ));
            $subjectlink = $text_filter->filter($filtered_subject, 'text2html', array(
                'parselevel' => Horde_Text_Filter_Text2html::MICRO
            ));

            if ($subjectlink != $result['subject']) {
                $result['subjectlink'] = $subjectlink;
            }
            if (!$preview) {
                $result['title'] = $subject;
            }
        } else {
            $result['subject'] = _("[No Subject]");
            if (!$preview) {
                $result['title'] = _("[No Subject]");
            }
        }

        // Create message text and attachment list.
        $result['msgtext'] = '';
        $show_parts = $prefs->getValue('parts_display');

        switch ($registry->getView()) {
        case $registry::VIEW_SMARTMOBILE:
            $contents_mask = 0;
            break;

        default:
            $contents_mask = IMP_Contents::SUMMARY_BYTES |
                IMP_Contents::SUMMARY_SIZE |
                IMP_Contents::SUMMARY_ICON |
                IMP_Contents::SUMMARY_DESCRIP_LINK |
                IMP_Contents::SUMMARY_DOWNLOAD |
                IMP_Contents::SUMMARY_DOWNLOAD_ZIP |
                IMP_Contents::SUMMARY_PRINT_STUB;
            break;
        }

        $part_info = $part_info_display = array('icon', 'description', 'size', 'download', 'download_zip');
        $part_info_display[] = 'print';

        list($mbox, $uid) = $this->_indices->getSingle();

        /* Do MDN processing now. */
        switch ($registry->getView()) {
        case $registry::VIEW_DYNAMIC:
            if ($imp_ui->MDNCheck(new IMP_Indices($mbox, $uid), $mime_headers)) {
                $status = new IMP_Mime_Status(array(
                    _("The sender of this message is requesting notification from you when you have read this message."),
                    sprintf(_("Click %s to send the notification message."), Horde::link('#', '', '', '', '', '', '', array('id' => 'send_mdn_link')) . _("HERE") . '</a>')
                ));
                $status->domid('sendMdnMessage');
                $result['msgtext'] .= strval($status);
            }
        }

        /* Build body text. This needs to be done before we build the
         * attachment list. */
        $session->close();
        $inlineout = $this->_contents->getInlineOutput(array(
            'mask' => $contents_mask,
            'part_info_display' => $part_info_display,
            'show_parts' => $show_parts
        ));
        $session->start();

        $result['js'] = array_merge($result['js'], $inlineout['js_onload']);
        $result['msgtext'] .= $inlineout['msgtext'];
        if ($inlineout['one_part']) {
            $result['onepart'] = true;
        }

        if (count($inlineout['atc_parts']) ||
            (($show_parts == 'all') && count($inlineout['display_ids']) > 2)) {
            $result['atc_label'] = ($show_parts == 'all')
                ? _("Parts")
                : sprintf(ngettext("%d Attachment", "%d Attachments", count($inlineout['atc_parts'])), count($inlineout['atc_parts']));
            if (count($inlineout['atc_parts']) > 2) {
                $result['atc_download'] = Horde::link($this->_contents->urlView($this->_contents->getMIMEMessage(), 'download_all')) . '[' . _("Save All") . ']</a>';
            }
        }

        /* Show attachment information in headers? */
        if (!empty($inlineout['atc_parts'])) {
            $partlist = array();

            if ($show_parts == 'all') {
                array_unshift($part_info, 'id');
            }

            foreach ($inlineout['atc_parts'] as $id) {
                $contents_mask |= IMP_Contents::SUMMARY_DESCRIP |
                    IMP_Contents::SUMMARY_DOWNLOAD |
                    IMP_Contents::SUMMARY_ICON |
                    IMP_Contents::SUMMARY_SIZE;
                $part_info[] = 'description_raw';
                $part_info[] = 'download_url';

                $summary = $this->_contents->getSummary($id, $contents_mask);
                $tmp = array();
                foreach ($part_info as $val) {
                    if (isset($summary[$val])) {
                        $tmp[$val] = ($summary[$val] instanceof Horde_Url)
                            ? strval($summary[$val]->setRaw(true))
                            : $summary[$val];
                    }
                }
                $partlist[] = array_filter($tmp);
            }

            $result['atc_list'] = $partlist;
        }

        list($bmbox, $buid) = ($this->_indices instanceof IMP_Indices_Mailbox)
            ? $this->_indices->buids->getSingle()
            : $this->_indices->getSingle();

        $result['save_as'] = IMP_Contents_View::downloadUrl(
            htmlspecialchars_decode($result['subject']),
            array_merge(
                array('actionID' => 'save_message'),
                $bmbox->urlParams($buid)
            )
        );

        if ($preview) {
            /* Need to grab cached inline scripts. */
            Horde::startBuffer();
            $page_output->outputInlineScript(true);
            if ($js_inline = Horde::endBuffer()) {
                $result['js'][] = $js_inline;
            }

            $result['save_as'] = strval($result['save_as']->setRaw(true));
        } else {
            $list_info = $imp_ui->getListInformation($mime_headers);
            if (!empty($list_info['exists'])) {
                $result['list_info'] = $list_info;
            }
        }

        if (empty($result['js'])) {
            unset($result['js']);
        }

        /* Add changed flag information. */
        if (!$this->_peek && $mbox->is_imap) {
            $status = $mbox->imp_imap->status($mbox, Horde_Imap_Client::STATUS_PERMFLAGS);
            if (in_array(Horde_Imap_Client::FLAG_SEEN, $status['permflags'])) {
                $ajax_queue->flag(array(Horde_Imap_Client::FLAG_SEEN), true, $this->_indices);
            }
        }

        return $result;
    }

    /**
     * Return data to build an address header.
     *
     * @param string $header  The address header.
     * @param integer $limit  Limit display to this many addresses. If null,
     *                        shows all addresses.
     *
     * @return array  An array with the following entries:
     *   - addr: (array) List of addresses/groups.
     *           Group keys: 'a' (list of addresses); 'g' (group name)
     *           Address keys: 'b' (bare address); 'p' (personal part)
     *   - limit: (integer) If limit was reached, the number of total
     *            addresses.
     *   - raw: (string) A raw string to display instead of addresses.
     */
    public function getAddressHeader($header, $limit = 50)
    {
        $addrlist = ($header == 'reply-to')
            ? $this->_envelope->reply_to
            : $this->_envelope->$header;
        $addr_array = $out = array();
        $count = 0;

        $addrlist->unique();

        foreach ($addrlist->base_addresses as $ob) {
            if ($ob instanceof Horde_Mail_Rfc822_Group) {
                $ob->addresses->unique();

                $tmp = array(
                    'a' => array(),
                    'g' => $ob->groupname
                );

                foreach ($ob->addresses as $val) {
                    $tmp['a'][] = array_filter(array(
                        'b' => $val->bare_address,
                        'p' => $val->personal
                    ));

                    if (!is_null($limit) && ($count++ > $limit)) {
                        break;
                    }
                }
            } else {
                $tmp = array_filter(array(
                    'b' => $ob->bare_address,
                    'p' => $ob->personal
                ));
                ++$count;
            }

            $addr_array[] = $tmp;

            if (!is_null($limit) && ($count > $limit)) {
                $out['limit'] = count($addrlist);
                break;
            }
        }

        if (!empty($addr_array)) {
            $out['addr'] = $addr_array;
        } elseif ($header == 'to') {
            $out['raw'] = _("Undisclosed Recipients");
        }

        return $out;
    }

}
