<?php
/**
 * $Horde: imp/compose.php,v 2.369.2.63 2004/03/26 22:43:23 jan Exp $
 *
 * Copyright 1999-2004 Charles J. Hagenbuch <chuck@horde.org>
 * Copyright 1999-2004 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.
 */

/**
 * Returns a To:, Cc: or Bcc: list build from a selection based on 'expand names'.
 */
function getAddressList($field)
{
    $to_list = Horde::getFormData($field . '_list');
    $to = Horde::getFormData($field . '_field');
    if (isset($to_list) && is_array($to_list) && is_array($to)) {
        $tmp = array();
        foreach ($to as $key => $address) {
            $address = format_addresses($address);
            $tmp[$key] = $address;
        }
        foreach ($to_list as $key => $address) {
            $address = format_addresses($address);
            if ($address != '') {
                $tmp[$key] = $address;
            }
        }
        $to_new = Horde::getFormData($field . '_new');
        if (!empty($to_new)) {
            $tmp[] = $to_new;
        }
        return implode(', ', $tmp);
    }
}

/**
 * Return an url for mailbox.php depending on what kind of start/page
 * information we have.
 */
function mailboxReturnURL($confirm = false)
{
    $url = Horde::applicationUrl('mailbox.php');

    if (($start = Horde::getFormData('start'))) {
        $url = IMP::addParameter($url, 'start=' . urlencode($start));
    }

    if (($page = Horde::getFormData('page'))) {
        $url = IMP::addParameter($url, 'page=' . urlencode($page));
    }

    if ($confirm) {
        $url = IMP::addParameter($url, 'compose_confirm=1');
    }

    return $url;
}

/**
 * Find the main "body" part (if any) in a set of attachments.
 */
function findBody(&$attachments)
{
    foreach ($attachments as $mime) {
        if (isset($mime->type) && $mime->type == TYPETEXT) {
            $data = IMP::getDecodedPart($mime);
            if ($mime->subtype == 'html') {
                $trans = get_html_translation_table(HTML_ENTITIES);
                $trans = array_flip($trans);
                return strtr(strip_tags(str_replace(array('<br>', '<BR>', '<br />'),
                                                    array("\n", "\n", "\n"),
                                                    $data)), $trans);
            }

            return $data;
        }
    }

    return '';
}

/**
 * Behaves like explode() except that it ignores $delimiter if
 * preceded by a "\" character, or if it is inside single or double
 * quotes.
 */
function rfc822_explode($delimiter, $str)
{
    $i = 0;
    $match = 0;
    $quotes = array('"', "'");
    $in_quote = null;
    $prev = null;

    if (in_array($str[0], $quotes)) {
        $in_quote = $str[0];
    } elseif ($str[0] == $delimiter) {
        $arr[] = '';
        $match = 1;
    }

    for ($i = 1; $i < strlen($str); $i++) {
        $char = $str[$i];
        if (in_array($char, $quotes)) {
            if ($prev !== "\\") {
                if ($in_quote === $char) {
                    $in_quote = null;
                } elseif ($in_quote === null) {
                    $in_quote = $char;
                }
            }
        } elseif ($char == $delimiter && $prev !== "\\" && is_null($in_quote)) {
            $arr[] = substr($str, $match, $i - $match);
            $match = $i + 1;
        }
        $prev = $char;
    }

    if ($match != $i) {
        // The string ended without a $delimiter
        $arr[] = substr($str, $match, $i - $match);
    }

    return $arr;
}

/**
 * Use the Registry to expand names and, if $full is true, generate a
 * full, rfc822-valid address list, returning error information for
 * any address that is either not valid or fails to expand.
 */
function expandAddresses($addrString, $full = false)
{
    if (!preg_match('|[^\s]|', $addrString)) {
        return '';
    }

    global $registry, $prefs;

    $addrString = preg_replace('/,\s+/', ',', $addrString);

    include_once 'Mail/RFC822.php';
    $parser = new Mail_RFC822(null, '@INVALID');

    $src = explode("\t", $prefs->getValue('search_sources'));
    if (count($src) == 1 && empty($src[0])) {
        $src = array();
    }

    $search_fields = array();
    $search_field_pref = $prefs->getValue('search_fields');
    if (!empty($search_field_pref)) {
        $field_arr = explode("\n", $prefs->getValue('search_fields'));
        foreach ($field_arr as $field) {
            $field = trim($field);
            if (!empty($field)) {
                $tmp = explode("\t", $field);
                if (count($tmp) > 1) {
                    $source = array_splice($tmp, 0, 1);
                    $search_fields[$source[0]] = $tmp;
                }
            }
        }
    }

    $arr = rfc822_explode(',', $addrString);
    $results = $registry->call('contacts/search', array($arr, $src, $search_fields));
    if (PEAR::isError($results)) {
        return $results;
    }
    $i = 0;
    $error = false;
    $ambiguous = false;
    $missing = array();
    foreach ($results as $res) {
        $tmp = $arr[$i];
        if ($parser->validateMailbox(MIME::encodeAddress($tmp, null, ''))) {
            // noop
        } elseif (count($res) == 1) {
            if ($full) {
                if (strpos($res[0]['email'], ',') !== false) {
                    $arr[$i] = $res[0]['name'] . ': ' . $res[0]['email'] . ';';
                } else {
                    list($mbox, $host) = explode('@', $res[0]['email']);
                    $arr[$i] = IMP::rfc822WriteAddress($mbox, $host, $res[0]['name']);
                }
            } else {
                $arr[$i] = $res[0]['name'];
            }
        } elseif (count($res) > 1) {
            // Handle the multiple case - we return an array with all found addresses.
            $arr[$i] = array($arr[$i]);
            foreach ($res as $one_res) {
                if ($full) {
                    if (strpos($one_res['email'], ',') !== false) {
                        $arr[$i][] = $one_res['name'] . ': ' . $one_res['email'] . ';';
                    } else {
                        $mbox_host = explode('@', $one_res['email']);
                        if (isset($mbox_host[1])) {
                            $arr[$i][] = IMP::rfc822WriteAddress($mbox_host[0], $mbox_host[1], $one_res['name']);
                        }
                    }
                } else {
                    $arr[$i][] = $one_res['name'];
                }
            }
            $ambiguous = true;
        } else {
            // Handle the missing/invalid case - we should return error info
            // on each address that couldn't be expanded/validated.
            $error = true;
            if (!$ambiguous) {
                $arr[$i] = new PEAR_Error(null, null, null, null, $arr[$i]);
                $missing[$i] = $arr[$i];
            }
        }
        $i++;
    }

    if ($ambiguous) {
        foreach ($missing as $i => $addr) {
            $arr[$i] = $addr->getUserInfo();
        }
        return $arr;
    } elseif ($error) {
        return new PEAR_Error(_("Please resolve ambiguous or invalid addresses."), null, null, null, $res);
    } else {
        return implode(', ', $arr);
    }
}

