Well, I had an epiphany over the US Thanksgiving holiday weekend (or maybe it was a brain aneurism) so I've spent some late nights and some time stolen from work and come up with a first pass at embedding Python in Exim.
I imagine that embedding Python in Exim will be interesting to those folks writing virus scanners or for VERY tight integration of Mailman with Exim. You can also do all of those things that you can do with the Perl string expansions.
Note: this code is VERY lightly tested. It compiles on my home hacked-up RedHat Linux system and works on a couple of simple tests that I've done, but no real e-mails have passed through this code yet. I'm tossing this out for public comment on the design and implementation... It's been a while since I've done a lot of C coding.
Unfortunately, you must embed Python 2.0. This is because previous versions of Python used a hacked copy of PCRE as it's regular expression engine that conflicts with the copy of PCRE in Exim.
Starting in Python 2.0 the default regular expression is a completely new one called SRE that supports Unicode. The PCRE-based regular expression engine is still included in Python 2.0 but I get around that by creating a private copy of the Python library and delete the offending object modules. You could do that in versions of Python prior to 2.0 but a lot of the standard library depends on regular expressions I didn't think that there was much point in trying to make the embedding work with 1.5.2 or 1.6.
Well, on to the patch itself. The patch is made against Exim v3.20. After you've patched the source code, you need to set four variables in the Local/Makefile:
EXIM_PYTHON=python.o PYTHON_INC=-I/usr/local/include/python2.0 PYTHON_LIB=/usr/local/lib/python2.0/config/libpython2.0.a PYTHON_EXTRA_LIBS=-lpthread -ldl -lutil
Then build Exim as usual.
There are three runtime directives that control the embedding, all of which are optional:
python_at_start boolean - force startup of Python interpreter python_module_paths colon separated list of paths to append to sys.path python_initial_import colon separated list of modules to import at interpreter initialization time
There are also two new command line switches -ys and -yd that will force an immediate startup of Python or delay startup, overriding python_at_start.
Then you use it:
${python{<module>.<function>}{<arg1>}...}
You must specify the module name and the function name. The named module will be imported automatically. And currently you must specify a function that returns a string (I'll look into adding accessing attributes, methods of classes, and converting non-strings into strings). There can be up to eight arguments. Each argument will be expanded by Exim before it's passed to the Python interpreter.
Your python code has access to a module called "exim", which currently defines two things:
exim.expand_string(s) which calls back to Exim to expand a string and exim.error which is an exception object used by expand_string
One simple example:
${python{string.upper}{$local_part}}
Error reporting isn't the best right now, I'm going to look into formatting exceptions and tracebacks for more meaningful error messages.
Well, I think that's the gist of it. I'll work on patches to the documentation when this thing becomes more stable. As I said before I've only lightly tested this... beware!
Comments, criticisms, suggestions, flames welcome...
Jeff
participants (1)
-
Jeffrey C. Ollie