Example #1
0
 /**
  * Format flowed text for HTML output.
  *
  * @param string $text    The text to format.
  * @param boolean $delsp  Was text created with DelSp formatting?
  *
  * @return string  The formatted text.
  */
 protected function _formatFlowed($text, $delsp = null)
 {
     $flowed = new Horde_Text_Flowed($text, $this->_mimepart->getCharset());
     $flowed->setMaxLength(0);
     if (!is_null($delsp)) {
         $flowed->setDelSp($delsp);
     }
     return $flowed->toFixed();
 }
Example #2
0
 public function testFlowedToFixed()
 {
     $flowed = new Horde_Text_Flowed(">line 1 \n>line 2 \n>line 3");
     $this->assertEquals(">line 1 line 2 line 3", $flowed->toFixed());
     // See Bug #4832
     $flowed = new Horde_Text_Flowed("line 1\n>from line 2\nline 3");
     $this->assertEquals("line 1\n>from line 2\nline 3", $flowed->toFixed());
     $flowed = new Horde_Text_Flowed("line 1\n From line 2\nline 3");
     $this->assertEquals("line 1\nFrom line 2\nline 3", $flowed->toFixed());
 }
Example #3
0
    public function testBug10431()
    {
        $text = 'Das kรถnnte zum Beispiel so aussehen, dass wir bei entsprechenden Anfragen diese an eine Kontaktperson bei Euch weiterleiten. Oder Ihr schnรผrt ein entsprechendes Paket, dass wir in unseren Angeboten mit anfรผhren. Bei erfolgreicher Vermittlung bekรคmen wir eine Vermittlungsgebรผhr.
Wir stรคnden dann weiterhin fรผr 3rd-Level-Support zur Verfรผgung, d.h. fรผr alle Anfragen des Kunden bzgl. Horde, die nicht zum Tagesgeschรคft gehรถren.';
        $text = Horde_String::convertCharset($text, 'UTF-8', 'ISO-8859-1');
        $textBody = new Horde_Mime_Part();
        $textBody->setType('text/plain');
        $textBody->setCharset('ISO-8859-1');
        $flowed = new Horde_Text_Flowed($text, 'ISO-8859-1');
        $flowed->setDelSp(true);
        $textBody->setContents($flowed->toFlowed());
        $flowed_txt = $textBody->toString(array('headers' => false));
        $textBody2 = new Horde_Mime_Part();
        $textBody2->setType('text/plain');
        $textBody2->setCharset('ISO-8859-1');
        $textBody2->setContents($flowed_txt, array('encoding' => 'quoted-printable'));
        $flowed2 = new Horde_Text_Flowed($textBody2->getContents(), 'ISO-8859-1');
        $flowed2->setMaxLength(0);
        $flowed2->setDelSp(true);
        $this->assertEquals($text, trim($flowed2->toFixed()));
    }
Example #4
0
File: Copy.php Project: horde/horde
 /**
  * Copy/move messages.
  *
  * @param string $mbox          The mailbox name to copy/move the task to.
  * @param IMP_Indices $indices  An indices object.
  * @param boolean $move         Move if true, copy if false.
  *
  * @return boolean  True on success.
  */
 public function copy($mbox, IMP_Indices $indices, $move)
 {
     global $injector;
     $success = true;
     foreach ($indices as $ob) {
         foreach ($ob->uids as $uid) {
             /* Fetch the message contents. */
             $imp_contents = $injector->getInstance('IMP_Factory_Contents')->create($ob->mbox->getIndicesOb($uid));
             /* Fetch the message headers. */
             $subject = strval($imp_contents->getHeader()->getHeader('Subject'));
             /* Re-flow the message for prettier formatting. */
             $body_part = $imp_contents->getMimePart($imp_contents->findBody());
             if (!$body_part) {
                 $success = false;
                 continue;
             }
             $flowed = new Horde_Text_Flowed($body_part->getContents());
             if ($body_part->getContentTypeParameter('delsp') == 'yes') {
                 $flowed->setDelSp(true);
             }
             $body = $flowed->toFlowed(false);
             /* Convert to current charset */
             /* TODO: When Horde_Icalendar supports setting of charsets
              * we need to set it there instead of relying on the fact
              * that both Nag and IMP use the same charset. */
             $body = Horde_String::convertCharset($body, $body_part->getCharset(), 'UTF-8');
             if (!$this->_create($mbox, $subject, $body)) {
                 $success = false;
             }
         }
     }
     /* Delete the original messages if this is a "move" operation. */
     if ($move) {
         $indices->delete();
     }
     return $success;
 }