function expandAllAddresses()
{
    global $to, $cc, $bcc, $to_errors, $cc_errors, $bcc_errors;

    $to  = expandAddresses(Horde::getFormData('to', getAddressList('to')), true);
    $cc  = expandAddresses(Horde::getFormData('cc', getAddressList('cc')), true);
    $bcc = expandAddresses(Horde::getFormData('bcc', getAddressList('bcc')), true);

    if (is_array($to) || is_array($cc) || is_array($bcc)) {
        Horde::raiseMessage(_("Please resolve ambiguous or invalid addresses."), HORDE_MESSAGE);
    }

    if (PEAR::isError($to)) {
        $to_errors = $to;
        $to = cleanExpandedAddressList($to_errors->getUserInfo());
        Horde::raiseMessage($to_errors->getMessage(), HORDE_MESSAGE);
    }
    if (PEAR::isError($cc)) {
        $cc_errors = $cc;
        $cc = cleanExpandedAddressList($cc_errors->getUserInfo());
        Horde::raiseMessage($cc_errors->getMessage(), HORDE_MESSAGE);
    }
    if (PEAR::isError($bcc)) {
        $bcc_errors = $bcc;
        $bcc = cleanExpandedAddressList($bcc_errors->getUserInfo());
        Horde::raiseMessage($bcc_errors->getMessage(), HORDE_MESSAGE);
    }
}

function cleanExpandedAddressList($list)
{
    $results = array();
    if (is_array($list)) {
        foreach ($list as $entry) {
            if (is_object($entry)) {
                $results[] = $entry->getUserInfo();
            } else {
                $results[] = $entry;
            }
        }
    }
}

/*
 * Make sure a path is inside of the upload directory.
 */
function tempFilePath($file) {
    return Horde::getTempDir() . '/' . basename($file);
}

/*
 * Checks for non-standard address formats, such as separating with
 * spaces or semicolons.
 */
function format_addresses($address_string)
{
    /*
     * If there are angle brackets (<>), or a colon (group name
     * delimiter), assume the user knew what they were doing.
     */
    if ((strpos($address_string, '>') === false) &&
        (strpos($address_string, ':') === false)){
        $address_string = trim(strtr($address_string, ';,', '  '));
        $address_string = preg_replace('|\s+|', ', ', $address_string);
    }
    return $address_string;
}

/*
 * Encodes attachments for a message being sent or saved.
 */
function addMimeParts(&$message)
{
    global $imp, $conf;

    if (isset($_POST['attachments_name']) && count($_POST['attachments_name']) > 0) {
        $index = Horde::getFormData('index');
        if (!empty($index)) {
            include_once HORDE_BASE . '/lib/MIME/Structure.php';
            $structure = imap_fetchstructure($imp['stream'], $index, FT_UID);
            $attachments = MIME_Structure::parse($structure, $index, $MimeID);
        }

        for ($i = 0; $i < count($_POST['attachments_name']); $i++) {
            /* Read it from the filesystem if it's a normal
               attachment, and then delete the temp file. */
            if (!preg_match('|.*-(.*).mime$|', $_POST['attachments_file'][$i], $regs)) {
                if ($GLOBALS['conf']['compose']['use_vfs'] && file_exists(HORDE_BASE . '/lib/VFS.php')) {
                    require_once HORDE_BASE . '/lib/VFS.php';
                    $vfs = &Horde_VFS::singleton($conf['vfs']['type'], $conf['vfs']['params']);
                    $contents = $vfs->read('.horde/imp/attachments', $_POST['attachments_file'][$i]);
                    $vfs->deleteFile('.horde/imp/attachments', $_POST['attachments_file'][$i]);
                } else {
                    $filename = tempFilePath($_POST['attachments_file'][$i]);
                    $fd = fopen($filename, 'rb');
                    $contents = fread($fd, filesize($filename));
                    fclose($fd);
                    unlink($filename);
                }
            } else {
                /* Fetch it from the mail message content if it's a
                 * forwarded attachment. */
                $ref = $regs[1];
                $fmime = $attachments[$ref];
                $contents = IMP::getDecodedPart($fmime);
            }

            include_once HORDE_BASE . '/lib/MIME/Message.php';
            $part = new MIME_Part();

            /* Some browsers do not send the mime type. */
            if (strcmp($_POST['attachments_type'][$i], '')) {
                $part->setType($_POST['attachments_type'][$i]);
            } else {
                $part->setType('application/octet-stream');
            }

            $part->setName($_POST['attachments_name'][$i]);

            /* Base64-encode all attachments to preserve linebreaks,
               ensure transportability, etc. */
            $encoded_contents = base64_encode($contents);
            $contents = chunk_split($encoded_contents, 76, "\n");
            $part->setTransferEncoding('base64');
            $part->setContents($contents);
            $message->addPart($part);
        }
    }
}

