On Thu, 2003-07-10 at 05:19, Ken Murchison wrote:
> What is this perl script trying to do?  Can you get a protocol dump?

The script:

I require a server-side sent directory.  That way, I don't have to try
to synchronize the sent directory on the three clients that I use, and
we have a copy of every outgoing mail sent

So, in Postfix, I say:   always_bcc = storesent
and in /etc/aliases:     storesent: |/usr/sbin/storesent

That way, Postfix invokes this script on every message that passes
through the mail server.  The script throws out the messages with
non-local origin and then saves the rest in the appropriate user's
mailbox.  I've been doing this for years on Courier -- it takes
about a 10-line script.

Here's where the authentication happens:

    my $userid = shift;

    my $cl = Cyrus::IMAP::Admin->new('localhost');
    die "Could not connect to Cyrus server" .
        ($! ? ": $!\n" : "\n") unless $cl;

    my $rc = $cl->authenticate('-user' => $user, '-password' => $pass);
    die "Could not log in: " . $cl->error . "\n" unless $rc;

    # check for the existence of the Sent folder
    unless( $cl->list("user.$userid.$sent") ) {
        # doesn't exist, so create it.
        $cl->create("user.$userid.$sent");
        die "Could not create user.$userid.$sent: " .
            $cl->error . "\n" if $cl->error;
    }

    ...

I've included the entire script as an attachment.

It must be possible to get a protocol dump, but I'm not sure how.
Is there a flag I can turn on in Perl to get the Cyrus::IMAP::Admin
module to log all LMTP traffic?  Feel free to comment, no matter how
harshly.  :)

Thanks,

    - Scott

#!/usr/bin/perl -w

# storesent
# Scott Bronson
# 3 July 2003


# TODO: should report errors via an email message.
# TODO: right now Return-Path on stored messages is [EMAIL PROTECTED]  Prolly want to fix this.

# This script is called by the Postfix local transport to automatically
# deliver messages into a user's Sent mail folder.
# This works for Cyrus.  Simple modifications would be needed for Courier.

# How to install:
# 1) Put "always_bcc = storesent" in /etc/postfix/main.cf
# 2) Put "storesent = |/usr/local/sbin/storesent" in /etc/aliases
# 3) Run "newaliases" and "postdfix reload".  That's it!
# 4) On my system, I need to do this to get it to work (I haven't
#    figured out why): "chown cyrus storesent; chmod u+s storesent"
#    This is a very minor but totally unnecessary security risk.
# 5) "chmod go-r" so regular users can't see the password.


# There is only one known bug: if you send a message to a mailing
# list, that message will usually be reflected back to you.  Since
# you are listed as the sender in that message, and there's no easy
# way to tell that it is from a mailing list and not directly from
# the user, it will be stored in your Sent folder.  This is not a
# problem if your IMAP server removes dupes (like Cyrus).


use strict;


# the following three lines are only used by ensure_folder_exists().
# If you create all the Sent folders ahead of time, you can delete
# these three lines, the routine itself, and the one line that calls it.
use Cyrus::IMAP::Admin;
my $user = 'root';	# username of an IMAP administrator
my $pass = 'PASSPASS';	# his/her PASSWORD   < < <   % % % % % 


# These should be configured for the local site:
my $sent = "Sent";	# name of the sent mail mailbox
my %ignores = ( 'root' => 1, 'daemon' => 1 );
my $deliver = "/usr/sbin/cyrdeliver";


# allow this script to run suid
$ENV{PATH} = '/bin:/usr/bin';


#
# 	Use postconf to tell us the value of $mydomain
#

open(CONF, "-|", "/usr/sbin/postconf", "-h", "mydomain")
	or die "Could not execute /usr/sbin/postconf: $!\n";
my $mydomain = <CONF>;
chomp $mydomain;
close CONF or die "Error running /usr/sbin/postconf: $1\n";


#
# 	Read headers into @msgheaders.  Each header will be a
# 	single element in this ordered set of multi-line strings.
#

my @msgheaders;
while( <STDIN> ) {
    last if /^\s*$/;  # empty line markes the end of the headers
    if( /^\s/ ) {
        # Any line in the header beginning with whitespace is a
        # continuation of the previous header.
        $msgheaders[$#msgheaders] .= $_;
    } else {
        push @msgheaders, $_;
    }
}


#
# 	Decide whether to store in sent mail or not.  If the sender's
# 	domain is the same as $mydomain, then sender is local.  If
# 	not, we throw it away.
#

my $sender = $ENV{'SENDER'};

if( $sender =~ /^\s*([EMAIL PROTECTED])\@(?:\S+\.)?$mydomain\s*$/ && !$ignores{$1} ) {
	my $userid = $1;
	ensure_folder_exists($userid);
	open(OUT, "|-", $deliver, "-m", $sent, $userid)
		or die "Could not execute $deliver: $!\n";
} else {
	# not local: dump this mesage
	open(OUT, ">/dev/null") or die "Could not open /dev/null??? $!\n";
}


#
# 	Send message to cyrdeliver.  First send headers, then
# 	a blank line, then the message body.
#

# The first header: output a custom Recieved header
print OUT "Received: from $mydomain (Postfix) by $mydomain (storesent)\n";

# Then output 
for( @msgheaders ) {
	# Get rid of Postfix's delivery headers. Cyrus will supply its own.
	next if /^From[ \t]/is;
	next if /^Return-Path:/is;
	next if /^X-Original-To:/is;
	next if /^Delivered-To:/is;

	# Cyrus thinks it's a dupe if we don't munge the Message-ID
	s/\@/.570@/ if /^Message-ID:/is;

	print OUT $_;
}

print OUT "\n";
# print body
print OUT while <STDIN>;


#
# 	we're done!
#

close OUT or die "storesent could not save message: $! ($?)\n";
exit 0;



#
# This function should not be necessary!  cyrdeliver needs an
# option to create the mail folder if it doesn't already
# exist.
#

sub ensure_folder_exists
{
	my $userid = shift;

	my $cl = Cyrus::IMAP::Admin->new('localhost');
	die "Could not connect to Cyrus server" .
		($! ? ": $!\n" : "\n") unless $cl;

	my $rc = $cl->authenticate('-user' => $user, '-password' => $pass);
	die "Could not log in: " . $cl->error . "\n" unless $rc;

	# check for the existence of the Sent folder
	unless( $cl->list("user.$userid.$sent") ) {
		# doesn't exist, so create it.
		$cl->create("user.$userid.$sent");
		die "Could not create user.$userid.$sent: " .
			$cl->error . "\n" if $cl->error;
	}
	
	$cl->setacl("user.$userid.$sent", 'anyone', 'p');
	die "Could not set acl for anyone on user.$userid.$sent: " .
		$cl->error . "\n" if $cl->error;
}

Reply via email to