gunnar: server/php-kolab/Kolab_Filter/Filter Content.php, NONE, 1.1 Outgoing.php, 1.5, NONE
cvs at kolab.org
cvs at kolab.org
Tue Nov 27 16:31:46 CET 2007
- Previous message: gunnar: server/php-kolab/Kolab_Filter/tests dumb_usage.phpt, 1.2, 1.3 outgoing.phpt, 1.1, 1.2 simple_in_msg.phpt, 1.2, 1.3 simple_out_msg.phpt, 1.2, 1.3
- Next message: marcus: server/kolabd/kolabd/dist_conf suse,1.63,1.64
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Author: gunnar
Update of /kolabrepository/server/php-kolab/Kolab_Filter/Filter
In directory doto:/tmp/cvs-serv17267/php-kolab/Kolab_Filter/Filter
Added Files:
Content.php
Removed Files:
Outgoing.php
Log Message:
Rename Outgoing to Content and make the tests work again.
--- NEW FILE: Content.php ---
<?php
/*
* COPYRIGHT
* ---------
*
* See ../AUTHORS file
*
*
* LICENSE
* -------
*
* 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 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision: 1.1 $
*
* ABOUT
* -----
*
* A Kolab Server filter for outgoing mails.
*
*/
/* Load the basic filter definition */
require_once 'Kolab/Filter/Filter.php';
define('RM_STATE_READING_HEADER', 1 );
define('RM_STATE_READING_FROM', 2 );
define('RM_STATE_READING_SUBJECT',3 );
define('RM_STATE_READING_SENDER', 4 );
define('RM_STATE_READING_BODY', 5 );
class Filter_Content extends Filter
{
function Filter_Content($transport = 'SMTP', $debug = false)
{
Filter::Filter($transport, $debug);
}
function _parse($inh = STDIN)
{
global $conf;
if (!empty($conf['filter']['verify_from_header'])) {
$verify_from_header = $conf['filter']['verify_from_header'];
} else {
$verify_from_header = true;
}
if (!empty($conf['filter']['allow_sender_header'])) {
$allow_sender_header = $conf['filter']['allow_sender_header'];
} else {
$allow_sender_header = true;
}
if (!empty($conf['filter']['allow_outlook_ical_forward'])) {
$allow_outlook_ical_forward = $conf['filter']['allow_outlook_ical_forward'];
} else {
$allow_outlook_ical_forward = true;
}
$ical = false;
$from = false;
$subject = false;
$senderok = true;
$rewrittenfrom = false;
$state = RM_STATE_READING_HEADER;
while (!feof($inh) && $state != RM_STATE_READING_BODY) {
$buffer = fgets($inh, 8192);
$line = rtrim($buffer, "\r\n");
if ($line == '') {
/* Done with headers */
$state = RM_STATE_READING_BODY;
if ($from && $verify_from_header) {
$rc = verify_sender($this->_sasl_username, $this->_sender,
$from, $this->_client_address);
if (is_a($rc, 'PEAR_Error')) {
return $rc;
} else if ($rc === true) {
/* All OK, do nothing */
} else if ($rc === false) {
/* Reject! */
$senderok = false;
} else if (is_string($rc)) {
/* Rewrite from */
if (strpos($from, $rc) === false) {
Horde::logMessage(sprintf(_("Rewriting '%s' to '%s'"),
$from, $to),
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$rewrittenfrom = "From: $rc\r\n";
}
}
}
} else {
if ($line[0] != ' ' && $line[0] != "\t") {
$state = RM_STATE_READING_HEADER;
}
switch( $state ) {
case RM_STATE_READING_HEADER:
if ($allow_sender_header &&
eregi('^Sender: (.*)', $line, $regs)) {
$from = $regs[1];
$state = RM_STATE_READING_SENDER;
} else if (!$from && eregi('^From: (.*)', $line, $regs)) {
$from = $regs[1];
$state = RM_STATE_READING_FROM;
} else if (eregi('^Subject: (.*)', $line, $regs)) {
$subject = $regs[1];
$state = RM_STATE_READING_SUBJECT;
} else if (eregi('^Content-Type: text/calendar', $line)) {
Horde::logMessage(_("Found iCal data in message"),
__FILE__, __LINE__, PEAR_LOG_DEBUG);
$ical = true;
} else if (eregi('^Message-ID: (.*)', $line, $regs)) {
$this->_id = $regs[1];
}
break;
case RM_STATE_READING_FROM:
$from .= $line;
break;
case RM_STATE_READING_SENDER:
$from .= $line;
break;
case RM_STATE_READING_SUBJECT:
$subject .= $line;
break;
}
}
if (@fwrite($this->_tmpfh, $buffer) === false) {
$msg = $php_errormsg;
return PEAR::raiseError(sprintf(_("Error: Could not write to %s: %s"),
$this->_tmpfile, $msg),
OUT_LOG | EX_IOERR);
}
}
while (!feof($inh)) {
$buffer = fread($inh, 8192);
if (@fwrite($this->_tmpfh, $buffer) === false) {
$msg = $php_errormsg;
return PEAR::raiseError(sprintf(_("Error: Could not write to %s: %s"),
$this->_tmpfile, $msg),
OUT_LOG | EX_IOERR);
}
}
if (@fclose($this->_tmpfh) === false) {
$msg = $php_errormsg;
return PEAR::raiseError(sprintf(_("Error: Failed closing %s: %s"),
$this->_tmpfile, $msg),
OUT_LOG | EX_IOERR);
}
if (!$senderok) {
if ($ical && $allow_outlook_ical_forward ) {
require_once('Kolab/Filter/Outlook.php');
$rc = olhacks_embedical($this->_fqhostname, $this->_sender, $this->_recipients,
$from, $subject, $this->_tmpfname);
if (is_a($rc, 'PEAR_Error')) {
return $rc;
} else if ($rc === true) {
return;
}
} else {
return PEAR::raiseError(sprintf(_("Invalid From: header. %s looks like a forged sender"),
$from),
OUT_LOG | OUT_STDOUT | EX_NOPERM);
}
}
$result = $this->deliver($rewrittenfrom);
if (is_a($result, 'PEAR_Error')) {
return $result;
}
}
function deliver($rewrittenfrom)
{
global $conf;
if (!empty($conf['filter']['smtp_host'])) {
$host = $conf['filter']['smtp_host'];
} else {
$host = 'localhost';
}
if (!empty($conf['filter']['smtp_port'])) {
$port = $conf['filter']['smtp_port'];
} else {
$port = 10025;
}
$transport = $this->_getTransport($host, $port);
$tmpf = @fopen($this->_tmpfile, 'r');
if (!$tmpf) {
$msg = $php_errormsg;
return PEAR::raiseError(sprintf(_("Error: Could not open %s for writing: %s"),
$this->_tmpfile, $msg),
OUT_LOG | EX_IOERR);
}
$result = $transport->start($this->_sender, $this->_recipients);
if (is_a($result, 'PEAR_Error')) {
return $this->_rewriteCode($result);
}
$state = RM_STATE_READING_HEADER;
while (!feof($tmpf) && $state != RM_STATE_READING_BODY) {
$buffer = fgets($tmpf, 8192);
if ($rewrittenfrom) {
if (eregi( '^From: (.*)', $buffer)) {
$result = $transport->data($rewrittenfrom);
if (is_a($result, 'PEAR_Error')) {
return $this->_rewriteCode($result);
}
$state = RM_STATE_READING_FROM;
continue;
} else if ($state == RM_STATE_READING_FROM &&
($buffer[0] == ' ' || $buffer[0] == "\t")) {
/* Folded From header, ignore */
continue;
}
}
if (rtrim($buffer, "\r\n") == '') {
$state = RM_STATE_READING_BODY;
} else if ($buffer[0] != ' ' && $buffer[0] != "\t") {
$state = RM_STATE_READING_HEADER;
}
$result = $transport->data($buffer);
if (is_a($result, 'PEAR_Error')) {
return $this->_rewriteCode($result);
}
}
while (!feof($tmpf)) {
$buffer = fread($tmpf, 8192);
$len = strlen($buffer);
/* We can't tolerate that the buffer breaks the data
* between \r and \n, so we try to avoid that. The limit
* of 100 reads is to battle abuse
*/
while ($buffer{$len-1} == "\r" && $len < 8192 + 100) {
$buffer .= fread($tmpf,1);
$len++;
}
$result = $transport->data($buffer);
if (is_a($result, 'PEAR_Error')) {
return $this->_rewriteCode($result);
}
}
return $transport->end();
}
}
// Cleanup function
function is_my_domain($addr)
{
global $conf;
if (!empty($conf['filter']['verify_subdomains'])) {
$verify_subdomains = $conf['filter']['verify_subdomains'];
} else {
$verify_subdomains = true;
}
if (!empty($conf['filter']['email_domain'])) {
$email_domain = $conf['filter']['email_domain'];
} else {
$email_domain = 'localhost';
}
$domains = (array) $email_domain;
$adrs = imap_rfc822_parse_adrlist($addr, $email_domain);
foreach ($adrs as $adr) {
$adrdom = $adr->host;
if (empty($adrdom)) {
continue;
}
foreach ($domains as $dom) {
if ($dom == $adrdom) {
return true;
}
if ($verify_subdomains && substr($adrdom, -strlen($dom)-1) == ".$dom") {
return true;
}
}
}
return false;
}
/**
Returns a list of allowed email addresses for user $sasluser
or a PEAR_Error object if something croaked.
*/
function addrs_for_uid($sasluser)
{
global $conf;
/* Connect to the LDAP server and retrieve the users'
* allowed email addresses
*/
$ldap = ldap_connect($conf['filter']['ldap_uri']);
if (!ldap_bind($ldap, $conf['filter']['bind_dn'], $conf['filter']['bind_pw'])) {
return PEAR::raiseError(sprintf(_("Unable to contact LDAP server: %s"),
ldap_error($ldap)),
OUT_LOG | EX_TEMPFAIL);
}
$filter = "(&(objectClass=kolabInetOrgPerson)(|(mail=$sasluser)(uid=$sasluser)))";
$result = ldap_search($ldap, $conf['filter']['base_dn'],
$filter,
array("dn", "mail", "alias" ));
if (!$result) {
return PEAR::raiseError(sprintf(_("Unable to perform LDAP search: %s"),
ldap_error($ldap)),
OUT_LOG | EX_TEMPFAIL);
}
$entries = ldap_get_entries($ldap, $result);
if ($entries['count'] != 1) {
return PEAR::raiseError(sprintf(_("%s objects returned for uid %s. Unable to look up user."),
$entries['count'], $sasluser),
OUT_LOG | EX_TEMPFAIL);
}
unset($entries[0]['mail']['count']);
unset($entries[0]['alias']['count']);
$addrs = array_merge((array) $entries[0]['mail'],(array) $entries[0]['alias']);
$mail = $entries[0]['mail'][0];
ldap_free_result($result);
$filter = "(&(objectClass=kolabInetOrgPerson)(kolabDelegate=$mail))";
$result = ldap_search($ldap, $conf['filter']['base_dn'],
$filter,
array("dn", "mail" ));
if (!$result) {
return PEAR::raiseError(sprintf(_("Unable to perform LDAP search: %s"),
ldap_error($ldap)),
OUT_LOG | EX_TEMPFAIL);
}
$entries = ldap_get_entries($ldap, $result);
unset( $entries['count'] );
foreach( $entries as $adr ) {
if( $adr['mail']['count'] > 0 ) {
unset($adr['mail']['count']);
$addrs = array_merge((array) $addrs,(array) $adr['mail']);
}
}
ldap_free_result($result);
ldap_close($ldap);
return $addrs;
}
/** Returns the format string used to rewrite
the From header for untrusted messages */
function get_untrusted_subject_insert($sasluser,$sender)
{
global $conf;
if ($sasluser) {
if (!empty($conf['filter']['untrusted_subject_insert'])) {
$fmt = $conf['filter']['untrusted_subject_insert'];
} else {
$fmt = _("(UNTRUSTED, sender is <%s>)");
}
} else {
if (!empty($conf['filter']['unauthenticated_subject_insert'])) {
$fmt = $conf['filter']['unauthenticated_subject_insert'];
} else {
$fmt = _("(UNTRUSTED, sender <%s> is not authenticated)");
}
}
return sprintf($fmt, $sender);
}
/** Check that the From header is not trying
to impersonate a valid user that is not
$sasluser. Returns one of:
* True if From can be accepted
* False if From must be rejected
* A string with a corrected From header that makes
From acceptable
* A PEAR_Error object if something croaked
*/
function verify_sender($sasluser, $sender, $fromhdr, $client_addr) {
global $conf;
if (!empty($conf['filter']['email_domain'])) {
$domains = $conf['filter']['email_domain'];
} else {
$domains = 'localhost';
}
if (!is_array($domains)) {
$domains = array($domains);
}
if (!empty($conf['filter']['local_addr'])) {
$local_addr = $conf['filter']['local_addr'];
} else {
$local_addr = 'localhost';
}
if (!empty($conf['filter']['verify_subdomains'])) {
$verify_subdomains = $conf['filter']['verify_subdomains'];
} else {
$verify_subdomains = true;
}
if (!empty($conf['filter']['reject_forged_from_headers'])) {
$reject_forged_from_headers = $conf['filter']['reject_forged_from_headers'];
} else {
$reject_forged_from_headers = true;
}
if (!empty($conf['filter']['kolabhosts'])) {
$kolabhosts = $conf['filter']['kolabhosts'];
} else {
$kolabhosts = 'localhost';
}
/* Allow anything from localhost and
* fellow Kolab-hosts
*/
if ($client_addr == $local_addr) {
return true;
}
$kolabhosts = split(',', $kolabhosts);
$kolabhosts = array_map('gethostbyname', $kolabhosts );
if (array_search($client_addr, $kolabhosts) !== false) {
return true;
}
if ($sasluser) {
$allowed_addrs = addrs_for_uid($sasluser);
if (is_a($allowed_addrs, 'PEAR_Error')) {
return $allowed_addrs;
}
} else {
$allowed_addrs = false;
}
$untrusted = get_untrusted_subject_insert($sasluser,$sender);
$adrs = imap_rfc822_parse_adrlist($fromhdr, $domains[0]);
foreach ($adrs as $adr) {
$from = $adr->mailbox . '@' . $adr->host;
$fromdom = $adr->host;
if ($sasluser) {
if (!in_array(strtolower($from), $allowed_addrs)) {
Horde::logMessage(sprintf(_("%s is not an allowed From address for %s"),
$from, $sasluser), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return false;
}
} else {
foreach ($domains as $domain) {
if (strtolower($fromdom) == $domain
|| ($verify_subdomains
&& substr($fromdom, -strlen($domain)-1) == ".$domain")) {
if ($reject_forged_from_header) {
Horde::logMessage(sprintf(_("%s is not an allowed From address for unauthenticated users."),
$from), __FILE__, __LINE__, PEAR_LOG_DEBUG);
return false;
} else {
/* Rewrite */
Horde::logMessage(sprintf(_("%s is not an allowed From address for unauthenticated users, rewriting."),
$from), __FILE__, __LINE__, PEAR_LOG_DEBUG);
if (strpos( $fromhdr, $untrusted )===false) {
return '"'.str_replace(array("\\",'"'),array("\\\\",'\"'),$adr->personal).' '.$untrusted.'" '.'<'.$from.'>';
} else {
return true;
}
}
}
}
}
}
/* All seems OK */
return true;
/* TODO: What do we do about subdomains? */
/*
$senderdom = substr(strrchr($sender, '@'), 1);
foreach( $domains as $domain ) {
if( $conf['filter']['verify_subdomains'] ) {
if( ($senderdom == $domain ||
$fromdom == $domain ||
substr($senderdom, -strlen($domain)-1) == ".$domain" ||
substr($fromdom, -strlen($domain)-1) == ".$domain" ) &&
$sender != $from ) {
return false;
}
} else {
if( ($senderdom == $domain ||
$fromdom == $domain ) &&
$sender != $from ) {
return false;
}
}
}
}
return true;
*/
}
?>
--- Outgoing.php DELETED ---
- Previous message: gunnar: server/php-kolab/Kolab_Filter/tests dumb_usage.phpt, 1.2, 1.3 outgoing.phpt, 1.1, 1.2 simple_in_msg.phpt, 1.2, 1.3 simple_out_msg.phpt, 1.2, 1.3
- Next message: marcus: server/kolabd/kolabd/dist_conf suse,1.63,1.64
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the commits
mailing list