/*
 * Adds the attachments to the message (in the case of a forward with
 * attachments).
 */
function attachFiles($structure, $ref)
{
    include_once HORDE_BASE . '/lib/MIME/Structure.php';
    $attachments = MIME_Structure::parse($structure, Horde::getFormData('index'), $MimeID);
    foreach ($attachments as $ref => $mime) {
        if ($ref != 1 || $mime->type != TYPETEXT) {
            if (!$mime->header) {
                $_POST['attachments_name'][] = $mime->name;
                $_POST['attachments_size'][] = $mime->bytes;
                $_POST['attachments_file'][] = Horde::getFormData('index') . '-' . $ref . '.mime';
                $_POST['attachments_type'][] = $mime->TYPE . '/' . $mime->subtype;
            }
        }
    }
}

/**
 * Retrieves and wraps the submitted message text.
 */
function getMessage()
{
    global $prefs;

    $msg = Horde::getFormData('message', '');
    $msg = Text::wrap($msg, $prefs->getValue('wrap_width'), "\n", Lang::getCharset());
    return $msg;
}


define('IMP_BASE', dirname(__FILE__));

$session_control = 'netscape';
require_once IMP_BASE . '/lib/base.php';
require_once IMP_BASE . '/lib/version.php';
require_once IMP_BASE . '/lib/Folder.php';
require_once HORDE_BASE . '/lib/Menu.php';
require_once HORDE_BASE . '/lib/MIME.php';
require_once HORDE_BASE . '/lib/Text.php';
require_once IMP_BASE . '/lib/Identity/IMP.php';
require_once 'PEAR.php';


IMP::checkAuthentication();

$msg = '';
$to = '';
$cc = '';
$bcc = '';
$subject = '';
$get_sig = true;
$identity = new Identity_IMP();
$sent_mail_folder = $identity->getValue('sent_mail_folder', Horde::getFormData('identity'));
$actionID = Horde::getFormData('actionID', NO_ACTION);
$index = Horde::getFormData('index');

/* Start to compress pages if requested. */
if ($conf['compress_pages']) {
    ob_start('ob_gzhandler');
}

/* Translate non-int values into the correct int. */
if (strcspn($actionID, '0123456789')) {
    $actionID = $actions[$actionID];
}

/* Set the current time zone. */
if ($prefs->getValue('timezone') != '') {
    putenv('TZ=' . $prefs->getValue('timezone'));
}

