PDA

View Full Version : Simple autoresponder - testing for subject


DogAndPony
01-10-2008, 11:30 PM
Hi, kids...

I'm trying to put together a simple autoresponder for a client, and all I seem to find script-wise out there are complex scripts that are massive overkill.

All I need to do is:

1) Test for a specific Subject
2) Reply with the contents of a specific text file.

(Looping protection would be a good thing, too, natch.)

bouncesaying wouldn't be appropriate, since a bounce code would be generated, and the reply is long/multiline.

I can't see any way to do it as a simple filter... Or is there?

If it has to be a processor, it'd be great if it could be a Perl script.

It's been ages since I piped mail to a script, and since a crash I don't have my old work, so I'm just hoping to not have to reinvent the wheel here.

And my client needs this asap, of course... :)

Any code or links to tutorials that might help?

Many TIA's!

sheila
01-10-2008, 11:43 PM
A real quick thought is to set up a filter that tests for the subject, and if it matches, uses mutt to email back the required response, similiar to what was done here:

(although this example used cron instead of a mail filter...same basic idea)

http://www.aota.net/forums/showthread.php?p=98307&highlight=mutt+filter#post98307

DogAndPony
01-11-2008, 03:11 AM
Thanks for that, Sheila...

Wasn't quite what I was looking for, tho.

Here's what I've come up with -- which doesn't work, natch. :)

The filter line -- which is loosely based on a line in the tutorial page, but I have no idea what sort of syntax to use for the if (tried killing the brackets, using parens, using procmail-ish stuff, but no go):

if [^Subject =~ /a particular string/] ; then /big/dom/xdomain/cgi-bin/autoreply.pl; exit 99; fi

Also tried:

if '822field subject | grep -iq /a particular string/'; then /big/dom/xdomain/cgi-bin/autoreply.pl; exit 99; fi

...but no joy.

And -- using snippets of found code, esp. getting the From: -- this is autoreply.pl:


#!/usr/local/bin/perl

$mailprog = '/usr/lib/sendmail';

while (<STDIN>)

{
# Get the from address

if( $_ =~ "From:" && $fromcount == 0 )
{
$fromline = $_;
@sender_address = split(/: /, $fromline );
$fromcount++;
};
};

$sender_address = $sender_address[1];

############## Load the reply content
my $contentPath = "/big/dom/xdomain/www/docs/reply_content";

my $content = "";

binmode(STDOUT);
if (open(RE, $contentPath)) {
binmode(RE);
undef $/;
$content = <RE>;
close(RE);
}

######## Send mail to the requester

open (MAIL, "|$mailprog $sender_address") || die "Unable to send your mail.\nPlease send e-mail to webmaster\@domain.com. Thank you.\n";
print MAIL "To: $sender_address\n";
print MAIL "From: webmaster\@domain.com\n";
print MAIL "Reply-to: webmaster\@domain.com\n";
print MAIL "Errors-to: webmaster\@domain.com\n";
print MAIL "Subject: Requested info\n";
print MAIL "\n";
print MAIL "$content\n";
close (MAIL);

exit 0;


First, should I not be exiting 0 here? I mean, there's an exit 99 in the filter line, so I figured that'd suffice.

But what do I know? :clown:

Thanks again!

DogAndPony
01-13-2008, 06:48 PM
Sorry for the ::bump::, but I need to get this done.

Anyone have any ideas on the above?

Thanks!

kitchin
01-13-2008, 10:53 PM
You could condredirect to an autorespond email address. Not sure how people could respond to that message, who the "from" would be.

Another way is to use a Simple Filter to do everything: the pattern matching and sending the autoresponse. The following code relies on the $ENV{'RPLINE'} variable, which contains the email address you should really be responding to, as opposed to "From" or "Reply-to". I think it is the same as the envelope sender, which one of the built-in filters uses, by the way ("SMTP Sender"). The idea of the envelope sender is that you don't want an autoresponder replying to a bounce or to another auotresponder. So those two kinds of emails have an empty envelope sender. I'm assuming $ENV{'RPLINE'} is more or less the same thing. It would be simpler to use $ENV{'NEWSENDER'} which seems to be the same thing, parsed, but who knows.

I also don't know how well the server has scrubbed the email address in $ENV{'RPLINE'}, so I included a basic email validation function, which is not perfect, but is a little better than just checking for '@'.


#!/usr/local/bin/perl
##############################################
# Experimenatal, use at your own risk.
# This file is /big/dom/xACCOUNT/filter/filter.pl
# Use a simple filter in the CNC:
# sh -c '822field | /usr/local/bin/perl /big/dom/xACCOUNT/filter/filter.pl'
##############################################

