gunnar: server/php-kolab/Kolab_Filter/Filter kolabmailtransport.php, NONE, 1.1 misc.php, NONE, 1.1 olhacks.php, NONE, 1.1 resmgr.php, NONE, 1.1
cvs at kolab.org
cvs at kolab.org
Wed Aug 8 08:51:18 CEST 2007
Author: gunnar
Update of /kolabrepository/server/php-kolab/Kolab_Filter/Filter
In directory doto:/tmp/cvs-serv29057/Filter
Added Files:
kolabmailtransport.php misc.php olhacks.php resmgr.php
Log Message:
A second PEAR package derived from kolab-resource-handlers/resmgr. Not yet completed.
--- NEW FILE: kolabmailtransport.php ---
<?php
/*
* Copyright (c) 2005 Klaraelvdalens Datakonsult AB
*
* Written by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
class KolabMailTransport {
function KolabMailTransport( $host = '127.0.0.1', $port = 2003 ) {
$this->host = $host;
$this->port = $port;
$this->transport = false;
}
/*abstract*/ function createTransport() {
myLog("Abstract method KolabMailTransport::createTransport() called!", RM_LOG_ERROR);
}
function start($sender,$recips) {
$this->createTransport();
$myclass = get_class($this->transport);
$this->got_newline = true;
if (!$this->transport) {
return new PEAR_Error('Failed to connect to $myclass: ' . $error->getMessage(), 421);
}
if (PEAR::isError($error = $this->transport->connect())) {
return new PEAR_Error('Failed to connect to $myclass: ' . $error->getMessage(), 421);
}
if (PEAR::isError($error = $this->transport->mailFrom($sender))) {
$resp = $this->transport->getResponse();
return new PEAR_Error('Failed to set sender: ' . $resp[1], $resp[0] );
}
if( !is_array( $recips ) ) $recips = array($recips);
$reciperrors = array();
foreach( $recips as $recip ) {
if (PEAR::isError($error = $this->transport->rcptTo($recip))) {
$resp = $this->transport->getResponse();
$msg = "Failed to set recipient $recip: " .$resp[1]. ", code=".$resp[0];
myLog($msg, RM_LOG_ERROR);
$reciperrors[] = new PEAR_Error('Failed to set recipient: '.$resp[1], $resp[0]);
}
}
if( count($reciperrors) == count($recips) ) {
// OK, all failed, just give up
if( count($reciperrors) == 1 ) {
// Only one failure, just return that
return $reciperrors[0];
}
// Multiple errors
return $this->createErrorObject( $reciperrors, 'Delivery to all recipients failed' );
}
if (PEAR::isError($error = $this->transport->_put('DATA'))) {
return $error;
}
if (PEAR::isError($error = $this->transport->_parseResponse(354))) {
return $error;
}
if( !empty($reciperrors) ) {
return $this->createErrorObject( $reciperrors, 'Delivery to some recipients failed' );
}
return true;
}
// Encapsulate multiple errors in one
function createErrorObject( $reciperrors, $msg = null ) {
// Return the lowest errorcode to not bounce more
// than we have to
if($msg == null) $msg = 'Delivery to recipients failed.';
$code = 1000;
foreach( $reciperrors as $err ) {
if( $err->code < $code ) $code = $err->code;
}
return new PEAR_Error( $msg, $code, null, null, $reciperrors);
}
/* Modified implementation from Net_SMTP that supports
* dotstuffing even when getting the mail line-by line */
function quotedataline(&$data) {
/*
* Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF
* (\r\n) linefeeds.
*/
//$data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
//$data = preg_replace("/\n\n/", "\n\r\n", $data);
$data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
//$data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\n", $data);
/*
* Because a single leading period (.) signifies an end to the data,
* legitimate leading periods need to be "doubled" (e.g. '..').
*/
if( $this->got_newline && $data[0] == '.' ) $data = '.'.$data;
$data = str_replace("\n.", "\n..", $data);
$len = strlen($data);
if( $len > 0 )
$this->got_newline = ( $data[$len-1] == "\n" );
}
function data( $data) {
$this->quotedataline($data);
if (PEAR::isError($this->transport->_send($data))) {
return new PEAR_Error('write to socket failed');
}
return true;
}
function end() {
if ($this->got_newline)
$dot = ".\r\n";
else
$dot = "\r\n.\r\n";
if (PEAR::isError($this->transport->_send($dot))) {
return new PEAR_Error('write to socket failed');
}
if (PEAR::isError($error = $this->transport->_parseResponse(250))) {
return $error;
}
$this->transport->disconnect();
$this->transport = false;
return true;
}
var $host;
var $port;
var $transport;
var $got_newline;
};
class KolabLMTP extends KolabMailTransport {
function KolabLMTP( $host = '127.0.0.1', $port = 2003 ) {
$this->KolabMailTransport($host,$port);
}
function createTransport() {
require_once 'Net/LMTP.php';
$this->transport = &new Net_LMTP($this->host, $this->port);
}
};
class KolabSMTP extends KolabMailTransport {
function KolabSMTP( $host = '127.0.0.1', $port = 25 ) {
$this->KolabMailTransport($host,$port);
}
function createTransport() {
require_once 'Net/SMTP.php';
$this->transport = &new Net_SMTP($this->host, $this->port);
}
};
?>
--- NEW FILE: misc.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Writen by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
// What myLog levels we can use
define('RM_LOG_SUPER', -1);
define('RM_LOG_SILENT', 0);
define('RM_LOG_ERROR', 1);
define('RM_LOG_WARN', 2);
define('RM_LOG_INFO', 3);
define('RM_LOG_DEBUG', 4);
$logLevelPrefixes = array(
RM_LOG_SUPER => '',
RM_LOG_SILENT => '',
RM_LOG_ERROR => 'Error',
RM_LOG_WARN => 'Warning',
RM_LOG_INFO => '',
RM_LOG_DEBUG => 'Debug',
);
// What logging mechanisms are available for use
define('RM_LOG_SYSLOG', 1);
define('RM_LOG_FILE', 2);
$logType = 0;
$logPrefix = '';
$logDestination = NULL;
function logInit($name = '')
{
global $params, $argv, $logType, $logPrefix, $logDestination;
if (empty($name)) {
$name = basename($argv[0]);
}
if (!array_key_exists('log', $params) || empty($params['log'])) {
return;
}
$matches = array();
if (preg_match('/(\w+):(.*)?/', $params['log'], $matches)) {
switch ($matches[1]) {
case 'syslog':
$logType = RM_LOG_SYSLOG;
$txtopts = preg_split('/[\s,]+/', $matches[2], -1, PREG_SPLIT_NO_EMPTY);
$options = 0;
foreach ($txtopts as $txtopt) {
switch ($txtopt) {
case 'cons': $options |= LOG_CONS; break;
case 'ndelay': $options |= LOG_NDELAY; break;
case 'odelay': $options |= LOG_ODELAY; break;
case 'perror': $options |= LOG_PERROR; break;
case 'pid': $options |= LOG_PID; break;
}
}
openlog($name, $options, LOG_USER);
break;
case 'file':
$logType = RM_LOG_FILE;
$logPrefix = $name;
$logDestination = fopen($matches[2], 'a');
break;
}
}
}
function logClose()
{
global $logType, $logDestination;
switch ($logType) {
case RM_LOG_SYSLOG:
closelog();
break;
case RM_LOG_FILE:
fclose($logDestination);
$logDestination = NULL;
break;
}
}
function myLog($text, $priority = RM_LOG_INFO)
{
global $params, $logLevelPrefixes, $logPrefix, $logType, $logDestination;
if ($params['log_level'] >= $priority) {
if (!empty($logLevelPrefixes[$priority])) {
$text = $logLevelPrefixes[$priority] . ": $text";
}
switch ($logType) {
case RM_LOG_SYSLOG:
syslog(RM_LOG_INFO, $text);
break;
case RM_LOG_FILE:
fwrite($logDestination, strftime('%B %d %T') . " ${logPrefix}[" . getmypid() . "]: $text\n");
fflush($logDestination);
break;
}
}
}
function init()
{
global $params;
define_syslog_variables();
logInit();
//myLog('Starting up in ' . ($params['group'] ? 'group' : 'resource') . ' mode', RM_LOG_SUPER);
$url_fopen = ini_get('allow_url_fopen');
if (!$url_fopen) {
myLog('\'allow_url_fopen\' is disabled in php.ini, enabling...', RM_LOG_WARN);
ini_set('allow_url_fopen', '1');
}
// This is used as the default domain for unqualified adresses
global $_SERVER;
if (!array_key_exists('SERVER_NAME', $_SERVER)) {
$_SERVER['SERVER_NAME'] = $params['email_domain'];
}
if (!array_key_exists('REMOTE_ADDR', $_SERVER)) {
$_SERVER['REMOTE_ADDR'] = $params['server'];
}
if (!array_key_exists('REMOTE_HOST', $_SERVER)) {
$_SERVER['REMOTE_HOST'] = $params['server'];
}
}
/* Since getopt() in php can't parse the options the way
* postfix gives them to us, we write our own option
* handling:
*
* Inputs:
* $opts: array('a','b','c'), a list of wanted options
* $args: the argv list
* Output:
* array of options and values. For example, the input
* "-a foo -b bar baz" would result in
* array( 'a' => 'foo', 'b' => array('bar','baz') )
*/
function parse_args( $opts, $args )
{
$ret = array();
for( $i = 0; $i < count($args); ++$i ) {
$arg = $args[$i];
if( $arg[0] == '-' ) {
if( in_array( $arg[1], $opts ) ) {
$val = array();
$i++;
while( $i < count($args) && $args[$i][0] != '-' ) {
$val[] = $args[$i];
$i++;
}
$i--;
if( array_key_exists($arg[1],$ret) && is_array( $ret[$arg[1]] ) ) $ret[$arg[1]] = array_merge((array)$ret[$arg[1]] ,(array)$val);
else if( count($val) == 1 ) $ret[$arg[1]] = $val[0];
else $ret[$arg[1]] = $val;
}
}
}
return $ret;
}
?>
--- NEW FILE: olhacks.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Writen by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
require_once 'Kolab/Filter/misc.php';
require_once HORDE_BASE . '/lib/core.php';
require_once 'Horde/iCalendar.php';
require_once 'Horde/NLS.php';
require_once 'Horde/MIME.php';
require_once 'Horde/MIME/Message.php';
require_once 'Horde/MIME/Headers.php';
require_once 'Horde/MIME/Part.php';
require_once 'Horde/MIME/Structure.php';
$forwardtext = "This is an invitation forwarded by outlook and\n".
"was rectified by the Kolab server.\n".
"The invitation was originally sent by\n%s.\n\n".
"Diese Einladung wurde von Outlook weitergeleitet\n".
"und vom Kolab-Server in gute Form gebracht.\n".
"Die Einladung wurde ursprünglich von\n%s geschickt.\n";
/*static*/ function olhacks_mime_parse( &$text ) {
/* Taken from Horde's MIME/Structure.php */
require_once 'Mail/mimeDecode.php';
/* Set up the options for the mimeDecode class. */
$decode_args = array();
$decode_args['include_bodies'] = true;
$decode_args['decode_bodies'] = false;
$decode_args['decode_headers'] = false;
$mimeDecode = &new Mail_mimeDecode($text, MIME_PART_EOL);
if (!($structure = $mimeDecode->decode($decode_args))) {
return false;
}
/* Put the object into imap_parsestructure() form. */
MIME_Structure::_convertMimeDecodeData($structure);
return array($structure->headers, $ret = &MIME_Structure::parse($structure));
}
/* static */ function copy_header( $name, &$msg_headers, &$headerarray ) {
$lname = strtolower($name);
if( array_key_exists($lname, $headerarray)) {
if( is_array( $headerarray[$lname] ) ) {
foreach( $headerarray[$lname] as $h ) {
$msg_headers->addHeader($name, $h );
}
} else {
$msg_headers->addHeader($name, $headerarray[$lname] );
}
}
}
/*
* Yet another problem: Outlook seems to remove the organizer
* from the iCal when forwarding -- we put the original sender
* back in as organizer.
*/
/* static */ function add_organizer( &$icaltxt, $from ) {
global $params;
$iCal = &new Horde_iCalendar();
$iCal->parsevCalendar($icaltxt);
$vevent =& $iCal->findComponent('VEVENT');
if( $vevent ) {
#myLog("Successfully parsed vevent", RM_LOG_DEBUG);
if( !$vevent->organizerName() ) {
#myLog("event has no organizer, adding $from", RM_LOG_DEBUG);
$adrs = imap_rfc822_parse_adrlist($from, $params['email_domain']);
if( count($adrs) > 0 ) {
$org_email = 'mailto:'.$adrs[0]->mailbox.'@'.$adrs[0]->host;
$org_name = $adrs[0]->personal;
if( $org_name ) $vevent->setAttribute( 'ORGANIZER', $org_email,
array( 'CN' => $org_name), false );
else $vevent->setAttribute( 'ORGANIZER', $org_email,
array(), false );
myLog("Adding missing organizer '$org_name <$org_email>' to iCal", RM_LOG_DEBUG);
$icaltxt = $iCal->exportvCalendar();
}
}
}
}
/* Yet another Outlook problem: Some versions of Outlook seems to be incapable
* of handling non-ascii characters properly in text/calendar parts of
* a multi-part/mixed mail which we use for forwarding.
* As a solution, we encode common characters as humanreadable
* two-letter ascii.
*/
/* static */ function olhacks_recode_to_ascii( $text ) {
#myLog("recoding \"$text\"", RM_LOG_DEBUG);
$text = str_replace( ('æ'), 'ae', $text );
$text = str_replace( ('ø'), 'oe', $text );
$text = str_replace( ('Ã¥'), 'aa', $text );
$text = str_replace( ('ä'), 'ae', $text );
$text = str_replace( ('ö'), 'oe', $text );
$text = str_replace( ('ü'), 'ue', $text );
$text = str_replace( ('Ã'), 'ss', $text );
$text = str_replace( ('Ã'), 'Ae', $text );
$text = str_replace( ('Ã'), 'Oe', $text );
$text = str_replace( ('Ã
'), 'Aa', $text );
$text = str_replace( ('Ã'), 'Ae', $text );
$text = str_replace( ('Ã'), 'Oe', $text );
$text = str_replace( ('Ã'), 'Ue', $text );
#myLog("recoded to \"$text\"", RM_LOG_DEBUG);
return $text;
}
/* main entry point */
function olhacks_embedical( $fqhostname, $sender, $recipients, $origfrom, $subject, $tmpfname ) {
myLog("Encapsulating iCal message forwarded by $sender", RM_LOG_DEBUG);
global $forwardtext;
// Read in message text
$requestText = '';
$handle = @fopen( $tmpfname, "r" );
if( $handle === false ) {
myLog("Error opening $tmpfname", RM_LOG_ERROR);
return false;
}
while (!feof($handle)) {
$requestText .= fread($handle, 8192);
}
fclose($handle);
// Parse existing message
list( $headers, $mime) = olhacks_mime_parse($requestText);
$parts = $mime->contentTypeMap();
if( count($parts) != 1 || $parts[1] != 'text/calendar' ) {
myLog("Message does not contain exactly one toplevel text/calendar part, passing through",
RM_LOG_DEBUG);
return false;
}
$basepart = $mime->getBasePart();
// Construct new MIME message with original message attached
$toppart = &new MIME_Message();
$dorigfrom = Mail_mimeDecode::_decodeHeader($origfrom);
$textpart = &new MIME_Part('text/plain', sprintf($forwardtext,$dorigfrom,$dorigfrom), 'UTF-8' );
$ical_txt = $basepart->transferDecode();
add_organizer($ical_txt, $dorigfrom);
$msgpart = &new MIME_Part($basepart->getType(), olhacks_recode_to_ascii($ical_txt),
$basepart->getCharset() );
$toppart->addPart($textpart);
$toppart->addPart($msgpart);
// Build the reply headers.
$msg_headers = &new MIME_Headers();
copy_header( 'Received', $msg_headers, $headers );
//$msg_headers->addReceivedHeader();
$msg_headers->addMessageIdHeader();
//myLog("Headers=".print_r($headers,true), RM_LOG_DEBUG);
copy_header( 'Date', $msg_headers, $headers );
copy_header( 'Resent-Date', $msg_headers, $headers );
copy_header( 'Subject', $msg_headers, $headers );
$msg_headers->addHeader('From', $sender);
$msg_headers->addHeader('To', join(', ', $recipients));
$msg_headers->addHeader('X-Kolab-Forwarded', 'TRUE');
$msg_headers->addMIMEHeaders($toppart);
copy_header( 'Content-Transfer-Encoding', $msg_headers, $headers );
if (is_object($msg_headers)) {
$headerArray = $toppart->encode($msg_headers->toArray(), $toppart->getCharset());
} else {
$headerArray = $toppart->encode($msg_headers, $toppart->getCharset());
}
// Inject message back into postfix
require_once 'Mail.php';
$mailer = &Mail::factory('SMTP', array('auth' => false, 'port' => 10026 ));
$msg = $toppart->toString();
/* Make sure the message has a trailing newline. */
if (substr($msg, -1) != "\n") {
$msg .= "\n";
}
$error = $mailer->send($recipients, $headerArray, $msg);
if( PEAR::isError($error) ) {
fwrite(STDOUT, $error->getMessage()."\n"); exit(EX_TEMPFAIL);
}
return true;
}
?>
--- NEW FILE: resmgr.php ---
<?php
/*
* Copyright (c) 2004 Klaraelvdalens Datakonsult AB
*
* Writen by Steffen Hansen <steffen at klaralvdalens-datakonsult.se>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, 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
* General Public License for more details.
*
* You can view the GNU General Public License, online, at the GNU
* Project's homepage; see <http://www.gnu.org/licenses/gpl.html>.
*/
[...1437 lines suppressed...]
unset($imap);
// Get the resource's free/busy list
// once more so it is up to date
if( !triggerFreeBusy($resource,false) ) {
myLog("Error updating fblist", RM_LOG_SUPER );
}
return false;;
default:
// We either don't currently handle these iTip methods, or they do not
// apply to what we're trying to accomplish here
myLog("Ignoring $method method and passing message through to $resource");
return true;
}
// Pass the message through to the group's mailbox
myLog("Passing through $method method to $resource");
return true;
}
?>
More information about the commits
mailing list