Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F174717
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/kolab_sync_message.php b/lib/kolab_sync_message.php
index 5068738..e916be4 100644
--- a/lib/kolab_sync_message.php
+++ b/lib/kolab_sync_message.php
@@ -1,494 +1,494 @@
<?php
/**
+--------------------------------------------------------------------------+
| Kolab Sync (ActiveSync for Kolab) |
| |
| Copyright (C) 2011-2012, Kolab Systems AG <contact@kolabsys.com> |
| |
| This program is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Affero General Public License as published |
| by the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/> |
+--------------------------------------------------------------------------+
| Author: Aleksander Machniak <machniak@kolabsys.com> |
+--------------------------------------------------------------------------+
*/
class kolab_sync_message
{
protected $headers = array();
protected $body;
protected $ctype;
protected $ctype_params = array();
/**
* Constructor
*
* @param string|resource $source MIME message source
*/
function __construct($source)
{
$this->parse_mime($source);
}
/**
* Returns message headers
*
* @return array Message headers
*/
public function headers()
{
return $this->headers;
}
public function source()
{
$headers = array();
// Build the message back
foreach ($this->headers as $header => $header_value) {
$headers[$header] = $header . ': ' . $header_value;
}
return trim(implode("\r\n", $headers)) . "\r\n\r\n" . ltrim($this->body);
// @TODO: work with file streams
}
/**
* Appends text at the end of the message body
*
* @todo: HTML support
*
* @param string $text Text to append
* @param string $charset Text charset
*/
public function append($text, $charset = null)
{
if ($this->ctype == 'text/plain') {
// decode body
$body = $this->decode($this->body, $this->headers['Content-Transfer-Encoding']);
$body = rcube_charset::convert($body, $this->ctype_params['charset'], $charset);
// append text
$body .= $text;
// encode and save
$body = rcube_charset::convert($body, $charset, $this->ctype_params['charset']);
$this->body = $this->encode($body, $this->headers['Content-Transfer-Encoding']);
}
}
/**
* Adds attachment to the message
*
* @param string $body Attachment body (not encoded)
* @param string $params Attachment parameters (Mail_mimePart format)
*/
public function add_attachment($body, $params = array())
{
// convert the message into multipart/mixed
if ($this->ctype != 'multipart/mixed') {
$boundary = '_' . md5(rand() . microtime());
$this->body = "--$boundary\r\n"
."Content-Type: " . $this->headers['Content-Type']."\r\n"
."Content-Transfer-Encoding: " . $this->headers['Content-Transfer-Encoding']."\r\n"
."\r\n" . trim($this->body) . "\r\n"
."--$boundary\r\n";
$this->ctype = 'multipart/mixed';
$this->ctype_params = array('boundary' => $boundary);
unset($this->headers['Content-Transfer-Encoding']);
$this->save_content_type($this->ctype, $this->ctype_params);
}
// make sure MIME-Version header is set, it's required by some servers
if (empty($this->headers['MIME-Version'])) {
$this->headers['MIME-Version'] = '1.0';
}
$boundary = $this->ctype_params['boundary'];
$part = new Mail_mimePart($body, $params);
$body = $part->encode();
foreach ($body['headers'] as $name => $value) {
$body['headers'][$name] = $name . ': ' . $value;
}
$this->body = rtrim($this->body);
$this->body = preg_replace('/--$/', '', $this->body);
// add the attachment to the end of the message
$this->body .= "\r\n"
.implode("\r\n", $body['headers']) . "\r\n\r\n"
.$body['body'] . "\r\n--$boundary--\r\n";
}
/**
* Sets the value of specified message header
*
* @param string $name Header name
* @param string $value Header value
*/
public function set_header($name, $value)
{
$name = self::normalize_header_name($name);
if ($name != 'Content-Type') {
$this->headers[$name] = $value;
}
}
/**
* Send the given message using the configured method.
*
* @param array $smtp_error SMTP error array (reference)
* @param array $smtp_opts SMTP options (e.g. DSN request)
*
* @return boolean Send status.
*/
public function send(&$smtp_error = null, $smtp_opts = null)
{
$rcube = rcube::get_instance();
$headers = $this->headers;
$mailto = $headers['To'];
- $headers['User-Agent'] .= $rcube->app_name . ' ' . kolab_sync::VERSION;
+ $headers['User-Agent'] = $rcube->app_name . ' ' . kolab_sync::VERSION;
if ($agent = $rcube->config->get('useragent')) {
$headers['User-Agent'] .= '/' . $agent;
}
if (empty($headers['From'])) {
$headers['From'] = $this->get_identity();
}
// make sure there's sender name in From:
else if ($rcube->config->get('activesync_fix_from')
&& preg_match('/^<?((\S+|("[^"]+"))@\S+)>?$/', trim($headers['From']), $m)
) {
$identities = kolab_sync::get_instance()->user->list_identities();
$email = $m[1];
foreach ((array) $identities as $ident) {
if ($ident['email'] == $email) {
if ($ident['name']) {
$headers['From'] = format_email_recipient($email, $ident['name']);
}
break;
}
}
}
if (empty($headers['Message-ID'])) {
$headers['Message-ID'] = $rcube->gen_message_id();
}
// remove empty headers
$headers = array_filter($headers);
$smtp_headers = $headers;
// generate list of recipients
$recipients = array();
if (!empty($headers['To']))
$recipients[] = $headers['To'];
if (!empty($headers['Cc']))
$recipients[] = $headers['Cc'];
if (!empty($headers['Bcc']))
$recipients[] = $headers['Bcc'];
if (empty($headers['To']) && empty($headers['Cc'])) {
$headers['To'] = 'undisclosed-recipients:;';
}
// remove Bcc header
unset($smtp_headers['Bcc']);
// send message
if (!is_object($rcube->smtp)) {
$rcube->smtp_init(true);
}
$sent = $rcube->smtp->send_mail($headers['From'], $recipients, $smtp_headers, $this->body, $smtp_opts);
$smtp_response = $rcube->smtp->get_response();
$smtp_error = $rcube->smtp->get_error();
// log error
if (!$sent) {
rcube::raise_error(array('code' => 800, 'type' => 'smtp',
'line' => __LINE__, 'file' => __FILE__,
'message' => "SMTP error: ".join("\n", $smtp_response)), true, false);
}
if ($sent) {
$rcube->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $this->body));
// remove MDN headers after sending
unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
if ($rcube->config->get('smtp_log')) {
// get all recipient addresses
$mailto = rcube_mime::decode_address_list(implode(',', $recipients), null, false, null, true);
rcube::write_log('sendmail', sprintf("User %s [%s]; Message %s for %s; %s",
$rcube->get_user_name(),
rcube_utils::remote_addr(),
$headers['Message-ID'],
implode(', ', $mailto),
!empty($smtp_response) ? implode('; ', $smtp_response) : '')
);
}
}
$this->headers = $headers;
return $sent;
}
/**
* Parses the message source and fixes 8bit data for ActiveSync.
* This way any not UTF8 characters will be encoded before
* sending to the device.
*
* @param string $message Message source
*
* @return string Fixed message source
*/
public static function recode_message($message)
{
// @TODO: work with stream, to workaround memory issues with big messages
if (is_resource($message)) {
$message = stream_get_contents($message);
}
list($headers, $message) = preg_split('/\r?\n\r?\n/', $message, 2, PREG_SPLIT_NO_EMPTY);
$hdrs = self::parse_headers($headers);
// multipart message
if (preg_match('/boundary="?([a-z0-9-\'\(\)+_\,\.\/:=\? ]+)"?/i', $hdrs['Content-Type'], $matches)) {
$boundary = '--' . $matches[1];
$message = explode($boundary, $message);
for ($x=1, $parts = count($message) - 1; $x<$parts; $x++) {
$message[$x] = "\r\n" . self::recode_message(ltrim($message[$x]));
}
return $headers . "\r\n\r\n" . implode($boundary , $message);
}
// single part
$enc = !empty($hdrs['Content-Transfer-Encoding']) ? strtolower($hdrs['Content-Transfer-Encoding']) : null;
// do nothing if already encoded
if ($enc != 'quoted-printable' && $enc != 'base64') {
// recode body if any non-printable-ascii characters found
if (preg_match('/[^\x20-\x7E\x0A\x0D\x09]/', $message)) {
$hdrs['Content-Transfer-Encoding'] = 'base64';
foreach ($hdrs as $header => $header_value) {
$hdrs[$header] = $header . ': ' . $header_value;
}
$headers = trim(implode("\r\n", $hdrs));
$message = rtrim(chunk_split(base64_encode(rtrim($message)), 76, "\r\n")) . "\r\n";
}
}
return $headers . "\r\n\r\n" . $message;
}
/**
* Creates a fake plain text message source with predefined headers and body
*
* @param string $headers Message headers
* @param string $body Plain text body
*
* @return string Message source
*/
public static function fake_message($headers, $body = '')
{
$hdrs = self::parse_headers($headers);
$result = '';
$hdrs['Content-Type'] = 'text/plain; charset=UTF-8';
$hdrs['Content-Transfer-Encoding'] = 'quoted-printable';
foreach ($hdrs as $header => $header_value) {
$result .= $header . ': ' . $header_value . "\r\n";
}
return $result . "\r\n" . self::encode($body, 'quoted-printable');
}
/**
* MIME message parser
*
* @param string|resource $message MIME message source
* @param bool $decode_body Enables body decoding
*
* @return array Message headers array and message body
*/
protected function parse_mime($message)
{
// @TODO: work with stream, to workaround memory issues with big messages
if (is_resource($message)) {
$message = stream_get_contents($message);
}
list($headers, $message) = preg_split('/\r?\n\r?\n/', $message, 2, PREG_SPLIT_NO_EMPTY);
$headers = self::parse_headers($headers);
// parse Content-Type header
$ctype_parts = preg_split('/[; ]+/', $headers['Content-Type']);
$this->ctype = strtolower(array_shift($ctype_parts));
foreach ($ctype_parts as $part) {
if (preg_match('/^([a-z-_]+)\s*=\s*(.+)$/i', trim($part), $m)) {
$this->ctype_params[strtolower($m[1])] = trim($m[2], '"');
}
}
if (!empty($headers['Content-Transfer-Encoding'])) {
$headers['Content-Transfer-Encoding'] = strtolower($headers['Content-Transfer-Encoding']);
}
$this->headers = $headers;
$this->body = $message;
}
/**
* Parse message source with headers
*/
protected static function parse_headers($headers)
{
// Parse headers
$headers = str_replace("\r\n", "\n", $headers);
$headers = explode("\n", trim($headers));
$ln = 0;
$lines = array();
foreach ($headers as $line) {
if (ord($line[0]) <= 32) {
$lines[$ln] .= (empty($lines[$ln]) ? '' : "\r\n") . $line;
}
else {
$lines[++$ln] = trim($line);
}
}
// Unify char-case of header names
$headers = array();
foreach ($lines as $line) {
list($field, $string) = explode(':', $line, 2);
if ($field = self::normalize_header_name($field)) {
$headers[$field] = trim($string);
}
}
return $headers;
}
/**
* Normalize (fix) header names
*/
protected static function normalize_header_name($name)
{
$headers_map = array(
'subject' => 'Subject',
'from' => 'From',
'to' => 'To',
'cc' => 'Cc',
'bcc' => 'Bcc',
'message-id' => 'Message-ID',
'references' => 'References',
'content-type' => 'Content-Type',
'content-transfer-encoding' => 'Content-Transfer-Encoding',
);
$name_lc = strtolower($name);
return isset($headers_map[$name_lc]) ? $headers_map[$name_lc] : $name;
}
/**
* Encodes message/part body
*
* @param string $body Message/part body
* @param string $encoding Content encoding
*
* @return string Encoded body
*/
protected function encode($body, $encoding)
{
switch ($encoding) {
case 'base64':
$body = base64_encode($body);
$body = chunk_split($body, 76, "\r\n");
break;
case 'quoted-printable':
$body = quoted_printable_encode($body);
break;
}
return $body;
}
/**
* Decodes message/part body
*
* @param string $body Message/part body
* @param string $encoding Content encoding
*
* @return string Decoded body
*/
protected function decode($body, $encoding)
{
$body = str_replace("\r\n", "\n", $body);
switch ($encoding) {
case 'base64':
$body = base64_decode($body);
break;
case 'quoted-printable':
$body = quoted_printable_decode($body);
break;
}
return $body;
}
/**
* Returns email address string from default identity of the current user
*/
protected function get_identity()
{
$user = kolab_sync::get_instance()->user;
if ($identity = $user->get_identity()) {
return format_email_recipient(format_email($identity['email']), $identity['name']);
}
}
protected function save_content_type($ctype, $params = array())
{
$this->ctype = $ctype;
$this->ctype_params = $params;
$this->headers['Content-Type'] = $ctype;
if (!empty($params)) {
foreach ($params as $name => $value) {
$this->headers['Content-Type'] .= sprintf('; %s="%s"', $name, $value);
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 3:49 AM (19 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
120068
Default Alt Text
(16 KB)
Attached To
Mode
R4 syncroton
Attached
Detach File
Event Timeline
Log In to Comment