/* Run through the action handlers. */
$ACTION_TEXT = _("Compose a message");
switch ($actionID) {
 case NO_ACTION:
     $ACTION_TEXT = _("Message Composition");
     break;

 case REFRESH_COMPOSE:
     $ACTION_TEXT = _("Message Composition");
     $get_sig = false;
     break;

 case COMPOSE:
     $to = MIME::decode(Horde::getFormData('to'));
     $subject = MIME::decode(Horde::getFormData('subject'));
     $ACTION_TEXT = _("Compose a message");
     break;

 case MAILTO:
     if (!empty($index)) {
         $h = @imap_header($imp['stream'], imap_msgno($imp['stream'], $index));
         if (isset($mailto) && isset($h->$mailto)) {
             $to = MIME::decode($h->$mailto);
         } else if (!empty($h->reply_to)) {
             $to = IMP::addrArray2String($h->reply_to);
         } else if (isset($h->from)) {
             $to = IMP::addrArray2String($h->from);
         } else {
             $to = '';
         }
         $ACTION_TEXT = _("Compose a message");
     }
     break;

 case DRAFT:
     if (!empty($index)) {
         $h = imap_header($imp['stream'], imap_msgno($imp['stream'], $index));

         if (is_object($h)) {
             if (isset($h->fromaddress)) {
                 $from_list = $identity->getAllFromAddresses();
                 $default = $identity->getDefault();
                 if (isset($from_list[$default]) && strstr(strtolower($h->fromaddress), strtolower($from_list[$default]))) {
                     $_GET['identity'] = $default;
                 } else {
                     unset($from_list[$default]);
                     foreach ($from_list as $id => $from_item) {
                         if (strstr(strtolower($h->fromaddress), strtolower($from_item))) {
                             $_GET['identity'] = $id;
                             break;
                         }
                     }
                 }
                 $sent_mail_folder = IMP::preambleString() . $identity->getValue('sent_mail_folder', Horde::getFormData('identity'));
             }
             if (isset($h->to)) {
                 $to = IMP::addrArray2String($h->to);
             }
             if (isset($h->cc)) {
                 $cc = IMP::addrArray2String($h->cc);
             }
             if (isset($h->bcc)) {
                 $bcc = IMP::addrArray2String($h->bcc);
             }
             if (isset($h->reply_to)) {
                 $reply_to = IMP::addrArray2String($h->reply_to);
             }
             if (isset($h->subject)) {
                 $subject = MIME::decode($h->subject);
             }
         }

         include_once HORDE_BASE . '/lib/MIME/Structure.php';
         $structure = @imap_fetchstructure($imp['stream'], $index, FT_UID);
         $attachments = MIME_Structure::parse($structure, $index, $MimeID);
         $msg = "\n" . findBody($attachments);
         if (@count($structure->parts) > 1) {
             attachFiles($structure, '');
         }

         $ACTION_TEXT = _("Compose a message");
     }
     $get_sig = false;
     break;

 case EXPAND_ADDRESSES:
     expandAllAddresses();
     $get_sig = false;
     break;

 case REPLY:
 case REPLY_ALL:
     if (!empty($index)) {
         $h = imap_header($imp['stream'], imap_msgno($imp['stream'], $index));

         /* Set the message_id and references headers. */
         if (isset($h->message_id)) {
             $_POST['in_reply_to'] = chop($h->message_id);
             if (isset($h->references)) {
                 $_POST['references'] = chop($h->references) . ' ' . $_POST['in_reply_to'];
             } else {
                 $_POST['references'] = $_POST['in_reply_to'];
             }
         } else {
             $_POST['in_reply_to'] = '';
             $_POST['references'] = '';
         }

         // Set headers.
         if ($actionID == REPLY) {
             $to = '';
             if (isset($h->reply_to)) {
                 $to = IMP::addrArray2String($h->reply_to);
             }
             if (empty($to)) {
                 $to = IMP::addrArray2String($h->from);
             }
         } else {
             // filter out our own address from the addresses we reply to
             $me = $identity->getAllFromAddresses();

             // build the to: header
             $from_arr = $h->from;
             $to_arr = $h->reply_to;
             $reply = '';

             if (!empty($to_arr)) {
                 $reply = IMP::addrArray2String($to_arr);
             } elseif (!empty($from_arr)) {
                 $reply = IMP::addrArray2String($from_arr);
             }
             $to = IMP::addrArray2String(array_merge($to_arr, $from_arr));

             // build the cc: header
             $cc_arr = $h->to;
             if (isset($h->cc)) {
                 if (!empty($cc_arr) &&
                     ($reply != IMP::addrArray2String($cc_arr))) {
                     $cc_arr = array_merge($cc_arr, $h->cc);
                 } else {
                     $cc_arr = $h->cc;
                 }
             }
             $cc = IMP::addrArray2String($cc_arr, array_merge($me, IMP::bareAddress($to)));

             // build the bcc: header
             if (isset($h->bcc)) {
                 $bcc = IMP::addrArray2String($h->bcc, $me);
             }
         }

         if (isset($h->from)) {
             $qfrom = IMP::addrArray2String($h->from);
         } else {
             $qfrom = '&lt;&gt;';
         }

         include_once HORDE_BASE . '/lib/MIME/Structure.php';
         $structure = imap_fetchstructure($imp['stream'], $index, FT_UID);
         $attachments = MIME_Structure::parse($structure, $index, $MimeID);

         if ($prefs->getValue('reply_quote')) {
             $quote_str = "\n" . rtrim($prefs->getValue('quote_prefix')) . ' ';
             $msg = findBody($attachments);
             $wrap_width = $prefs->getValue('wrap_width') - strlen($quote_str);
             if ($wrap_width < 20) {
                 $wrap_width = 20;
             }
             $msg = Text::wrap($msg, $wrap_width, $quote_str, Lang::getCharset());
             $msg  = _("Quoting") . ' ' . $qfrom . ":\n$quote_str$msg";
             $msg .= "\n";
         }

         if (isset($h->subject) && (strtolower(substr(MIME::decode($h->subject), 0, 3)) != 're:')) {
             $subject = 'Re: ' . MIME::decode($h->subject);
         } else if (isset($h->subject)) {
             $subject = MIME::decode($h->subject);
         } else {
             $subject = 'Re: ';
         }

         if ($actionID == REPLY) {
             $ACTION_TEXT = _("Reply:") . ' ' . $subject;
         } else {
             $ACTION_TEXT = _("Reply to All:") . ' ' . $subject;
         }
     }
     break;

 case FORWARD:
     if (!empty($index)) {
         $h = imap_header($imp['stream'], imap_msgno($imp['stream'], $index));
         if (isset($h->from[0])) {
             $message_source = IMP::addrObject2String($h->from[0]);
         } else {
             $message_source = '';
         }

         $msg  = "\n\n";
         $msg .= "\n----- ";
         $msg .= sprintf(_("Forwarded message from %s"), $message_source);
         $msg .= " -----\n";

         if (isset($h->date) && $h->date) {
             $msg .= _("    Date: ") . MIME::decode($h->date);
             $msg .= "\n";
         }
         if (isset($h->from) && is_array($h->from)) {
             $msg .= _("    From: ") . IMP::addrArray2String($h->from);
             $msg .= "\n";
         }
         if (isset($h->reply_to) && is_array($h->reply_to)) {
             $msg .= _("Reply-To: ") . IMP::addrArray2String($h->reply_to);
             $msg .= "\n";
         }
         if (isset($h->subject) && $h->subject) {
             $msg .= _(" Subject: ") . MIME::decode($h->subject);
             $msg .= "\n";
         }
         if (isset($h->to) && is_array($h->to)) {
             if (($to_string = IMP::addrArray2String($h->to))) {
                 $msg .= _("      To: ") . $to_string;
                 $msg .= "\n";
             }
         }

         include_once HORDE_BASE . '/lib/MIME/Structure.php';
         $structure = imap_fetchstructure($imp['stream'], $index, FT_UID);
         $attachments = MIME_Structure::parse($structure, $index, $MimeID);
         $msg .= "\n";
         $msg .= findBody($attachments);
         $msg .= "\n----- " . _("End forwarded message") . " -----\n";

         if (isset($h->subject)) {
             $subject = _("Fwd:") . ' ' . MIME::decode($h->subject);
             $ACTION_TEXT = _("Forward:") . ' ' . MIME::decode($h->subject);
         } else {
             $subject = _("Fwd:");
             $ACTION_TEXT = _("Forward");
         }

         attachFiles($structure, '');
     }
     break;

 case BOUNCE_MESSAGE:
     if (!empty($index) && ($f_to = Horde::getFormData('to'))) {
         $recipients = $f_to = format_addresses($f_to);

         /* Check for 8-bit characters in addresses. */
         foreach (IMP::bareAddress($recipients, true) as $val) {
             if (MIME::is8bit($val)) {
                 Horde::raiseMessage(_("Invalid character in e-mail address."), HORDE_ERROR);
                 $get_sig = false;
                 break 2;
             }
         }

         $orig_headers = @imap_fetchheader($imp['stream'], $index, FT_UID);
         $orig_headers = str_replace("\r", '', $orig_headers);
         $header_arr = explode("\n", $orig_headers);

         foreach ($header_arr as $header) {
             if (!empty($header) && $header != "\n") {
                 if (preg_match('|^([-\w]*): (.*)|', $header, $pieces)) {
                     if (isset($headers[$pieces[1]])) {
                         $headers[$pieces[1]] .= "\n" . $header;
                     } else {
                         $headers[$pieces[1]] = $pieces[2];
                     }
                     $last = $pieces[1];
                 } else {
                     $headers[$last] .= "\n" . $header;
                 }
             }
         }

         /* We need to set the Return-Path header to the current user - see
            RFC 2821 [4.4]. */
         $headers['Return-Path'] = $identity->getFromAddress();

         $headers['Resent-Date'] = date('r');
         $headers['Resent-From'] = $identity->getFromAddress();
         $headers['Resent-To'] = $f_to;
         $headers['Resent-Message-ID'] = '<' . uniqid(time() . '.') . '@' . $registry->getParam('server_name') . '>';
         IMP::addSiteHeaders($headers);

         $msg = @imap_body($imp['stream'], $index, FT_UID);

         include_once 'Mail.php';
         $params = IMP::prepareMailerParams();
         $mailer = &Mail::factory($conf['mailer']['type'], $params);
         $status = $mailer->send(MIME::encodeAddress($recipients, null, $imp['maildomain']), $headers, $msg);
         if (!PEAR::isError($status)) {
             $entry = sprintf("%s Redirected message sent to %s from %s",
                              $_SERVER['REMOTE_ADDR'], $recipients, $imp['user']);
             Horde::logMessage($entry, __FILE__, __LINE__, LOG_INFO);

             if (($prefs->getValue('compose_popup') || Horde::getFormData('popup'))
                 && $browser->hasFeature('javascript')) {
                 echo '<script language="JavaScript" type="text/javascript">window.close();</script>';
             } else {
                 header('Location: ' . mailboxReturnURL());
             }

             // catch error messages from c-client
             imap_errors();

             exit;
         } else {
             Horde::logMessage($status->getMessage(), __FILE__, __LINE__, LOG_ERR);
         }
         $actionID = BOUNCE_COMPOSE;
         Horde::raiseMessage(_("Redirecting failed."));
     }
     break;

 case SEND_MESSAGE:
     $message = getMessage();
     $recips = null;
     if ($f_to = Horde::getFormData('to', getAddressList('to'))) {
         $recips = $f_to;
     }
     if ($f_cc = Horde::getFormData('cc', getAddressList('cc'))) {
         $recips .= (empty($recips)) ? $f_cc : ', ' . $f_cc;
     }
     if ($f_bcc = Horde::getFormData('bcc', getAddressList('bcc'))) {
         $recips .= (empty($recips)) ? $f_bcc : ', ' . $f_bcc;
     }

     /* Check for 8-bit characters in addresses. */
     foreach (IMP::bareAddress($recips, true) as $val) {
         if (MIME::is8bit($val)) {
             Horde::raiseMessage(_("Invalid character in e-mail address."), HORDE_ERROR);
             $get_sig = false;
             break 2;
         }
     }

     if (empty($f_to) && empty($f_cc) && empty($f_bcc)) {
         $get_sig = false;
         Horde::raiseMessage(_("You must have at least one recipient."), HORDE_ERROR);
         break;
     } else {
         include_once HORDE_BASE . '/lib/MIME/Message.php';
         $mime = new MIME_Message($imp['maildomain']);

         $message = str_replace("\r\n", "\n", $message);
         $body = new MIME_Part('text/plain', $message);

         // Append trailer text to $body.
         if ($conf['msg']['append_trailer'] && @is_readable(IMP_BASE . '/config/trailer.txt')) {
             $trailer = "\n";
             $trailer .= implode(@file(IMP_BASE . '/config/trailer.txt'), '');
             $trailer = Text::expandEnvironment($trailer);
             $body->appendContents($trailer);
         }

         $mime->addPart($body);
         addMimeParts($mime);
         $msg = $mime->toString();

         $recipients = '';
         $from = $identity->getFromLine(Horde::getFormData('identity'), Horde::getFormData('from'));
         $from_info = imap_rfc822_parse_adrlist($from, $imp['maildomain']);
         $barefrom = $from_info[0]->mailbox . '@' . $from_info[0]->host;

         // Add a Received header for the hop from browser to server.
         if (empty($_SERVER['REMOTE_HOST'])) {
             $remote = gethostbyaddr($_SERVER['REMOTE_ADDR']);
         } else {
             $remote = $_SERVER['REMOTE_HOST'];
         }
         $received = 'from ' . $remote . ' (';

         if (!empty($_SERVER['REMOTE_IDENT'])) {
             $received .= $_SERVER['REMOTE_IDENT'] . '@' . $remote . ' ';
         } elseif ($remote != $_SERVER['REMOTE_ADDR']) {
             $received .= $remote . ' ';
         }
         $received .= '[' . $_SERVER['REMOTE_ADDR'] . '])' . " \n\t";
         $received .= 'by ' . $_SERVER['SERVER_NAME'] . ' (IMP) with HTTP' . " \n\t";
         $received .= 'for <' . $imp['user'] . '@' . $imp['server'] . '>; ' . date('r');
         $headers['Received'] = $received;

         $headers['Message-ID'] = '<' . uniqid(time() . '.') . '@' . $registry->getParam('server_name') . '>';
         $headers['Date'] = date('r');

         if ($conf['compose']['allow_receipts'] &&
             Horde::getFormData('request_return_receipt')) {
             $headers['Disposition-Notification-To'] = $barefrom;
             $headers['X-Confirm-Reading-To'] = $barefrom;
             $headers['X-pmrqc'] = 1;
             $headers['Return-receipt-to'] = $barefrom;
         }

         $headers['From'] = $from;

         $identity->setDefault(Horde::getFormData('identity'));
         $replyto = $identity->getValue('replyto_addr');
         if (!empty($replyto) && $replyto != $barefrom) {
             $headers['Reply-to'] = $replyto;
         }
         if (!empty($f_to)) {
             $f_to = format_addresses($f_to);
             $headers['To'] = $f_to;
             $recipients = $f_to;
         }
         if ($conf['compose']['allow_cc'] && !empty($f_cc)) {
             $f_cc = format_addresses($f_cc);
             $headers['Cc'] = $f_cc;
             $recipients = (empty($recipients)) ? $f_cc : "$recipients, " . $f_cc;
         }
         if ($conf['compose']['allow_bcc'] && !empty($f_bcc)) {
             $f_bcc = format_addresses($f_bcc);
             $recipients = (empty($recipients)) ? $f_bcc : "$recipients, " . $f_bcc;
         }
         $subject = Horde::getFormData('subject');
         if (!empty($subject)) {
             $headers['Subject'] = $subject;
         }
         if (($ref = Horde::getFormData('references'))) {
             $headers['References'] = $ref;
         }
         if (($irt = Horde::getFormData('in_reply_to'))) {
             $headers['In-Reply-To'] = $irt;
         }
         $headers = $mime->header($headers);
         $headers['User-Agent'] = 'Internet Messaging Program (IMP) ' . IMP_VERSION;
         IMP::addSiteHeaders($headers);

         include_once 'Mail.php';
         $params = IMP::prepareMailerParams();
         $mailer = &Mail::factory($conf['mailer']['type'], $params);
         $status = $mailer->send(MIME::encodeAddress($recipients, null, $imp['maildomain']), $headers, $msg);
         if (!PEAR::isError($status)) {
             if (Horde::getFormData('is_reply') && $index) {
                 imap_setflag_full($imp['stream'], $index, '\\ANSWERED', SE_UID);
             }

             $entry = sprintf("%s Message sent to %s from %s",
                              $_SERVER['REMOTE_ADDR'], $recipients, $imp['user']);
             Horde::logMessage($entry, __FILE__, __LINE__, LOG_INFO);

             /* Should we save this message in the sent mail folder? */
             $sent_mail_folder = $identity->getSentmailFolder(null, Horde::getFormData('sent_mail_folder'));
             if (!empty($sent_mail_folder) &&
                 ((!$prefs->isLocked('save_sent_mail') &&
                  Horde::getFormData('save_sent_mail') == 'on') ||
                 ($prefs->isLocked('save_sent_mail') &&
                  $prefs->getValue('save_sent_mail')))) {

                 /* Keep Bcc: headers on saved messages. */
                 if (!empty($f_bcc)) {
                     $headers['Bcc'] = $f_bcc;
                 }

                 /* Loop through the envelop and add headers. */
                 reset($headers);
                 $fcc = '';
                 foreach ($headers as $key => $val) {
                     if ($key != 'recipients') {
                         $fcc .= $key . ': ' . $val . "\n";
                     }
                 }
                 $fcc .= "\n";
                 $fcc .= $msg;

                 // Make absolutely sure there are no bare newlines.
                 $fcc = preg_replace("|([^\r])\n|", "\\1\r\n", $fcc);
                 $fcc = str_replace("\n\n", "\n\r\n", $fcc);

                 if (!IMP_Folder::exists($imp['stream'], $sent_mail_folder)) {
                     IMP_Folder::create($imp['stream'], $sent_mail_folder, $prefs->getValue('subscribe'));
                 }
                 if (!@imap_append($imp['stream'], IMP::serverString() . $sent_mail_folder, $fcc, '\\Seen')) {
                     /* TODO: status(_("Message sent successfully, but not saved to") . $sent_mail_folder); */
                 }
             }

             if (($prefs->getValue('compose_popup') || Horde::getFormData('popup'))
                 && $browser->hasFeature('javascript')) {
                 echo '<script language="JavaScript" type="text/javascript">window.close();</script>';
             } else {
                 header('Location: ' . mailboxReturnURL($prefs->getValue('compose_confirm')));
             }

             // catch error messages from c-client
             imap_errors();

             exit;
         } else {
             Horde::logMessage($status->getMessage(), __FILE__, __LINE__, LOG_ERR);
             Horde::raiseMessage(sprintf(_("There was an error sending your message: %s"), $status->getMessage()), HORDE_ERROR);
         }
     }
     unset($msg);
     $get_sig = false;
     break;

 case SAVE_DRAFT:
     $drafts_folder = $prefs->getValue('drafts_folder');
     if (!empty($drafts_folder)) {
         $drafts_folder = IMP::addPreambleString($drafts_folder);
         if ($message = getMessage()) {
             $message = str_replace("\r\n", "\n", $message);

             include_once HORDE_BASE . '/lib/MIME/Message.php';
             $mime = new MIME_Message($imp['maildomain']);

             $body = new MIME_Part('text/plain', $message);
             $mime->addPart($body);
             addMimeParts($mime);
             $body = $mime->toString();
         } else {
             $body = '';
         }

         $from = $identity->getFromLine(Horde::getFormData('identity'), Horde::getFormData('from'));

         $hdrs = 'Date: ' . date('r') . "\n";
         if (!empty($from)) {
             $hdrs .= "From: $from\n";
         }
         if (($to = Horde::getFormData('to'))) {
             $to = MIME::encodeAddress(format_addresses($to), null, $imp['maildomain']);
             $hdrs .= "To: $to\n";
         }
         if (($cc = Horde::getFormData('cc'))) {
             $cc = MIME::encodeAddress(format_addresses($cc), null, $imp['maildomain']);
             $hdrs .= "Cc: $cc\n";
         }
         if (($bcc = Horde::getFormData('bcc'))) {
             $bcc = MIME::encodeAddress(format_addresses($bcc), null, $imp['maildomain']);
             $hdrs .= "Bcc: $bcc\n";
         }
         if (($sub = Horde::getFormData('subject'))) {
             $sub = MIME::encode($sub, Lang::getCharset());
             $hdrs .= "Subject: $sub\n";
         }
         if (isset($mime)) {
             $env = $mime->header();
             foreach ($env as $key => $val) {
                 $hdrs .= $key . ': ' . $val . "\n";
             }
         }
         $hdrs .= "\n";

         $body = $hdrs . $body;

         // Make absolutely sure there are no bare newlines.
         $body = preg_replace("|([^\r])\n|", "\\1\r\n", $body);
         $body = str_replace("\n\n", "\n\r\n", $body);

         $append_flags = '\\Draft \\Seen';
         if (IMP_Folder::exists($imp['stream'], $drafts_folder) ||
             IMP_Folder::create($imp['stream'], $drafts_folder, $prefs->getValue('subscribe'))) {
             if (!@imap_append($imp['stream'], IMP::serverString() . $drafts_folder, $body, $append_flags)) {
                 Horde::raiseMessage(sprintf(_("Saving the draft failed. This is what the server said: %s"), imap_last_error()), HORDE_ERROR);
             } elseif ($prefs->getValue('close_draft')) {
                 if (($prefs->getValue('compose_popup') || Horde::getFormData('popup'))
                     && $browser->hasFeature('javascript')) {
                     echo '<script language="JavaScript" type="text/javascript">window.close();</script>';
                 } else {
                     header('Location: ' . mailboxReturnURL());
                 }

                 // catch error messages from c-client
                 imap_errors();

                 exit;
             } else {
                 $get_sig = false;
                 break;
             }
         }
     }
     $get_sig = false;
     Horde::raiseMessage(_("There was an error saving a draft."), HORDE_ERROR);
     break;

 case ADD_ATTACHMENT:
     if (!isset($_FILES['file_upload']['size']) ||
         !isset($_FILES['file_upload']['tmp_name']) ||
         !is_uploaded_file($_FILES['file_upload']['tmp_name'])) {
         Horde::raiseMessage(sprintf(_("There was a problem with the file upload. The file may have been larger than the maximum allowed size (%d MB)."),
                                     ini_get('upload_max_filesize')), HORDE_ERROR);
     } elseif ($_FILES['file_upload']['size'] > 0) {
         $file = $_FILES['file_upload']['tmp_name'];
         $_POST['attachments_name'][] = $_FILES['file_upload']['name'];
         $_POST['attachments_size'][] = $_FILES['file_upload']['size'];
         $_POST['attachments_type'][] = (isset($_FILES['file_upload']['type'])) ? $_FILES['file_upload']['type'] : '';

         if ($conf['compose']['use_vfs'] && file_exists(HORDE_BASE . '/lib/VFS.php')) {
             $attachment = basename($file);
             $fp = @fopen($file, 'rb');
             $data = fread($fp, filesize($file));
             fclose($fp);

             require_once HORDE_BASE . '/lib/VFS.php';
             $vfs = &Horde_VFS::singleton($conf['vfs']['type'], $conf['vfs']['params']);
             $vfs->writeData('.horde/imp/attachments', $attachment, $data);
         } else {
             $attachment = Horde::getTempFile('impatt', false);
             move_uploaded_file($file, $attachment);
             chmod($attachment, 0600);
         }
         $_POST['attachments_file'][] = $attachment;
         Horde::raiseMessage(_("Added an attachment."), HORDE_SUCCESS);
     } else {
         Horde::raiseMessage(_("There was a problem with the file upload. The file may have been larger than the maximum allowed size."), HORDE_ERROR);
     }

     $get_sig = false;
     break;

 case DELETE_ATTACHMENT:
     if (isset($_POST['delattachments'])) {
         for ($i = 0; $i < count($_POST['delattachments']); $i++) {
             for ($j = 0; $j < count($_POST['attachments_file']); $j++) {
                 if (strcmp($_POST['delattachments'][$i], $_POST['attachments_file'][$j]) == 0) {
                     if (!preg_match('|.mime$|', $_POST['attachments_file'][$j])) {
                         if ($conf['compose']['use_vfs'] && file_exists(HORDE_BASE . '/lib/VFS.php')) {
                             require_once HORDE_BASE . '/lib/VFS.php';
                             $vfs = &Horde_VFS::singleton($conf['vfs']['type'], $conf['vfs']['params']);
                             $vfs->deleteFile('.horde/imp/attachments', $_POST['attachments_file'][$j]);
                         } else {
                             unlink(tempFilePath($_POST['attachments_file'][$j]));
                         }
                     }
                     array_splice($_POST['attachments_name'], $j, 1);
                     array_splice($_POST['attachments_size'], $j, 1);
                     array_splice($_POST['attachments_file'], $j, 1);
                     array_splice($_POST['attachments_type'], $j, 1);
                 }
             }
         }
     }

     Horde::raiseMessage(_("Deleted an attachment."), HORDE_SUCCESS);
     $get_sig = false;
     break;

 case CANCEL_COMPOSE:
     $files = unserialize(rawurldecode(Horde::getFormData('files')));
     foreach ($files as $file) {
         unlink(tempFilePath($file));
     }
     if (Horde::getFormData('popup')) {
         echo '<script language="JavaScript" type="text/javascript">window.close(
);</script>';
     } else {
         header('Location: ' . mailboxReturnURL());
     }

     // catch error messages from c-client
     imap_errors();

     exit;
     break;

 case BOUNCE_COMPOSE:
     $ACTION_TEXT = _("Redirect this message");
     break;

 case SPELL_CHECK_CANCEL:
     $msg = "\n" . Horde::getFormData('oldmsg');
     $expanded = Horde::getFormData('to_list');
     if (!empty($expanded)) {
         expandAllAddresses();
     }
     $get_sig = false;
     break;

 case SPELL_CHECK_DONE:
     $msg = "\n";
     $msg .= Horde::getFormData('newmsg');
     $msg .= Horde::getFormData('message');
     $expanded = Horde::getFormData('to_list');
     if (!empty($expanded)) {
         expandAllAddresses();
     }
     $get_sig = false;
     break;

 case SPELL_CHECK:
 case SPELL_CHECK_FORWARD:
     include IMP_BASE . '/spelling.php';
     break;
}