$mailfrom= 'you@DOMAIN.tld';
$mailsubject= 'Requested info';
# Use -f in sendmail, otherwise outgoing RPLINE is not set correctly.
$sendmail= "/usr/lib/sendmail -t -f$mailfrom";

## Optional logging.
## You must create and *** CHMOD 664 *** the following file if you want to enable logging.
## $LOG= '>>/big/dom/xACCOUNT/filter/log.txt';

undef $/; # slurp...
$insubject= <STDIN>; # subject line(s)

# Test $insubject to decide whether to autorespond.
# Be sure $mailsubject above does *not* match this test to avoid loops!
# The code does *not* test for that.
$autorespond= 0;
if (index($insubject, 'testxtestx') != -1) { ## or use a regex
$autorespond= 'thanks for writing'; ## or read it from a file
}

$ok= 0;
if ($autorespond) {
# Send the autorespond.

# The following variable contains the address we should respond to.
# Should be empty if the incoming message is a bounce or an autorespond itself.
# That prevents some loops.
$rpline= $ENV{'RPLINE'};

if ($rpline=~ /^Return-Path: <([^<>]*)>\s*\z/) {

$rp= $1; # email address to respond to

if ($rp eq '') {
$ok= 'empty rpline, probably a bounce or autorespond';

# Do some validation on $rp to see if it looks like maybe a valid email address.
# Has the server already done this?
} elsif ($err= invalid_email($rp)) {
# err set

# Double-check for loop.
} elsif ($rp eq $mailfrom) {
$err= 'rpline same as mailfrom, stopping loop';

# Send the mail.
} elsif (open(MAIL, "| $sendmail")) {
print MAIL qq(To: $rp
From: $mailfrom
Subject: $mailsubject\n
$autorespond);
close MAIL;
if ($?) {
# Some servers fail closing the pipe and the mail is never sent.
$err= "bad sendmail pipe result [$?]";
} else {
$ok= "SENT MAIL TO [$rp]";
}

} else {
$err= 'cannot open sendmail pipe';

}
} else {
$err= "invalid rpline [$rpline]";

}
}


if ($LOG) {
&dolog("insubject=[$insubject]\nok=[$ok]\nerr=[$err]\nautorespond=[$autorespond]\n");
}

exit 0;



