Set the language(s) of this part.
public setLanguage ( mixed $lang ) | ||
$lang | mixed | A language string, or an array of language strings. |
/** * Recursively parse BODYSTRUCTURE data from a FETCH return (see * RFC 3501 [7.4.2]). * * @param Horde_Imap_Client_Tokenize $data Data returned from the server. * * @return Horde_Mime_Part Mime part object. */ protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data) { $ob = new Horde_Mime_Part(); // If index 0 is an array, this is a multipart part. if (($entry = $data->next()) === true) { do { $ob->addPart($this->_parseBodystructure($data)); } while (($entry = $data->next()) === true); // The subpart type. $ob->setType('multipart/' . $entry); // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. // This is parameter information. if (($tmp = $data->next()) === false) { return $ob; } elseif ($tmp === true) { foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } } else { $ob->setType($entry . '/' . $data->next()); if ($data->next() === true) { foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } if (!is_null($tmp = $data->next())) { $ob->setContentId($tmp); } if (!is_null($tmp = $data->next())) { $ob->setDescription(Horde_Mime::decode($tmp)); } if (!is_null($tmp = $data->next())) { $ob->setTransferEncoding($tmp); } $ob->setBytes($data->next()); // If the type is 'message/rfc822' or 'text/*', several extra // fields are included switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { if ($data->next() === true) { // Ignore: envelope $data->flushIterator(false); } if ($data->next() === true) { $ob->addPart($this->_parseBodystructure($data)); } $data->next(); // Ignore: lines } break; case 'text': $data->next(); // Ignore: lines break; } // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. // Ignore: MD5 if ($data->next() === false) { return $ob; } } // This is disposition information if (($tmp = $data->next()) === false) { return $ob; } elseif ($tmp === true) { $ob->setDisposition($data->next()); if ($data->next() === true) { foreach ($this->_parseStructureParams($data, 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } $data->next(); } // This is language information. It is either a single value or a list // of values. if (($tmp = $data->next()) === false) { return $ob; } elseif (!is_null($tmp)) { $ob->setLanguage($tmp === true ? $data->flushIterator() : $tmp); } // Ignore location (RFC 2557) and consume closing paren. $data->flushIterator(false); return $ob; }
/** * Recursively parse BODYSTRUCTURE data from a FETCH return (see * RFC 3501 [7.4.2]). * * @param array $data The tokenized information from the server. * * @return array The array of bodystructure information. */ protected function _parseStructure($data) { $ob = new Horde_Mime_Part(); // If index 0 is an array, this is a multipart part. if (is_array($data[0])) { // Keep going through array values until we find a non-array. for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) { if (!is_array($data[$i])) { break; } $ob->addPart($this->_parseStructure($data[$i])); } // The first string entry after an array entry gives us the // subpart type. $ob->setType('multipart/' . $data[$i]); // After the subtype is further extension information. This // information MAY not appear for BODYSTRUCTURE requests. // This is parameter information. if (isset($data[++$i]) && is_array($data[$i])) { foreach ($this->_parseStructureParams($data[$i], 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } // This is disposition information. if (isset($data[++$i]) && is_array($data[$i])) { $ob->setDisposition($data[$i][0]); foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } // This is language information. It is either a single value or // a list of values. if (isset($data[++$i])) { $ob->setLanguage($data[$i]); } // Ignore: location (RFC 2557) // There can be further information returned in the future, but // for now we are done. } else { $ob->setType($data[0] . '/' . $data[1]); foreach ($this->_parseStructureParams($data[2], 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } if ($data[3] !== null) { $ob->setContentId($data[3]); } if ($data[4] !== null) { $ob->setDescription(Horde_Mime::decode($data[4])); } if ($data[5] !== null) { $ob->setTransferEncoding($data[5]); } if ($data[6] !== null) { $ob->setBytes($data[6]); } // If the type is 'message/rfc822' or 'text/*', several extra // fields are included switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { // Ignore: envelope $ob->addPart($this->_parseStructure($data[8])); // Ignore: lines $i = 10; } else { $i = 7; } break; case 'text': // Ignore: lines $i = 8; break; default: $i = 7; break; } // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. // Ignore: MD5 // This is disposition information if (isset($data[++$i]) && is_array($data[$i])) { $ob->setDisposition($data[$i][0]); foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } // This is language information. It is either a single value or // a list of values. if (isset($data[++$i])) { $ob->setLanguage($data[$i]); } // Ignore: location (RFC 2557) } return $ob; }
/** * Recursively parse BODYSTRUCTURE data from a FETCH return (see * RFC 3501 [7.4.2]). * * @param Horde_Imap_Client_Tokenize $data Data returned from the server. * * @return array The array of bodystructure information. */ protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data) { $ob = new Horde_Mime_Part(); // If index 0 is an array, this is a multipart part. if (is_object($entry = $data->rewind())) { // Keep going through array values until we find a non-array. do { $ob->addPart($this->_parseBodystructure($entry)); } while (is_object($entry = $data->next())); // The first string entry after an array entry gives us the // subpart type. $ob->setType('multipart/' . $entry); // After the subtype is further extension information. This // information MAY not appear for BODYSTRUCTURE requests. // This is parameter information. if (is_object($tmp = $data->next())) { foreach ($this->_parseStructureParams($tmp, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } } else { $ob->setType($entry . '/' . $data->next()); if (is_object($tmp = $data->next())) { foreach ($this->_parseStructureParams($tmp, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } if (!is_null($tmp = $data->next())) { $ob->setContentId($tmp); } if (!is_null($tmp = $data->next())) { $ob->setDescription(Horde_Mime::decode($tmp)); } if (!is_null($tmp = $data->next())) { $ob->setTransferEncoding($tmp); } $ob->setBytes($data->next()); // If the type is 'message/rfc822' or 'text/*', several extra // fields are included switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { $data->next(); // Ignore: envelope $ob->addPart($this->_parseBodystructure($data->next())); $data->next(); // Ignore: lines } break; case 'text': $data->next(); // Ignore: lines break; } // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. $data->next(); // Ignore: MD5 } // This is disposition information if (is_object($tmp = $data->next())) { $ob->setDisposition($tmp->rewind()); foreach ($this->_parseStructureParams($tmp->next(), 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } // This is language information. It is either a single value or a list // of values. if (($tmp = $data->next()) !== false) { $ob->setLanguage($tmp); } $data->next(); // Ignore: location (RFC 2557) return $ob; }
public function testNullCharactersNotAllowedInMimeHeaderData() { $part = new Horde_Mime_Part(); $part->setType("text/plain"); $this->assertEquals('text/plain', $part->getType()); $part->setDisposition("inline"); $this->assertEquals('inline', $part->getDisposition()); $part->setDispositionParameter('size', '123' . "" . '456'); $this->assertEquals(123456, $part->getDispositionParameter('size')); $part->setDispositionParameter('foo', "foobar"); $this->assertEquals('foobar', $part->getDispositionParameter('foo')); $part->setCharset("utf-8"); $this->assertEquals('utf-8', $part->getCharset()); $part->setName("foobar"); $this->assertEquals('foobar', $part->getName()); $this->assertEquals('foobar', $part->getDispositionParameter('filename')); $this->assertEquals('foobar', $part->getContentTypeParameter('name')); $part->setLanguage("en"); $this->assertEquals(array('en'), $part->getLanguage()); $part->setLanguage(array("en", "de")); $this->assertEquals(array('en', 'de'), $part->getLanguage()); $part->setDuration('123' . "" . '456'); $this->assertEquals(123456, $part->getDuration()); $part->setBytes('123' . "" . '456'); $this->assertEquals(123456, $part->getBytes()); $part->setDescription("foobar"); $this->assertEquals('foobar', $part->getDescription()); $part->setContentTypeParameter('foo', "foobar"); $this->assertEquals('foobar', $part->getContentTypeParameter('foo')); $part->setContentId("foobar"); $this->assertEquals('foobar', $part->getContentId()); }
/** * Save message to sent-mail mailbox, if configured to do so. * * @param Horde_Mime_Headers $headers Headers object. * @param Horde_Mime_Part $save_msg Message data to save. * @param Horde_Mail_Rfc822_List $recips Recipient list. * @param array $opts See buildAndSendMessage() */ protected function _saveToSentMail(Horde_Mime_Headers $headers, Horde_Mime_Part $save_msg, Horde_Mail_Rfc822_List $recips, $opts) { global $injector, $language, $notification, $prefs; if (empty($opts['sent_mail']) || $prefs->isLocked('save_sent_mail') && !$prefs->getValue('save_sent_mail') || !$prefs->isLocked('save_sent_mail') && empty($opts['save_sent'])) { return; } $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create(); /* If message contains EAI addresses, we need to verify that the IMAP * server can handle this data in order to save. */ foreach ($recips as $val) { if ($val->eai) { if ($imp_imap->client_ob->capability->query('UTF8', 'ACCEPT')) { break; } $notification->push(sprintf(_("Message sent successfully, but not saved to %s."), $sent_mail->display)); return; } } /* Strip attachments if requested. */ if (!empty($opts['strip_attachments'])) { $save_msg->buildMimeIds(); /* Don't strip any part if this is a text message with both * plaintext and HTML representation. */ if ($save_msg->getType() != 'multipart/alternative') { for ($i = 2;; ++$i) { if (!($oldPart = $save_msg[$i])) { break; } $replace_part = new Horde_Mime_Part(); $replace_part->setType('text/plain'); $replace_part->setCharset($this->charset); $replace_part->setLanguage($language); $replace_part->setContents('[' . _("Attachment stripped: Original attachment type") . ': "' . $oldPart->getType() . '", ' . _("name") . ': "' . $oldPart->getName(true) . '"]'); $save_msg[$i] = $replace_part; } } } /* Generate the message string. */ $fcc = $save_msg->toString(array('defserver' => $imp_imap->config->maildomain, 'headers' => $headers, 'stream' => true)); /* Make sure sent mailbox is created. */ $sent_mail = IMP_Mailbox::get($opts['sent_mail']); $sent_mail->create(); $flags = array(Horde_Imap_Client::FLAG_SEEN, Horde_Imap_Client::FLAG_MDNSENT); try { $imp_imap->append($sent_mail, array(array('data' => $fcc, 'flags' => $flags))); } catch (IMP_Imap_Exception $e) { $notification->push(sprintf(_("Message sent successfully, but not saved to %s."), $sent_mail->display)); } }
/** * Builds and sends a MIME message. * * @param string $body The message body. * @param array $header List of message headers. * @param IMP_Prefs_Identity $identity The Identity object for the sender * of this message. * @param array $opts An array of options w/the * following keys: * - encrypt: (integer) A flag whether to encrypt or sign the message. * One of: * - IMP_Crypt_Pgp::ENCRYPT</li> * - IMP_Crypt_Pgp::SIGNENC</li> * - IMP_Crypt_Smime::ENCRYPT</li> * - IMP_Crypt_Smime::SIGNENC</li> * - html: (boolean) Whether this is an HTML message. * DEFAULT: false * - pgp_attach_pubkey: (boolean) Attach the user's PGP public key to the * message? * - priority: (string) The message priority ('high', 'normal', 'low'). * - save_sent: (boolean) Save sent mail? * - sent_mail: (IMP_Mailbox) The sent-mail mailbox (UTF-8). * - strip_attachments: (bool) Strip attachments from the message? * - signature: (string) The message signature. * - readreceipt: (boolean) Add return receipt headers? * - useragent: (string) The User-Agent string to use. * - vcard_attach: (string) Attach the user's vCard (value is name to * display as vcard filename). * * @throws Horde_Exception * @throws IMP_Compose_Exception * @throws IMP_Compose_Exception_Address * @throws IMP_Exception */ public function buildAndSendMessage($body, $header, IMP_Prefs_Identity $identity, array $opts = array()) { global $conf, $injector, $notification, $prefs, $registry, $session; /* We need at least one recipient & RFC 2822 requires that no 8-bit * characters can be in the address fields. */ $recip = $this->recipientList($header); if (!count($recip['list'])) { if ($recip['has_input']) { throw new IMP_Compose_Exception(_("Invalid e-mail address.")); } throw new IMP_Compose_Exception(_("Need at least one message recipient.")); } $header = array_merge($header, $recip['header']); /* Check for correct identity usage. */ if (!$this->getMetadata('identity_check') && count($recip['list']) === 1) { $identity_search = $identity->getMatchingIdentity($recip['list'], false); if (!is_null($identity_search) && $identity->getDefault() != $identity_search) { $this->_setMetadata('identity_check', true); $e = new IMP_Compose_Exception(_("Recipient address does not match the currently selected identity.")); $e->tied_identity = $identity_search; throw $e; } } /* Check body size of message. */ $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create(); if (!$imp_imap->accessCompose(IMP_Imap::ACCESS_COMPOSE_BODYSIZE, strlen($body))) { Horde::permissionDeniedError('imp', 'max_bodysize'); throw new IMP_Compose_Exception(sprintf(_("Your message body has exceeded the limit by body size by %d characters."), strlen($body) - $imp_imap->max_compose_bodysize)); } $from = new Horde_Mail_Rfc822_Address($header['from']); if (is_null($from->host)) { $from->host = $imp_imap->config->maildomain; } /* Prepare the array of messages to send out. May be more * than one if we are encrypting for multiple recipients or * are storing an encrypted message locally. */ $encrypt = empty($opts['encrypt']) ? 0 : $opts['encrypt']; $send_msgs = array(); $msg_options = array('encrypt' => $encrypt, 'html' => !empty($opts['html']), 'identity' => $identity, 'pgp_attach_pubkey' => !empty($opts['pgp_attach_pubkey']) && $prefs->getValue('use_pgp') && $prefs->getValue('pgp_public_key'), 'signature' => is_null($opts['signature']) ? $identity : $opts['signature'], 'vcard_attach' => !empty($opts['vcard_attach']) && $registry->hasMethod('contacts/ownVCard') ? (strlen($opts['vcard_attach']) ? $opts['vcard_attach'] : 'vcard') . '.vcf' : null); /* Must encrypt & send the message one recipient at a time. */ if ($prefs->getValue('use_smime') && in_array($encrypt, array(IMP_Crypt_Smime::ENCRYPT, IMP_Crypt_Smime::SIGNENC))) { foreach ($recip['list'] as $val) { $list_ob = new Horde_Mail_Rfc822_List($val); $send_msgs[] = array('base' => $this->_createMimeMessage($list_ob, $body, $msg_options), 'recipients' => $list_ob); } /* Must target the encryption for the sender before saving message * in sent-mail. */ $save_msg = $this->_createMimeMessage(IMP::parseAddressList($header['from']), $body, $msg_options); } else { /* Can send in clear-text all at once, or PGP can encrypt * multiple addresses in the same message. */ $msg_options['from'] = $from; $save_msg = $this->_createMimeMessage($recip['list'], $body, $msg_options); $send_msgs[] = array('base' => $save_msg, 'recipients' => $recip['list']); } /* Initalize a header object for the outgoing message. */ $headers = $this->_prepareHeaders($header, $opts); /* Add a Received header for the hop from browser to server. */ $headers->addReceivedHeader(array('dns' => $injector->getInstance('Net_DNS2_Resolver'), 'server' => $conf['server']['name'])); /* Add Reply-To header. */ if (!empty($header['replyto']) && $header['replyto'] != $from->bare_address) { $headers->addHeader('Reply-to', $header['replyto']); } /* Add the 'User-Agent' header. */ if (empty($opts['useragent'])) { $headers->setUserAgent('Internet Messaging Program (IMP) ' . $registry->getVersion()); } else { $headers->setUserAgent($opts['useragent']); } $headers->addUserAgentHeader(); /* Add preferred reply language(s). */ if ($lang = @unserialize($prefs->getValue('reply_lang'))) { $headers->addHeader('Accept-Language', implode(',', $lang)); } /* Send the messages out now. */ $sentmail = $injector->getInstance('IMP_Sentmail'); foreach ($send_msgs as $val) { switch (intval($this->replyType(true))) { case self::REPLY: $senttype = IMP_Sentmail::REPLY; break; case self::FORWARD: $senttype = IMP_Sentmail::FORWARD; break; case self::REDIRECT: $senttype = IMP_Sentmail::REDIRECT; break; default: $senttype = IMP_Sentmail::NEWMSG; break; } try { $this->_prepSendMessageAssert($val['recipients'], $headers, $val['base']); $this->sendMessage($val['recipients'], $headers, $val['base']); /* Store history information. */ $msg_id = new Horde_Mail_Rfc822_Identification($headers->getValue('message-id')); $sentmail->log($senttype, reset($msg_id->ids), $val['recipients'], true); } catch (IMP_Compose_Exception_Address $e) { throw $e; } catch (IMP_Compose_Exception $e) { /* Unsuccessful send. */ if ($e->log()) { $msg_id = new Horde_Mail_Rfc822_Identification($headers->getValue('message-id')); $sentmail->log($senttype, reset($msg_id->ids), $val['recipients'], false); } throw new IMP_Compose_Exception(sprintf(_("There was an error sending your message: %s"), $e->getMessage())); } } $recipients = strval($recip['list']); if ($this->_replytype) { /* Log the reply. */ if ($indices = $this->getMetadata('indices')) { switch ($this->_replytype) { case self::FORWARD: case self::FORWARD_ATTACH: case self::FORWARD_BODY: case self::FORWARD_BOTH: $log = new IMP_Maillog_Log_Forward($recipients); break; case self::REPLY: case self::REPLY_SENDER: $log = new IMP_Maillog_Log_Reply(); break; case IMP_Compose::REPLY_ALL: $log = new IMP_Maillog_Log_Replyall(); break; case IMP_Compose::REPLY_LIST: $log = new IMP_Maillog_Log_Replylist(); break; } $log_msgs = array(); foreach ($indices as $val) { foreach ($val->uids as $val2) { $log_msgs[] = new IMP_Maillog_Message(new IMP_Indices($val->mbox, $val2)); } } $injector->getInstance('IMP_Maillog')->log($log_msgs, $log); } $imp_message = $injector->getInstance('IMP_Message'); $reply_uid = new IMP_Indices($this); switch ($this->replyType(true)) { case self::FORWARD: /* Set the Forwarded flag, if possible, in the mailbox. * See RFC 5550 [5.9] */ $imp_message->flag(array('add' => array(Horde_Imap_Client::FLAG_FORWARDED)), $reply_uid); break; case self::REPLY: /* Make sure to set the IMAP reply flag and unset any * 'flagged' flag. */ $imp_message->flag(array('add' => array(Horde_Imap_Client::FLAG_ANSWERED), 'remove' => array(Horde_Imap_Client::FLAG_FLAGGED)), $reply_uid); break; } } Horde::log(sprintf("Message sent to %s from %s (%s)", $recipients, $registry->getAuth(), $session->get('horde', 'auth/remoteAddr')), 'INFO'); /* Should we save this message in the sent mail mailbox? */ if (!empty($opts['sent_mail']) && (!$prefs->isLocked('save_sent_mail') && !empty($opts['save_sent']) || $prefs->isLocked('save_sent_mail') && $prefs->getValue('save_sent_mail'))) { /* Keep Bcc: headers on saved messages. */ if (count($header['bcc'])) { $headers->addHeader('Bcc', $header['bcc']); } /* Strip attachments if requested. */ if (!empty($opts['strip_attachments'])) { $save_msg->buildMimeIds(); /* Don't strip any part if this is a text message with both * plaintext and HTML representation. */ if ($save_msg->getType() != 'multipart/alternative') { for ($i = 2;; ++$i) { if (!($oldPart = $save_msg->getPart($i))) { break; } $replace_part = new Horde_Mime_Part(); $replace_part->setType('text/plain'); $replace_part->setCharset($this->charset); $replace_part->setLanguage($GLOBALS['language']); $replace_part->setContents('[' . _("Attachment stripped: Original attachment type") . ': "' . $oldPart->getType() . '", ' . _("name") . ': "' . $oldPart->getName(true) . '"]'); $save_msg->alterPart($i, $replace_part); } } } /* Generate the message string. */ $fcc = $save_msg->toString(array('defserver' => $imp_imap->config->maildomain, 'headers' => $headers, 'stream' => true)); /* Make sure sent mailbox is created. */ $sent_mail = IMP_Mailbox::get($opts['sent_mail']); $sent_mail->create(); $flags = array(Horde_Imap_Client::FLAG_SEEN, Horde_Imap_Client::FLAG_MDNSENT); try { $imp_imap->append($sent_mail, array(array('data' => $fcc, 'flags' => $flags))); } catch (IMP_Imap_Exception $e) { $notification->push(sprintf(_("Message sent successfully, but not saved to %s."), $sent_mail->display)); } } /* Delete the attachment data. */ $this->deleteAllAttachments(); /* Save recipients to address book? */ $this->_saveRecipients($recip['list']); /* Call post-sent hook. */ try { $injector->getInstance('Horde_Core_Hooks')->callHook('post_sent', 'imp', array($save_msg['msg'], $headers)); } catch (Horde_Exception_HookNotSet $e) { } }