/* Set the 'save_sent_mail' checkbox for the form. */
if (Horde::getFormData('reloaded')) {
    $ssm_check = Horde::getFormData('save_sent_mail') == 'on';
} else {
    $ssm_check = $identity->saveSentmail(Horde::getFormData('identity'));
}

$title = _("Message Composition");
$js_onLoad = null;
require IMP_TEMPLATES . '/common-header.inc';

if ($prefs->getValue('compose_popup') || Horde::getFormData('popup')) {
    $cancel_js = 'self.close();';
    /* Include the JavaScript for the help system (if enabled). */
    if ($conf['user']['online_help'] && $browser->hasFeature('javascript')) {
        Help::javascript();
    }

    /* If the attachments list is not empty, we must reload this page and
       delete the attachments. */
    if (!empty($_POST['attachments_file'])) {
        $url = IMP::addParameter(Horde::selfUrl(), 'actionID=' . CANCEL_COMPOSE);
        $url = IMP::addParameter($url, 'files=' . rawurlencode(serialize($_POST['attachments_file'])));
        $url = IMP::addParameter($url, 'popup=1');
        $cancel_js = 'self.location.href=\'' . $url . ';\'';
    } else {
        $cancel_js = 'self.close();';
    }
} else {
    /* If the attachments list is not empty, we must reload this page and
       delete the attachments. */
    if (!empty($_POST['attachments_file'])) {
        $url = IMP::addParameter(mailboxReturnUrl(Horde::selfUrl()), 'actionID=' . CANCEL_COMPOSE);
        $url = IMP::addParameter($url, 'files=' . rawurlencode(serialize($_POST['attachments_file'])));
        $cancel_js = 'window.location = \'' . $url . '\';';
    } else {
        $cancel_js = 'window.location = \'' . mailboxReturnURL() . '\';';
    }
    include IMP_BASE . '/menu.php';
}
require IMP_BASE . '/status.php';

