Web Interface to procmail ??

I am currently developing a custom CMS and will hopefully be providing hosting services for small business / personal sites - and I would like to offer mail services.

Postfix / Dovecot side of things I think I can handle, but it would be nice to offer a web interface to procmail to handle server side basic filtering of incoming mail (users will NOT be given shell access). Procmail is what I personally use, and works best because it doesn't matter what client I connect with, the messages go where they are suppose to go.

I can code my own basic interface, but if one already exists, that would be better since it (hopefully) will already have been through testing / bug squashing. Anyone know of one?

The way I probably do it is to store the filtering information in XML that the web app modifies, and then use background process perl or php-cli to validate the XML and write a fresh .procmailrc upon modification of the XML by the web app, so the app doesn't need to execute command or have write permission to the .procmailrc - but if something like this already exists, I'd love to know.

4 Replies

Hmm. Not entirely sure I'd throw XML into the situation; if I were doing it all over again, I'd probably just lob stuff into a database with a schema something like:

class Recipe:
  user = models.ForeignKey(User)  # the recipe belongs to this user
  order = models.IntegerField()  # sort order for this recipe
  flags = models.CharField()  # flags; could probably go to town with a M2M field or something, too
  action = models.TextField()  # exactly one action line

  @property
  def block(self):
    "Returns a configuration stanza appropriate for .procmailrc"
    recipe = (":0 %s\n" % self.flags) if self.flags else ":0\n"
    for condition in self.condition_set.all():
      recipe += "* %s\n" % condition
    recipe += self.action + "\n"
    return recipe

class Condition:
  condition = models.TextField()  # the regexp to match
  recipe = models.ForeignKey(Recipe)  # the condition belongs to this recipe

# instantiated like:
my_recipe = Recipe.objects.create(
  user = User.objects.get(username='jboehner')
  order = 10  # like MX records ;-)
  flags = 'c'  # generate a carbon copy
  action = '! president@whitehouse.gov'  # forward to POTUS
)
my_recipe.condition_set.create(
  condition = "^From: vicepresident@whitehouse.gov"  # match that sneaky under-the-table-dealin' joe
)

Then simply, as a user that can write to all of these locations:

from models import User, Recipe

for user in User.objects.filter(recipes_set__count__gt=0):
  with open('%s/.procmailrc' % user.homedir, 'w') as fp:
    for recipe in Recipe.objects.filter(user=user).order_by('order'):
      fp.write(recipe.block)

And bam, out they come. Apologies for the Django-specificness here; it was the easiest way to bang this out in a hurry. Testing, error handling, user interfacing, not writing out all the .procmailrc files every time this runs, the inevitable race conditions, and all that other stuff are left as an exercise for the reader.

I think Webmin has a module that does this – it might be worth looking through its source for inspiration, too.

Your response is much appreciated, but I fundamentally disagree with using a DB for this. A proper RDBMS is overkill and NoSQL is really best suited for cases with lots of reads and writes and typically resides in memory.

I doubt lots of reads and writes are an issue here, most people who even make use of server side filtering will alter their filtering less than once a week, XML is excellent for file based data storage that doesn't change often, and most scripting languages have tools specifically designed to extract the needed info from it.

Map the schema to XML and you're set. I could go either way; it's just that in the environments I use, it adds another layer of handling and bloat that doesn't seem necessary for local data storage.

I get the feeling PHP generally doesn't make database access easy, which probably changes the equation significantly. In any case, use the tools you got.

database access is easy in php.

I just don't like to use them where flat file seems better (cases when relational queries and speed aren't necessary) - especially if/when I need to move accounts around from one server to another.

Reply

Please enter an answer
Tips:

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct