1

Postfix/Dovecot

I want to create my simple custom autoresponder for Postfix, in Python, for instance. I don't need any third-party ready-to-user one.

I want it to have some custom conditions such as "autoreply only when 'from == A' or/and 'to == B' or/and "there's have been no autoprely to that email today', etc...

I've found out that I'll need to use either content_filter or spawn in master.cf. Not milter, because milter is triggered because a message has been put into a queue, whereas I'll need to autoreply to messages that already have come through. Probably.

Other better options?

How to do implement that?

Rajaru
  • 11
  • 1
  • 2
  • if you have python knowledge, then its simple. just write your conditions and pipe your script into .qmail file in case if you are using Maildir format. – Zareh Kasparian Oct 20 '19 at 10:33
  • @ZarehK "just write your conditions" won't me at all. There must be a certain API, variables that are passed to my script and hundred of other thing. If everyone could "Just write", no one would ever ask a question here. – Rajaru Oct 20 '19 at 10:38
  • i have the same scenario, which works for me, without any API or complexity you said. as soon as you get the email into the user inbox, python script will be triggered and do what ever you have coded. just that simple. – Zareh Kasparian Oct 20 '19 at 10:44
  • @ZarehK a python won't be triggered. Just that simple. – Rajaru Oct 20 '19 at 15:17

1 Answers1

1

I don't think there is a simple (ie trivial) way to do this, and there are most likely multiple solutions.

I implemented this for my mail system (which hosts a fair number of boxes and domains) and hooks into a custom written CMS (BLISS3) as follows:

Added to /etc/postfix/master.cf

autoresponder
        unix  -       n       n       -       -       pipe flags=Rq
    user=mailuser argv=/etc/postfix/scripts/autoresponder.php ${sender} ${recipient}

This will execute my script autoresponder.php, parsing the sender and recipient. You could write the appropriate autoresponder in python if you prefer.

I then added the following to /etc/postfix/transport

  .autoresponder autoresponder:

This command triggered the autoresponder as the appropriate transport when I foward a copy of an email to a special "user@domain.name.autoresponder" email. If you are looking to do this on a small scale, you can probably simply replace ".autoresponder" with your email address if you don't need to keep a copy of the incoming email.

All thats left is the autoresponder script itself.

My PHP script looks as follows:

#! /usr/bin/php
<?php
    #########################################################################################
    # Davids Autoresponder for BLISS3.  This has been designed to query a database to pull
    # the message contents from.
    #########################################################################################

    # How frequently to resend messages, ie only send 1 message per person every period.
    # The idea of this is to prevent mail loops.

    $period='1 day';
    $dbconnection="mysql://username:password@127.0.0.1/mail";

    # We take the input, and strip off the ".autoresponder" part, revealing our actual address.
    $from=mysqli_real_escape_string(substr($argv[2],0,-14));
    $to=mysqli_real_escape_string($argv[1]);

    #########################################################################################

    include "/usr/share/php/adodb/adodb.inc.php";
    $ADODB_FETCH_MODE=ADODB_FETCH_ASSOC;
    $conn=ADONewConnection($dbconnection);

    $theargs=$argv[0]." ".$argv[1]." ".$argv[2]."\n";
    syslog (LOG_WARNING,"Called autoresponder - $theargs");

    # Check if the email is in our database of recently sent messages.   If not, add it.
    # If it is, do not resend.   Also do not send emails if from = to

    $sent=$conn->GetAll("select count(uid) from autoresponded where mailfrom='$from' and mailto='$to' and date > current_timestamp - interval '$period'");

    if ( $sent[0]['count'] < 1 and $from != $to )
    {
        $q=$conn->Execute("insert into autoresponded (date,mailfrom,mailto) values (current_timestamp,'$from','$to')");

        $messages=$conn->GetAll("select ar_subject, ar_body from virtual where mailfrom='".$from."'");

        # No point in sending if we don't have a message to send.
        if ($messages[0]['ar_subject'] != '' or $messages[0]['ar_body'] != '')
        {
            $headers='From: '.$from."\r\n";
            $subject=pg_unescape_bytea($messages[0]['ar_subject']);
            $message=pg_unescape_bytea($messages[0]['ar_body']);
            mail ($to, $subject, $message,$headers);
        }
    }

    # Automatically clean up after ourself.
    $cleanup=$conn->Execute("delete from autoresponded where date < current_timestamp - interval '$period'");

As you can see I use a simple database to keep track of when an email was last sent to a given address which I check to make sure I don't send more then 1 response per sender per day to prevent mail loops and other annoyances. The actual responder email is done with a simple mail command (6th last line)

In case you care to do something very similar,the schema for my autoresponded table is:

                                        Table "public.autoresponded"
  Column  |            Type             | Collation | Nullable |                  Default                   
----------+-----------------------------+-----------+----------+--------------------------------------------
 uid      | integer                     |           | not null | nextval('autoresponded_uid_seq'::regclass)
 date     | timestamp without time zone |           |          | now()
 mailfrom | character varying           |           |          | 
 mailto   | character varying           |           |          | 

The lines referring to ar_header and ar_body pull appropriate autoresponder header and body from a database. You can create/modify your own tables to do this or hard code them.

Hopefully the above gives you a solid starting point for doing what you want. As per @TobiM comment below, use this as the basis for your code - you probably should replace the sql with prepared statemements. I've not actually checked the modified version works, and it assumes a mysql database.

davidgo
  • 5,964
  • 2
  • 21
  • 38
  • 1
    Thank you for sharing this. And sorry for necroing, but it's dangerous in my opinion, as you are completely neglecting input validation. What if my "from" address is "me','');drop table autoresponded;--"? Not sure how to get this through postfix, but there is almost always a way, and be it using escape characters or unicode. – TobiM Oct 28 '21 at 13:11
  • @TobiM Good call. he $from and $to should be sanitized, and it would be better if prepared statements were used as well. I'll modify the above to at least use mysqli_real_escape_string – davidgo Oct 28 '21 at 22:36