$select_list = $identity->getSelectList();

if (Horde::getFormData('from') || $prefs->isLocked('default_identity')) {
    $from = $identity->getFromLine(Horde::getFormData('identity'), Horde::getFormData('from'));
} else {
    $from_selected = Horde::getFormData('identity');
    if (isset($from_selected)) {
        $identity->setDefault($from_selected);
    }
}

/* Grab any data that we were supplied with. */
if (empty($msg)) $msg = "\n" . getMessage();
if (empty($to)) $to = Horde::getFormData('to', getAddressList('to'));
if (empty($cc)) $cc = Horde::getFormData('cc', getAddressList('cc'));
if (empty($bcc)) $bcc = Horde::getFormData('bcc', getAddressList('bcc'));
if (empty($subject)) $subject = Horde::getFormData('subject');

$all_sigs = $identity->getAllSignatures();
$folders = IMP::flist(array('INBOX'));
foreach ($all_sigs as $ident => $sig) {
    if ($conf['user']['select_sentmail_folder']) {
        $i = 0;
        $select = null;
        foreach ($folders as $folder) {
            if ($folder['val'] == IMP::preambleString() . $identity->getValue('sent_mail_folder', $ident)) {
                $select = $i;
            }
            $i++;
        }
    } else {
        $select = $identity->getValue('sent_mail_folder', $ident);
    }
    $identities[$ident] = array($sig,
                                $identity->getValue('sig_first', $ident),
                                $select,
                                $identity->getValue('save_sent_mail', $ident));
}
$sig = $identity->getSignature();
$sig_first = $identity->getValue('sig_first');

if ($get_sig) {
    if (isset($msg) && !empty($sig)) {
        if ($sig_first) {
            $msg  = "\n" . $sig . $msg;
        } else {
            $msg .= "\n" . $sig;
        }
    }
}

$timeout = ini_get('session.gc_maxlifetime');

if (isset($actionID) && ($actionID == BOUNCE_COMPOSE)) {
    $mailbox = Horde::getFormData('thismailbox', $imp['mailbox']);
    include IMP_TEMPLATES . '/compose/bounce.inc';
    include IMP_TEMPLATES . '/compose/javascript.inc';
} else {
    if ($browser->hasFeature('javascript')) {
        include IMP_TEMPLATES . '/compose/javascript.inc';
    }

    if (isset($actionID) &&
        ($actionID == SPELL_CHECK || $actionID == SPELL_CHECK_FORWARD)) {
        include IMP_TEMPLATES . '/compose/spelling.inc';
    } else {
        $tabindex = 1;
        include IMP_TEMPLATES . '/compose/compose.inc';
    }
}

$registry->shutdown();
require IMP_TEMPLATES . '/common-footer.inc';
$prefs->store();

// catch error messages from c-client
imap_errors();
