Adding autoreply, forwarding and spam control to Horde/Imp
Horde is a suite of web-applications designed to implement mail, calendaring and other functionalities on top of an existing mail server, IMP is the module of Horde that implements the web mail functionality. In short, is an easy way to access your e-mails from everywhere in the world were you have an internet connection and a web browser.
Developed in PHP, Horde has a lot of modules, each one dedicated to add a specific function like shared calendar, notes, mailing, file management and the like.
You can found more details on http://www.horde.org.
Sometimes ago, my users starte asking for an 'easy and simple' way to setup autoreply (the kind of silly reply you get when someone is in holiday), autoforwarding of the mail from one account to another and a way to trim/enable/disable the spam filtering. Since it looks like "normal" users can't handle a telnet session, I dived into Horde's code to see if I could simply add such functionalities on top of IMP.
Horde has a module for that, but it looked like an overkill and it wasn't suited for antispam, so I discarded it.
The easiest way to setup an autoforward is to create a simple file named .forward in your homedir on the mail server. Ok, QMail doesn't lool at the .forward file unless you tell him to, but that's a detail.
For autoreply, usually procmail is used to process the incoming mail and send to the sender a mail with a standard text in response.
For spam control, trought the amazing procmail, the incoming mail are sent to a spam control program and then marked (by the spam control program itself or by an external utility) and discarded or filed into the inbox or a specific folder (it depends by the user).
So, to make a long story short, is a matter of writing a couple of files in the user's home dir. Easy huh?
Yeah, having the possibility of writing in the user's home dir!. The problem is that, most of the time the mail server CAN'T! Ok, you could implement SIEVE, that is an 'extension' (let's call it like this) to the SMTP protocol that allow a mail server to do this kind of stuff, but implementing it if the server doesn't already support it is not trivial, and I had to modify 36 servers, not one. So I voted agains it.
The alternativo is to write the file on the web server itself and then copy the files to the mail server using FTP or SFTP. This could be not so safe due to the fact that ftp isn't really "safe", but since most of the time the mail server and the web server are on the same LAN, I decided that was 'safe enough'.
note: in my code I implemented the FTP protocol, if you want to go for SFTP feel free.
For the autoreply I decided to be a little sofisticated and added a cache of sender, so the system will send ONE mail to the sender only, instead of sending one mail for every mail received.
The procmail's script is something like this:
:0 Whc: /home/USERNAME/.vacation.lock * !^FROM_DAEMON * !^X-Loop: USERNAME@SERVER * !^Precedence: bulk | FORMAIL -rD 8192 /home/USERNAME/.vacation.cache :0 ehc | (FORMAIL -rA"Precedence: junk" -A"X-Loop: USERNAME@SERVER" ; \ cat /home/USERNAME/.vacation.text) | SENDMAIL -oi -t
The script is compose by two parts: the first one store the address in the 'cache' file and discard everything that looks like an autoreply on his own or messages from mailing lists, the second part strip everything from the original message, get the text from the 'vacation.text' message and compose a reply mail. The mail is then sent using sendmail (or whatever is used at the mail server).
In both blocks USERNAME must be substituted with the user's name and SERVER must be the FQDN of the mail server itself. SENDMAIL and FORMAIL have to be replaced by the full path of the sendmail program (or whatever is used to send mail) and the formail program.
In short, is matter of write some code that 'compose' the script, replace the variables with the right path/names and then upload it to the mail server.
This is a little more complex, but substantially is matter of writing a procmail script that call the spam control program and eventually mark the mail if the result is positive. No big deal.
It is a little more complicated if you use spamassassin and you want to give users the ability to alter their own profile. In this case the program need to read the user_pref file of spamassassin and allow the user to alter the points of each rule.
The procmail script to call is the following:
:0fw | SPAMASSASSIN :0 * ^X-Spam: yes; | /usr/local/sbin/mark.plWhat's "mark.pl"? Is a simple script that alter the subject of the e-mail. I decided to use an external script because I wasn't sure if spamassassin was rigged to alter the subject line or not.
As said, horde is composed by a lot of modules, each one dedicated to a specific task. Adding a new module is, usually, matter of copying the files in the main directory and (eventually) adjust some configuration file.
Unfortunately, I quickly discovered that adding a function to an existing module is way more complicated... so I dived into the Horde code.
What I did was the following: I picked an existing function, copied and then modified the code to suit my need. The result is an "addendum" that looks mostly like the original thing.
The biggest problem was to find out where Horde store details about the user, like the username, which mail server and the password. I quickly discover that the password is stored encrypted even in the session variable. Not a big deal: decrypt is matter of calling a function (what's the point then?).
After a while (a lot), I succeded in having an interface that was working and did what I wanted. Of course, to have the ftp part working your PHP need to have support for the ftp protocol.
The whole code is splitted in two main parts: spam.php is the real program, he uses a "template" in templates/spam/main.inc to display the user interface. I admit that using a template for such program is an overkill, but I wanted to keep the same basic structure of the existing modules. Once clicked on the "update" button, spam.php will create the procmail file for the user in the /tmp directory, then use the ftp api to upload the file on the mail server.
To store and mantain the actual state of things (autoreply enabled or disabled and so on) I write 3 simple support file in the same homedir, those files have names like "ooo" (Out Of Office), and they contains "enabled" or "disabled" only. I decided against the use of "user's preferences" because that require a database (mysql or ldap).
To get user's messages is necessary to add the messages to the help files, that are in the locale/ directory. Those files are generated using the gettext suite. At the moment I have messages for english, spanish and italian.
To connect my piece of code with the rest there are two ways: adding it to the "Preferences" menu or adding an icon in the main menu. The first one is available only if there is a preference menu, and this depends if the installation is using a database of some sort to store the preferences, the second is always avaliabel.
To add a voice in the preference menu you need to modify the configuration file for the preferences adding a 'block' detailing the new preference and which code is used to handle it, to add a voice in the main menu you need to add a line in the menu template calling the Horde's API to display the icon.
The whole module is here.
For the installation I decided to create a full installation scripts that is able to alter configuration files, adding messages and the like. Basically, you call the install script and, if you have all the required programs and support tool, it will be installed in a pinch.
Note that the install script is designed to install the module on the very same machine that runs Horde and the mail server, if the two are on different machines what you need to do is to edit the installation script and inserting the full path of the required tools in there.
If you have all on the same machine, you run the script and this will morphe your "vanilla" installation of horde into an installation WITH the extra module. I tested on a couple of systems, so if it DOESN'T work on yours, start debugging.
Warning: there is no de-install, so get a backup before starting.
Davide Bianchi, works as Unix/Linux administrator for an hosting provider in The Netherlands.
Do you want to contribute?
This site is made by me with blood, sweat and gunpowder, if you want to republish or redistribute any part of it, please drop me (or the author of the article if is not me) a mail.
This site was composed with VIM, now is composed with VIM and the (in)famous CMS FdT.
This site isn't optimized for vision with any specific browser, nor
it requires special fonts or resolution.
You're free to see it as you wish.