Example #5
0
 /**
  * Generate the MDN according to the specifications listed in RFC
  * 3798 [3].
  *
  * @param boolean $action   Was this MDN type a result of a manual
  *                          action on part of the user?
  * @param boolean $sending  Was this MDN sent as a result of a manual
  *                          action on part of the user?
  * @param string $type      The type of action performed by the user.
  *                          Per RFC 3798 [3.2.6.2] the following types are
  *                          valid:
  *                            - deleted
  *                            - displayed
  * @param string $name      The name of the local server.
  * @param Horde_Mail_Transport $mailer  Mail transport object.
  * @param array $opts       Additional options:
  *   - charset: (string) Default charset.
  *              DEFAULT: NONE
  *   - from_addr: (string) From address.
  *                DEFAULT: NONE
  * @param array $mod        The list of modifications. Per RFC 3798
  *                          [3.2.6.3] the following modifications are
  *                          valid:
  *                            - error
  * @param array $err        If $mod is 'error', the additional
  *                          information to provide. Key is the type of
  *                          modification, value is the text.
  */
 public function generate($action, $sending, $type, $name, $mailer, array $opts = array(), array $mod = array(), array $err = array())
 {
     $opts = array_merge(array('charset' => null, 'from_addr' => null), $opts);
     if (!($hdr = $this->_headers[self::MDN_HEADER])) {
         throw new RuntimeException('Need at least one address to send MDN to.');
     }
     $to = $hdr->getAddressList(true);
     $ua = Horde_Mime_Headers_UserAgent::create();
     if ($orig_recip = $this->_headers['Original-Recipient']) {
         $orig_recip = $orig_recip->value_single;
     }
     /* Set up the mail headers. */
     $msg_headers = new Horde_Mime_Headers();
     $msg_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create());
     $msg_headers->addHeaderOb($ua);
     /* RFC 3834 [5.2] */
     $msg_headers->addHeader('Auto-Submitted', 'auto-replied');
     $msg_headers->addHeaderOb(Horde_Mime_Headers_Date::create());
     if ($opts['from_addr']) {
         $msg_headers->addHeader('From', $opts['from_addr']);
     }
     $msg_headers->addHeader('To', $to);
     $msg_headers->addHeader('Subject', Horde_Mime_Translation::t("Disposition Notification"));
     /* MDNs are a subtype of 'multipart/report'. */
     $msg = new Horde_Mime_Part();
     $msg->setType('multipart/report');
     $msg->setContentTypeParameter('report-type', 'disposition-notification');
     /* The first part is a human readable message. */
     $part_one = new Horde_Mime_Part();
     $part_one->setType('text/plain');
     $part_one->setCharset($opts['charset']);
     if ($type == 'displayed') {
         $contents = sprintf(Horde_Mime_Translation::t("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers['Date'], $this->_headers['To'], $this->_headers['Subject']);
         $flowed = new Horde_Text_Flowed($contents, $opts['charset']);
         $flowed->setDelSp(true);
         $part_one->setContentTypeParameter('format', 'flowed');
         $part_one->setContentTypeParameter('DelSp', 'Yes');
         $part_one->setContents($flowed->toFlowed());
     }
     // TODO: Messages for other notification types.
     $msg[] = $part_one;
     /* The second part is a machine-parseable description. */
     $part_two = new Horde_Mime_Part();
     $part_two->setType('message/disposition-notification');
     $part_two_h = new Horde_Mime_Headers();
     $part_two_h->addHeader('Reporting-UA', $name . '; ' . $ua);
     if (!empty($orig_recip)) {
         $part_two_h->addHeader('Original-Recipient', 'rfc822;' . $orig_recip);
     }
     if ($opts['from_addr']) {
         $part_two_h->addHeader('Final-Recipient', 'rfc822;' . $opts['from_addr']);
     }
     if ($msg_id = $this->_headers['Message-ID']) {
         $part_two_h->addHeader('Original-Message-ID', strval($msg_id));
     }
     /* Create the Disposition field now (RFC 3798 [3.2.6]). */
     $dispo = ($action ? 'manual-action' : 'automatic-action') . '/' . ($sending ? 'MDN-sent-manually' : 'MDN-sent-automatically') . '; ' . $type;
     if (!empty($mod)) {
         $dispo .= '/' . implode(', ', $mod);
     }
     $part_two_h->addHeader('Disposition', $dispo);
     if (in_array('error', $mod) && isset($err['error'])) {
         $part_two_h->addHeader('Error', $err['error']);
     }
     $part_two->setContents(trim($part_two_h->toString()) . "\n");
     $msg[] = $part_two;
     /* The third part is the text of the original message.  RFC 3798 [3]
      * allows us to return only a portion of the entire message - this
      * is left up to the user. */
     $part_three = new Horde_Mime_Part();
     $part_three->setType('message/rfc822');
     $part_three_text = array(trim($this->_headers->toString()) . "\n");
     if (!empty($this->_msgtext)) {
         $part_three_text[] = "\n" . $this->_msgtext;
     }
     $part_three->setContents($part_three_text);
     $msg[] = $part_three;
     return $msg->send($to, $msg_headers, $mailer);
 }
Example #6
0
}
if ($tid = $vars->get('transaction')) {
    $history = Whups::permissionsFilter($whups_driver->getHistory($ticket->getId()), 'comment', Horde_Perms::READ);
    if (!empty($history[$tid]['comment'])) {
        // If this was a restricted comment, load the group_id it was
        // restricted to and default to keeping that restriction on
        // the reply.
        foreach ($history[$tid]['changes'] as $change) {
            if (!empty($change['private'])) {
                $permission = $GLOBALS['injector']->getInstance('Horde_Perms')->getPermission('whups:comments:' . $change['value']);
                $group_id = array_shift(array_keys($permission->getGroupPermissions()));
                $vars->set('group', $group_id);
                break;
            }
        }
        $flowed = new Horde_Text_Flowed(preg_replace("/\\s*\n/U", "\n", $history[$tid]['comment']), 'UTF-8');
        $vars->set('newcomment', $flowed->toFlowed(true));
    }
}
// Edit action.
$title = '[#' . $id . '] ' . $ticket->get('summary');
$editform = new Whups_Form_Ticket_Edit($vars, $ticket, sprintf(_("Update %s"), $title));
if ($vars->get('formname') == 'whups_form_ticket_edit') {
    if ($editform->validate($vars)) {
        $editform->getInfo($vars, $info);
        $ticket->change('summary', $info['summary']);
        $ticket->change('state', $info['state']);
        $ticket->change('priority', $info['priority']);
        $ticket->change('due', $info['due']);
        if (!empty($info['version'])) {
            $ticket->change('version', $info['version']);
Example #7
0
    public function render($transaction, &$vars)
    {
        global $prefs, $conf, $registry;
        static $canUpdate, $comment_count = 0;
        if (!isset($canUpdate)) {
            $canUpdate = $GLOBALS['registry']->getAuth() && Whups::hasPermission($vars->get('queue'), 'queue', 'update');
        }
        $comment = '';
        $private = false;
        $changes = array();
        $changelist = $vars->get('changes');
        if (!$changelist) {
            return '';
        }
        /* Format each change in this history entry, including comments,
         * etc. */
        foreach ($changelist as $change) {
            switch ($change['type']) {
                case 'summary':
                    $changes[] = sprintf(_("Summary ⇒ %s"), htmlspecialchars($change['value']));
                    break;
                case 'message':
                    $ticket = $vars->get('ticket_id');
                    try {
                        if (Whups::hasMessage($ticket, $change['value'])) {
                            $changes[] = implode(' ', Whups::messageUrls($ticket, $change['value'], $vars->get('queue')));
                        }
                    } catch (Whups_Exception $e) {
                    }
                    break;
                case 'delete-attachment':
                    $changes[] = _("Deleted Original Message");
                    break;
                case 'attachment':
                    $ticket = $vars->get('ticket_id');
                    try {
                        if ($file = Whups::getAttachments($ticket, $change['value'])) {
                            $changes[] = sprintf(_("New Attachment: %s"), implode(' ', Whups::attachmentUrl($ticket, $file, $vars->get('queue'))));
                        } else {
                            $changes[] = sprintf(_("New Attachment: %s"), htmlspecialchars($change['value']));
                        }
                    } catch (Whups_Exception $e) {
                        $changes[] = sprintf(_("New Attachment: %s"), htmlspecialchars($change['value']));
                    }
                    break;
                case 'delete-attachment':
                    $changes[] = sprintf(_("Deleted Attachment: %s"), htmlspecialchars($change['value']));
                    break;
                case 'assign':
                    $changes[] = sprintf(_("Assigned to %s"), Whups::formatUser($change['value'], false, true, true));
                    break;
                case 'unassign':
                    $changes[] = sprintf(_("Taken from %s"), Whups::formatUser($change['value'], false, true, true));
                    break;
                case 'comment':
                    $comment = $change['comment'];
                    $private = !empty($change['private']);
                    if ($comment) {
                        $reply = Horde::link(Horde::url($canUpdate ? 'ticket/update.php' : 'ticket/comment.php')->add(array('id' => $vars->get('ticket_id'), 'transaction' => $transaction))) . _("Reply to this comment") . '</a>';
                    }
                    break;
                case 'queue':
                    $changes[] = sprintf(_("Queue &rArr; %s"), htmlspecialchars($change['label']));
                    break;
                case 'version':
                    $changes[] = sprintf(_("Version &rArr; %s"), htmlspecialchars($change['label']));
                    break;
                case 'type':
                    $changes[] = sprintf(_("Type &rArr; %s"), htmlspecialchars($change['label']));
                    break;
                case 'state':
                    $changes[] = sprintf(_("State &rArr; %s"), htmlspecialchars($change['label']));
                    break;
                case 'priority':
                    $changes[] = sprintf(_("Priority &rArr; %s"), htmlspecialchars($change['label']));
                    break;
                case 'attribute':
                    $changes[] = sprintf(_("%s &rArr; %s"), htmlspecialchars($change['label']), htmlspecialchars($change['human']));
                    break;
                case 'due':
                    if ($change['label']) {
                        $changes[] = sprintf(_("Due &rArr; %s"), strftime($prefs->getValue('date_format'), $change['label']));
                    }
                    break;
            }
        }
        if ($comment) {
            $flowed = new Horde_Text_Flowed($comment, 'UTF-8');
            $flowed->setDelSp(true);
            $comment = $flowed->toFlowed(false);
            $comment = $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($comment, array('text2html', 'simplemarkup', 'highlightquotes'), array(array('parselevel' => Horde_Text_Filter_Text2html::MICRO), array('html' => true), array('hideBlocks' => true)));
            if ($prefs->getValue('autolink_tickets') && $conf['prefs']['autolink_terms']) {
                // Replace existing links by tokens to avoid double linking.
                $comment = preg_replace_callback('/<a.*?<\\/a>/', array($this, '_writeTokens'), $comment);
                $comment = preg_replace_callback('/(' . $conf['prefs']['autolink_terms'] . ')\\s*#?(\\d+)/i', array($this, '_autolink'), $comment);
                $comment = preg_replace_callback('/\\0/', array($this, '_readTokens'), $comment);
            }
            $comment_count++;
            if ($private) {
                $comment_label = Horde::img('locked.png') . sprintf(_("Comment #%d (Private)"), $comment_count);
            } else {
                $comment_label = sprintf(_("Comment #%d"), $comment_count);
            }
            array_unshift($changes, '<a href="#c' . $comment_count . '" id="c' . $comment_count . '">' . $comment_label . '</a>');
        }
        if (count($changes)) {
            // Admins can delete entries.
            $delete_link = '';
            if (Whups::hasPermission($vars->get('queue'), 'queue', Horde_Perms::DELETE)) {
                $delete_link = Horde::url('ticket/delete_history.php')->add(array('transaction' => $transaction, 'id' => $vars->get('ticket_id'), 'url' => Whups::urlFor('ticket', $vars->get('ticket_id'), true)))->link(array('title' => _("Delete entry"), 'onclick' => 'return window.confirm(\'' . addslashes(_("Permanently delete entry?")) . '\');')) . Horde::img('delete.png', _("Delete entry")) . '</a>';
            }
            Horde::startBuffer();
            $class = $private ? 'pc' : 'c';
            ?>
<div id="t<?php 
            echo (int) $transaction;
            ?>
">
<table cellspacing="0" width="100%">
 <tr>
  <td width="20%" class="<?php 
            echo $class;
            ?>
_l nowrap" valign="top"><?php 
            echo strftime($prefs->getValue('date_format') . ' ' . $prefs->getValue('time_format'), $vars->get('timestamp'));
            ?>
</td>
  <td width="20%" class="<?php 
            echo $class;
            ?>
_m" valign="top"><?php 
            echo $vars->get('user_id') ? Whups::formatUser($vars->get('user_id'), false, true, true) : '&nbsp;';
            ?>
</td>
  <td width="30%" class="<?php 
            echo $class;
            ?>
_m" valign="top"><?php 
            echo implode('<br />', $changes);
            ?>
</td>
  <td width="30%" class="<?php 
            echo $class;
            ?>
_r rightAlign" valign="top"><?php 
            if ($comment && !$private) {
                echo $reply . ' ';
            }
            echo $delete_link;
            ?>
</td>
 </tr>
<?php 
            if ($comment) {
                ?>
 <tr><td colspan="4" class="<?php 
                echo $class;
                ?>
_b">
  <div class="comment-body fixed">
   <?php 
                echo $comment;
                ?>
  </div>
 </td></tr>
<?php 
            } else {
                ?>
 <tr><td colspan="4" class="c_b">&nbsp;</td></tr>
<?php 
            }
            ?>
</table>
</div>
<?php 
            $html = Horde::endBuffer();
            return $html;
        }
        return '';
    }
Example #8
0
 /**
  * Sends this message.
  *
  * @param Mail $mailer     A Mail object.
  * @param boolean $resend  If true, the message id and date are re-used;
  *                         If false, they will be updated.
  * @param boolean $flowed  Send message in flowed text format.
  *
  * @throws Horde_Mime_Exception
  */
 public function send($mailer, $resend = false, $flowed = true)
 {
     /* Add mandatory headers if missing. */
     $has_header = $this->_headers->getValue('Message-ID');
     if (!$resend || !$has_header) {
         if ($has_header) {
             $this->_headers->removeHeader('Message-ID');
         }
         $this->_headers->addMessageIdHeader();
     }
     if (!$this->_headers->getValue('User-Agent')) {
         $this->_headers->addUserAgentHeader();
     }
     $has_header = $this->_headers->getValue('Date');
     if (!$resend || !$has_header) {
         if ($has_header) {
             $this->_headers->removeHeader('Date');
         }
         $this->_headers->addHeader('Date', date('r'));
     }
     if (isset($this->_base)) {
         $basepart = $this->_base;
     } else {
         /* Send in flowed format. */
         if ($flowed && !empty($this->_body)) {
             $flowed = new Horde_Text_Flowed($this->_body->getContents(), $this->_body->getCharset());
             $flowed->setDelSp(true);
             $this->_body->setContentTypeParameter('format', 'flowed');
             $this->_body->setContentTypeParameter('DelSp', 'Yes');
             $this->_body->setContents($flowed->toFlowed());
         }
         /* Build mime message. */
         $body = new Horde_Mime_Part();
         if (!empty($this->_body) && !empty($this->_htmlBody)) {
             $body->setType('multipart/alternative');
             $this->_body->setDescription(Horde_Mime_Translation::t("Plaintext Version of Message"));
             $body->addPart($this->_body);
             $this->_htmlBody->setDescription(Horde_Mime_Translation::t("HTML Version of Message"));
             $body->addPart($this->_htmlBody);
         } elseif (!empty($this->_htmlBody)) {
             $body = $this->_htmlBody;
         } elseif (!empty($this->_body)) {
             $body = $this->_body;
         }
         if (count($this->_parts)) {
             $basepart = new Horde_Mime_Part();
             $basepart->setType('multipart/mixed');
             $basepart->isBasePart(true);
             if ($body) {
                 $basepart->addPart($body);
             }
             foreach ($this->_parts as $mime_part) {
                 $basepart->addPart($mime_part);
             }
         } else {
             $basepart = $body;
             $basepart->isBasePart(true);
         }
     }
     $basepart->setHeaderCharset($this->_charset);
     /* Build recipients. */
     $recipients = clone $this->_recipients;
     foreach (array('to', 'cc') as $header) {
         $recipients->add($this->_headers->getOb($header));
     }
     if ($this->_bcc) {
         $recipients->add($this->_bcc);
     }
     /* Trick Horde_Mime_Part into re-generating the message headers. */
     $this->_headers->removeHeader('MIME-Version');
     /* Send message. */
     $recipients->unique();
     $basepart->send($recipients->writeAddress(), $this->_headers, $mailer);
     /* Remember the basepart */
     $this->_base = $basepart;
 }
Example #9
0
 /**
  * Process DOM node.
  *
  * @param DOMDocument $doc  Document node.
  * @param DOMElement $node  Element node.
  *
  * @return string  The plaintext representation.
  */
 protected function _node($doc, $node)
 {
     $out = '';
     if (!empty($this->_params['nestingLimit']) && $this->_nestingLevel > $this->_params['nestingLimit']) {
         $this->_nestingLevel--;
         return;
     }
     $this->_nestingLevel++;
     if ($node->hasChildNodes()) {
         foreach ($node->childNodes as $child) {
             if ($this->_params['callback'] && ($txt = call_user_func($this->_params['callback'], $doc, $child)) !== null) {
                 $out .= $txt;
                 continue;
             }
             if ($child instanceof DOMElement) {
                 switch (Horde_String::lower($child->tagName)) {
                     case 'h1':
                     case 'h2':
                     case 'h3':
                         $out .= "\n\n" . strtoupper($this->_node($doc, $child)) . "\n\n";
                         break;
                     case 'h4':
                     case 'h5':
                     case 'h6':
                         $out .= "\n\n" . ucwords($this->_node($doc, $child)) . "\n\n";
                         break;
                     case 'b':
                     case 'strong':
                         $out .= strtoupper($this->_node($doc, $child));
                         break;
                     case 'u':
                         $out .= '_' . $this->_node($doc, $child) . '_';
                         break;
                     case 'em':
                     case 'i':
                         $out .= '/' . $this->_node($doc, $child) . '/';
                         break;
                     case 'hr':
                         $out .= "\n-------------------------\n";
                         break;
                     case 'ol':
                     case 'ul':
                     case 'dl':
                         ++$this->_indent;
                         $out .= "\n" . $this->_node($doc, $child) . "\n";
                         --$this->_indent;
                         break;
                     case 'p':
                         if ($tmp = $this->_node($doc, $child)) {
                             if (!strspn(substr($out, -2), "\n")) {
                                 $out .= "\n";
                             }
                             if (strlen(trim($tmp))) {
                                 $out .= $tmp . "\n";
                             }
                         }
                         break;
                     case 'table':
                         if ($tmp = $this->_node($doc, $child)) {
                             $out .= "\n\n" . $tmp . "\n\n";
                         }
                         break;
                     case 'tr':
                         $out .= "\n  " . trim($this->_node($doc, $child));
                         break;
                     case 'th':
                         $out .= strtoupper($this->_node($doc, $child)) . " \t";
                         break;
                     case 'td':
                         $out .= $this->_node($doc, $child) . " \t";
                         break;
                     case 'li':
                     case 'dd':
                     case 'dt':
                         $out .= "\n" . str_repeat('  ', $this->_indent) . '* ' . $this->_node($doc, $child);
                         break;
                     case 'a':
                         $out .= $this->_node($doc, $child) . $this->_buildLinkList($doc, $child);
                         break;
                     case 'blockquote':
                         $tmp = trim(preg_replace('/\\s*\\n{3,}/', "\n\n", $this->_node($doc, $child)));
                         if (class_exists('Horde_Text_Flowed')) {
                             $flowed = new Horde_Text_Flowed($tmp, $this->_params['charset']);
                             $flowed->setMaxLength($this->_params['width']);
                             $flowed->setOptLength($this->_params['width']);
                             $tmp = $flowed->toFlowed(true);
                         }
                         if (!strspn(substr($out, -1), " \r\n\t")) {
                             $out .= "\n";
                         }
                         $out .= "\n" . rtrim($tmp) . "\n\n";
                         break;
                     case 'div':
                         $out .= $this->_node($doc, $child) . "\n";
                         break;
                     case 'br':
                         $out .= "\n";
                         break;
                     default:
                         $out .= $this->_node($doc, $child);
                         break;
                 }
             } elseif ($child instanceof DOMText) {
                 $tmp = $child->textContent;
                 $out .= strspn(substr($out, -1), " \r\n\t") ? ltrim($child->textContent) : $child->textContent;
             }
         }
     }
     if (!empty($this->_params['nestingLimit'])) {
         $this->_nestingLevel--;
     }
     return $out;
 }
Example #10
0
 /**
  * Return the body text of the original email from a smart request.
  *
  * @param array $body_data       The body data array of the source msg.
  * @param Horde_Mime_Part $part  The body mime part of the email to send.
  * @param boolean $html          Do we want an html body?
  * @param boolean $flow          Should the body be flowed?
  *
  * @return string  The properly formatted/flowed message body.
  */
 protected function _msgBody(array $body_data, Horde_Mime_Part $part, $html, $flow = false)
 {
     $subtype = $html == true ? 'html' : 'plain';
     $msg = Horde_String::convertCharset($body_data[$subtype]['body'], $body_data[$subtype]['charset'], 'UTF-8');
     trim($msg);
     if (!$html) {
         if ($part->getContentTypeParameter('format') == 'flowed') {
             $flowed = new Horde_Text_Flowed($msg, 'UTF-8');
             if (Horde_String::lower($part->getContentTypeParameter('delsp')) == 'yes') {
                 $flowed->setDelSp(true);
             }
             $flowed->setMaxLength(0);
             $msg = $flowed->toFixed(false);
         } else {
             // If not flowed, remove padding at eol
             $msg = preg_replace("/\\s*\n/U", "\n", $msg);
         }
         if ($flow) {
             $flowed = new Horde_Text_Flowed($msg, 'UTF-8');
             $msg = $flowed->toFlowed(true);
         }
     }
     return $msg;
 }
Example #11
0
 /**
  * Generate the MDN according to the specifications listed in RFC
  * 3798 [3].
  *
  * @param boolean $action   Was this MDN type a result of a manual
  *                          action on part of the user?
  * @param boolean $sending  Was this MDN sent as a result of a manual
  *                          action on part of the user?
  * @param string $type      The type of action performed by the user.
  *                          Per RFC 3798 [3.2.6.2] the following types are
  *                          valid:
  *                            - deleted
  *                            - displayed
  * @param string $name      The name of the local server.
  * @param Mail $mailer      A Mail driver.
  * @param array $opts       Additional options:
  *   - charset: (string) Default charset.
  *              DEFAULT: NONE
  *   - from_addr: (string) From address.
  *                DEFAULT: NONE
  * @param array $mod        The list of modifications. Per RFC 3798
  *                          [3.2.6.3] the following modifications are
  *                          valid:
  *                            - error
  * @param array $err        If $mod is 'error', the additional
  *                          information to provide. Key is the type of
  *                          modification, value is the text.
  *
  * @throws Horde_Mime_Exception
  */
 public function generate($action, $sending, $type, $name, $mailer, array $opts = array(), array $mod = array(), array $err = array())
 {
     $opts = array_merge(array('charset' => null, 'from_addr' => null), $opts);
     $to = $this->getMdnReturnAddr();
     $ua = $this->_headers->getUserAgent();
     $orig_recip = $this->_headers->getValue('Original-Recipient');
     if (!empty($orig_recip) && is_array($orig_recip)) {
         $orig_recip = $orig_recip[0];
     }
     $msg_id = $this->_headers->getValue('Message-ID');
     /* Create the Disposition field now (RFC 3798 [3.2.6]). */
     $dispo = 'Disposition: ' . ($action ? 'manual-action' : 'automatic-action') . '/' . ($sending ? 'MDN-sent-manually' : 'MDN-sent-automatically') . '; ' . $type;
     if (!empty($mod)) {
         $dispo .= '/' . implode(', ', $mod);
     }
     /* Set up the mail headers. */
     $msg_headers = new Horde_Mime_Headers();
     $msg_headers->addMessageIdHeader();
     $msg_headers->addUserAgentHeader($ua);
     $msg_headers->addHeader('Date', date('r'));
     if ($opts['from_addr']) {
         $msg_headers->addHeader('From', $opts['from_addr']);
     }
     $msg_headers->addHeader('To', $this->getMdnReturnAddr());
     $msg_headers->addHeader('Subject', Horde_Mime_Translation::t("Disposition Notification"));
     /* MDNs are a subtype of 'multipart/report'. */
     $msg = new Horde_Mime_Part();
     $msg->setType('multipart/report');
     $msg->setContentTypeParameter('report-type', 'disposition-notification');
     /* The first part is a human readable message. */
     $part_one = new Horde_Mime_Part();
     $part_one->setType('text/plain');
     $part_one->setCharset($opts['charset']);
     if ($type == 'displayed') {
         $contents = sprintf(Horde_Mime_Translation::t("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers->getValue('Date'), $this->_headers->getValue('To'), $this->_headers->getValue('Subject'));
         $flowed = new Horde_Text_Flowed($contents, $opts['charset']);
         $flowed->setDelSp(true);
         $part_one->setContentTypeParameter('format', 'flowed');
         $part_one->setContentTypeParameter('DelSp', 'Yes');
         $part_one->setContents($flowed->toFlowed());
     }
     // TODO: Messages for other notification types.
     $msg->addPart($part_one);
     /* The second part is a machine-parseable description. */
     $part_two = new Horde_Mime_Part();
     $part_two->setType('message/disposition-notification');
     $part_two_text = array('Reporting-UA: ' . $name . '; ' . $ua . "\n");
     if (!empty($orig_recip)) {
         $part_two_text[] = 'Original-Recipient: rfc822;' . $orig_recip . "\n";
     }
     if ($opts['from_addr']) {
         $part_two_text[] = 'Final-Recipient: rfc822;' . $opts['from_addr'] . "\n";
     }
     if (!empty($msg_id)) {
         $part_two_text[] = 'Original-Message-ID: rfc822;' . $msg_id . "\n";
     }
     $part_two_text[] = $dispo . "\n";
     if (in_array('error', $mod) && isset($err['error'])) {
         $part_two_text[] = 'Error: ' . $err['error'] . "\n";
     }
     $part_two->setContents($part_two_text);
     $msg->addPart($part_two);
     /* The third part is the text of the original message.  RFC 3798 [3]
      * allows us to return only a portion of the entire message - this
      * is left up to the user. */
     $part_three = new Horde_Mime_Part();
     $part_three->setType('message/rfc822');
     $part_three_text = array($this->_headers->toString());
     if (!empty($this->_msgtext)) {
         $part_three_text[] = $part_three->getEOL() . $this->_msgtext;
     }
     $part_three->setContents($part_three_text);
     $msg->addPart($part_three);
     return $msg->send($to, $msg_headers, $mailer);
 }
Example #12
0
 /**
  * Regenerates body text for use in the compose screen from IMAP data.
  *
  * @param IMP_Contents $contents  An IMP_Contents object.
  * @param array $options          Additional options:
  * <ul>
  *  <li>html: (boolean) Return text/html part, if available.</li>
  *  <li>imp_msg: (integer) If non-empty, the message data was created by
  *               IMP. Either:
  *   <ul>
  *    <li>self::COMPOSE</li>
  *    <li>self::FORWARD</li>
  *    <li>self::REPLY</li>
  *   </ul>
  *  </li>
  *  <li>replylimit: (boolean) Enforce length limits?</li>
  *  <li>toflowed: (boolean) Do flowed conversion?</li>
  * </ul>
  *
  * @return mixed  Null if bodypart not found, or array with the following
  *                keys:
  *   - charset: (string) The guessed charset to use.
  *   - flowed: (Horde_Text_Flowed) A flowed object, if the text is flowed.
  *             Otherwise, null.
  *   - id: (string) The MIME ID of the bodypart.
  *   - mode: (string) Either 'text' or 'html'.
  *   - text: (string) The body text.
  */
 protected function _getMessageText($contents, array $options = array())
 {
     global $conf, $injector, $notification, $prefs;
     $body_id = null;
     $mode = 'text';
     $options = array_merge(array('imp_msg' => self::COMPOSE), $options);
     if (!empty($options['html']) && self::canHtmlCompose() && ($body_id = $contents->findBody('html')) !== null) {
         $mime_message = $contents->getMIMEMessage();
         switch ($mime_message->getPrimaryType()) {
             case 'multipart':
                 if ($body_id != '1' && $mime_message->getSubType() == 'mixed' && ($id_ob = new Horde_Mime_Id('1')) && !$id_ob->isChild($body_id)) {
                     $body_id = null;
                 } else {
                     $mode = 'html';
                 }
                 break;
             default:
                 if (strval($body_id) != '1') {
                     $body_id = null;
                 } else {
                     $mode = 'html';
                 }
                 break;
         }
     }
     if (is_null($body_id)) {
         $body_id = $contents->findBody();
         if (is_null($body_id)) {
             return null;
         }
     }
     if (!($part = $contents->getMimePart($body_id))) {
         return null;
     }
     $type = $part->getType();
     $part_charset = $part->getCharset();
     $msg = Horde_String::convertCharset($part->getContents(), $part_charset, 'UTF-8');
     /* Enforce reply limits. */
     if (!empty($options['replylimit']) && !empty($conf['compose']['reply_limit'])) {
         $limit = $conf['compose']['reply_limit'];
         if (Horde_String::length($msg) > $limit) {
             $msg = Horde_String::substr($msg, 0, $limit) . "\n" . _("[Truncated Text]");
         }
     }
     if ($mode == 'html') {
         $dom = $injector->getInstance('Horde_Core_Factory_TextFilter')->filter($msg, 'Xss', array('charset' => $this->charset, 'return_dom' => true, 'strip_style_attributes' => false));
         /* If we are replying to a related part, and this part refers
          * to local message parts, we need to move those parts into this
          * message (since the original message may disappear during the
          * compose process). */
         if ($related_part = $contents->findMimeType($body_id, 'multipart/related')) {
             $this->_setMetadata('related_contents', $contents);
             $related_ob = new Horde_Mime_Related($related_part);
             $related_ob->cidReplace($dom, array($this, '_getMessageTextCallback'), $part_charset);
             $this->_setMetadata('related_contents', null);
         }
         /* Convert any Data URLs to attachments. */
         $xpath = new DOMXPath($dom->dom);
         foreach ($xpath->query('//*[@src]') as $val) {
             $data_url = new Horde_Url_Data($val->getAttribute('src'));
             if (strlen($data_url->data)) {
                 $data_part = new Horde_Mime_Part();
                 $data_part->setContents($data_url->data);
                 $data_part->setType($data_url->type);
                 try {
                     $atc = $this->addAttachmentFromPart($data_part);
                     $val->setAttribute('src', $atc->viewUrl());
                     $this->addRelatedAttachment($atc, $val, 'src');
                 } catch (IMP_Compose_Exception $e) {
                     $notification->push($e, 'horde.warning');
                 }
             }
         }
         $msg = $dom->returnBody();
     } elseif ($type == 'text/html') {
         $msg = $injector->getInstance('Horde_Core_Factory_TextFilter')->filter($msg, 'Html2text');
         $type = 'text/plain';
     }
     /* Always remove leading/trailing whitespace. The data in the
      * message body is not intended to be the exact representation of the
      * original message (use forward as message/rfc822 part for that). */
     $msg = trim($msg);
     if ($type == 'text/plain') {
         if ($prefs->getValue('reply_strip_sig') && ($pos = strrpos($msg, "\n-- ")) !== false) {
             $msg = rtrim(substr($msg, 0, $pos));
         }
         /* Remove PGP armored text. */
         $pgp = $injector->getInstance('Horde_Crypt_Pgp_Parse')->parseToPart($msg);
         if (!is_null($pgp)) {
             $msg = '';
             $pgp->buildMimeIds();
             foreach ($pgp->partIterator() as $val) {
                 if ($val->getPrimaryType() === 'text') {
                     $msg .= $val->getContents();
                 }
             }
         }
         if ($part->getContentTypeParameter('format') == 'flowed') {
             $flowed = new Horde_Text_Flowed($msg, 'UTF-8');
             if (Horde_String::lower($part->getContentTypeParameter('delsp')) == 'yes') {
                 $flowed->setDelSp(true);
             }
             $flowed->setMaxLength(0);
             $msg = $flowed->toFixed(false);
         } else {
             /* If the input is *not* in flowed format, make sure there is
              * no padding at the end of lines. */
             $msg = preg_replace("/\\s*\n/U", "\n", $msg);
         }
         if (isset($options['toflowed'])) {
             $flowed = new Horde_Text_Flowed($msg, 'UTF-8');
             $msg = $options['toflowed'] ? $flowed->toFlowed(true) : $flowed->toFlowed(false, array('nowrap' => true));
         }
     }
     if (strcasecmp($part->getCharset(), 'windows-1252') === 0) {
         $part_charset = 'ISO-8859-1';
     }
     return array('charset' => $part_charset, 'flowed' => isset($flowed) ? $flowed : null, 'id' => $body_id, 'mode' => $mode, 'text' => $msg);
 }
Example #13
0
File: Mail.php Project: horde/horde
 /**
  * Return the body text of the original email from a smart request.
  *
  * @param array $body_data       The body data array of the source msg.
  * @param Horde_Mime_Part $part  The body mime part of the email to send.
  * @param boolean $html          Do we want an html body?
  * @param boolean $flow          Should the body be flowed?
  *
  * @return string  The properly formatted/flowed message body.
  */
 protected function _msgBody(array $body_data, Horde_Mime_Part $part, $html, $flow = false)
 {
     $subtype = $html == true ? 'html' : 'plain';
     $msg = Horde_String::convertCharset((string) $body_data[$subtype]['body'], $body_data[$subtype]['charset'], 'UTF-8');
     if (!$html) {
         if ($part->getContentTypeParameter('format') == 'flowed') {
             $flowed = new Horde_Text_Flowed($msg, 'UTF-8');
             if (Horde_String::lower($part->getContentTypeParameter('delsp')) == 'yes') {
                 $flowed->setDelSp(true);
             }
             $flowed->setMaxLength(0);
             $msg = $flowed->toFixed(false);
         } else {
             // If not flowed, remove padding at eol
             $msg = preg_replace("/\\s*\n/U", "\n", $msg);
         }
         if ($flow) {
             $flowed = new Horde_Text_Flowed($msg, 'UTF-8');
             $msg = $flowed->toFlowed(true);
         }
     } else {
         // This filter requires the tidy extenstion.
         if (Horde_Util::extensionExists('tidy')) {
             return Horde_Text_Filter::filter($msg, 'Cleanhtml', array('body_only' => true));
         } else {
             // If no tidy, use Horde_Dom.
             $dom = new Horde_Domhtml($msg, 'UTF-8');
             return $dom->returnBody();
         }
     }
     return $msg;
 }
Example #14
0
 /**
  * Copies or moves a list of messages to a tasklist or notepad.
  * Handles search and Trash mailboxes.
  *
  * @param string $list          The list in which the task or note will be
  *                              created.
  * @param string $action        Either 'copy' or 'move'.
  * @param IMP_Indices $indices  An indices object.
  * @param string $type          The object type to create ('note' or
  *                              'task').
  */
 protected function _createTasksOrNotes($list, $action, IMP_Indices $indices, $type)
 {
     global $injector, $registry, $notification;
     foreach ($indices as $ob) {
         foreach ($ob->uids as $uid) {
             /* Fetch the message contents. */
             $imp_contents = $injector->getInstance('IMP_Factory_Contents')->create($ob->mbox->getIndicesOb($uid));
             /* Fetch the message headers. */
             $imp_headers = $imp_contents->getHeader();
             $subject = $imp_headers->getValue('subject');
             /* Re-flow the message for prettier formatting. */
             $body_part = $imp_contents->getMIMEPart($imp_contents->findBody());
             $flowed = new Horde_Text_Flowed($body_part->getContents());
             if ($body_part->getContentTypeParameter('delsp') == 'yes') {
                 $flowed->setDelSp(true);
             }
             $body = $flowed->toFlowed(false);
             /* Convert to current charset */
             /* TODO: When Horde_Icalendar supports setting of charsets
              * we need to set it there instead of relying on the fact
              * that both Nag and IMP use the same charset. */
             $body = Horde_String::convertCharset($body, $body_part->getCharset(), 'UTF-8');
             /* Create a new iCalendar. */
             $vCal = new Horde_Icalendar();
             $vCal->setAttribute('PRODID', '-//The Horde Project//IMP ' . $registry->getVersion() . '//EN');
             $vCal->setAttribute('METHOD', 'PUBLISH');
             switch ($type) {
                 case 'task':
                     /* Create a new vTodo object using this message's
                      * contents. */
                     $vTodo = Horde_Icalendar::newComponent('vtodo', $vCal);
                     $vTodo->setAttribute('SUMMARY', $subject);
                     $vTodo->setAttribute('DESCRIPTION', $body);
                     $vTodo->setAttribute('PRIORITY', '3');
                     /* Get the list of editable tasklists. */
                     try {
                         $lists = $registry->call('tasks/listTasklists', array(false, Horde_Perms::EDIT));
                     } catch (Horde_Exception $e) {
                         $lists = null;
                         $notification->push($e);
                     }
                     /* Attempt to add the new vTodo item to the requested
                      * tasklist. */
                     try {
                         $res = $registry->call('tasks/import', array($vTodo, 'text/calendar', $list));
                     } catch (Horde_Exception $e) {
                         $res = null;
                         $notification->push($e);
                     }
                     break;
                 case 'note':
                     /* Create a new vNote object using this message's
                      * contents. */
                     $vNote = Horde_Icalendar::newComponent('vnote', $vCal);
                     $vNote->setAttribute('BODY', $subject . "\n" . $body);
                     /* Get the list of editable notepads. */
                     try {
                         $lists = $registry->call('notes/listNotepads', array(false, Horde_Perms::EDIT));
                     } catch (Horde_Exception $e) {
                         $lists = null;
                         $notification->push($e);
                     }
                     /* Attempt to add the new vNote item to the requested
                      * notepad. */
                     try {
                         $res = $registry->call('notes/import', array($vNote, 'text/x-vnote', $list));
                     } catch (Horde_Exception $e) {
                         $res = null;
                         $notification->push($e);
                     }
                     break;
             }
             if (!is_null($res)) {
                 if (!$res) {
                     switch ($type) {
                         case 'task':
                             $notification->push(_("An unknown error occured while creating the new task."), 'horde.error');
                             break;
                         case 'note':
                             $notification->push(_("An unknown error occured while creating the new note."), 'horde.error');
                             break;
                     }
                 } elseif (!is_null($lists)) {
                     $name = '"' . htmlspecialchars($subject) . '"';
                     /* Attempt to convert the object name into a
                      * hyperlink. */
                     try {
                         switch ($type) {
                             case 'task':
                                 $link = $registry->link('tasks/show', array('uid' => $res));
                                 break;
                             case 'note':
                                 $link = $registry->hasMethod('notes/show') ? $registry->link('notes/show', array('uid' => $res)) : false;
                                 break;
                         }
                         if ($link) {
                             $name = sprintf('<a href="%s">%s</a>', Horde::url($link), $name);
                         }
                         $notification->push(sprintf(_("%s was successfully added to \"%s\"."), $name, htmlspecialchars($lists[$list]->get('name'))), 'horde.success', array('content.raw'));
                     } catch (Horde_Exception $e) {
                     }
                 }
             }
         }
     }
     /* Delete the original messages if this is a "move" operation. */
     if ($action == 'move') {
         $this->delete($indices);
     }
 }