#####################
sub invalid_email {
local($v)= @_;
my $d;

# //////////////////////////////////////////////////////////////////////
# // RFC 3696 and errata, on the user-name part ("local-part"):
# // Without quotes, local-parts may consist of any combination of
# // alphabetic characters, digits, or any of the special characters
# // ! # $ % & ' * + - / = ? ^ _ ` . { | } ~
# // The . char can't appear first, last or repeated.
# //
# // Note, the "quotes" exception uses " and \ chars.
# // To simplify we don't allow that kind of quoting, nor ` or ' chars.
# //
# // The domain part here is standard, except we simplify by assuming the TLD is alpha, length >= 2.
# // As of 2005/04, that is fine: http://data.iana.org/TLD/tlds-alpha-by-domain.txt
# //////////////////////////////////////////////////////////////////////

if ($v eq '') { return('Empty string.') }
if (length($v)>256) { return('Address too long.') } ## RFC 3696 errata

($v,$d)= split(/\@/, $v, 2);
if (length($v)>64) { return('Address username part too long.') } ## RFC 3696

if ($v eq '') { return('User name part missing.') }
if ($v=~ /[`'"]/) { return('Quotes are not allowed.') } ## hassle
if ($v=~ /[^\w\-!#$%&*+\/=?^.{|}~]/) { return('Bad character in user name part.') }
if (substr($v,0,1) eq '.' || substr($v,-1) eq '.' || index($v,'..')!=-1) { return('Bad form (.) in user name part.') }

if ($d eq '') { return('Domain part missing, is it @aol.com?') }
if ($d!~ /^[a-z0-9]([a-z0-9.-]*[a-z0-9])?\.[a-z]{2,}\z/i) { return('Domain part invalid.') }
if ($d=~ /[.-]{2}/) { return('Bad form (.-) in domain part.') }

return 0;
}


#####################
sub dolog {
local($_)= @_;
my $date= localtime();
s/[^\n\x20-\xFF]/_/g;
tr/`<>&/'[]*/;
## for my $k(sort(keys %ENV)) { $_.= "ENV{$k}=[$ENV{$k}]\n"; }
for my $k(qw(NEWSENDER RPLINE)) { $_.= "ENV{$k}=[$ENV{$k}]\n"; }
$_= "\n" . ('=' x 40) . "\n[$date]\n" . $_ . "\n";
if (open LOG) {
print LOG;
close LOG;
}
}

DogAndPony
01-14-2008, 12:31 AM
Kitchin!

WOW!

You truly ROCK!

Works great... I expanded the subject test to include case insensitivity, and lack of a space between the required words.

BTW-- How does this exit? I'm thinking the mail gets passed through to the account, yes? I see just the right number of mails in the user's inbox for that to be the case.

So how do I change the filter line to stop delivery? Would it be:

sh -c '822field | /usr/local/bin/perl /big/dom/xDOMAIN/filter/filter.pl'; exit 99;

...?

Many thanks... You've gone above and beyond!

kitchin
01-14-2008, 01:03 AM
Just change the line
exit 0;
to the value you want. It's right before the first
sub ...

kitchin
01-14-2008, 01:06 AM
Or you probably want to change it to

exit ($autorespond ? 99 : 0);

DogAndPony
01-14-2008, 01:11 AM
Or you probably want to change it to

exit ($autorespond ? 99 : 0);...ahhh, yes. Great idea-- Then again, the client is POPping that box, so if the spam gets thick, I'll kill the conditional.

Thanks again!

CamFraser
01-17-2008, 10:08 PM
relies on the $ENV{'RPLINE'} variable, which contains the email address you should really be responding to, as opposed to "From" or "Reply-to"


I get why you're validating on the envelope sender. I don't understand why you're not sending to the "Reply-to" (if it exists). Isn't that what it's for? I'm no expert, and my understanding could well be wrong. I await enlightenment at the feet of the filter king. :P

I'm seeing a major increase in the use of "Reply-to" by gmail users (most (all?) are small biz types), so it's becoming an issue.

kitchin
01-18-2008, 01:34 PM
Well, I think that's how an autoresponder works, but I could be wrong. I know the thing about using an empty envelope to prevent loops and other undesired responding is right. Two kinds of email have empty envelope senders: bounces and autoresponses. So you never bounce or autorespond to a bounce or an autoresponse. Also, bounces happen at the level of the envelope, not the message, so cannot go to the reply-to. Autoreponse may be similar, or behave the same way anyway...

Here's an experiment.

1. Set up a "Vacation Responder" in gmail, say your address is x@gmail.com

2. Send this message:

#!/usr/bin/perl
$mailto= 'x@gmail.com';
$mailfrom= 'y@example.com';
$replyto= 'z@example.com';
$sendmail= "/usr/lib/sendmail -t -f$mailfrom";

$message= qq(To: $mailto
From: $mailfrom
Subject: test
Reply-to: $replyto
\n
test to gmail!);

open (MAIL, "| $sendmail") or die;
print MAIL $message;
close MAIL;


Google will Vacation Respond to y@example.com, not z@example.com.

You can only do the test once. Google Mail has a feature where it will only Vacation Respond once every 4 days to a particular address. Nice.

kitchin
01-18-2008, 01:35 PM
And yes, I don't know why I'm validating the envelope sender. The FQ server may have already done that, don't know. :)

DogAndPony
01-18-2008, 01:47 PM
And yes, I don't know why I'm validating the envelope sender. The FQ server may have already done that, don't know. :)Could it really hurt? :smile:

Bruce
01-21-2008, 01:00 AM
I get why you're validating on the envelope sender. I don't understand why you're not sending to the "Reply-to" (if it exists). Isn't that what it's for?This way lies madness. The Reply-To: header is intended to direct further discussion to a different email address. It does not indicate who the sender is. Automatic responses should still go to the sender, not to whoever will continue the conversation. It may additionally be argued that the real sender address is contained in the envelope sender (stored by qmail in $SENDER) and not the From: header.

BTW, you also need to be careful to avoid replying to bounces, mailing list posts, bulk email, or other autoresponder messages. In particular, do not respond to messages with an empty sender, a sender of "#@[]" (used by qmail for double bounces), a sender that starts with "mailer-daemon" ignoring case, or a sender containing no "@". Furthermore, messages containing one or more of the following headers are either mailing list or self-identified bulk messages, and should not be responded to:

List-ID: (as per RFC 2919)
Mailing-List:
X-Mailing-List:
X-ML-Name:
List-Help:
List-Unsubscribe:
List-Subscribe:
List-Post:
List-Owner:
List-Archive: (as per RFC 2369)
Precedence: containing either "junk", "bulk", or "list"

Also, qmail stamps all messages with a Delivered-To: header, and when it invokes filters, it makes note of that line in $DTLINE. If that line appears in a message, it is looping and no response should be generated.

Sadly, there are myriads of ways in which autoresponder programs can be made to either loop or otherwise misbehave by responding to other automated messages. The above should account for most of the cases, but I'm sure something new is bound to come up.