On Fri, Jun 7, 2013 at 1:44 PM, Hrvoje Niksic <hniksic@xemacs.org> wrote:
Todd Lyons <tlyons@ivenue.com> writes:
Hi all, I'm embedding python into a c app (exim, the MTA). I've gotten code in to initialize python, read in the embedded script, call various functions within that script, and appropriately return data from those functions back to the exim call.
I've gotten quite a bit further now that when I sent in that email. My code can be seen at:
http://git.exim.org/users/tlyons/exim.git/shortlog/refs/heads/master_volatil...
One thing that does not make sense yet though, is how to assemble the data to be passed to the python functions. Imagine the following simple function:
def simple_math(a,b): return a+b
In this case, a and b are integers or long integers. How can I format that incoming data without knowing in advance that there are two arguments to this function and that are both are integers?
Obviously the scripts and your embedding module need to agree on an API;
Yes. I've set the following limitations:
- All args passed to a python function are converted to strings. It is up to the function to include an implicit cast
- No dicts, tuples, etc are passed. Just simple strings or numbers (which are converted to strings).
- Return from a function is always cast back to a string, which is what exim prefers.
This is subject to changes and additions.
The "exim" module is provided by you as the embedder and serves for the
I plan to make two basic functions:
- exim.log - allows functions in python to log info to the exim logging system.
- exim.expand - allows functions in python to access read-only exim expansion variables (i.e. headers, body, recipients, senders, configuration directives, etc)
script to access exim's functionality. (Otherwise the script would be executed as just another Python script, which would not be too useful from the embedding standpoint.)
Correct, it's not executed as a standalone script.
In other words, you're not calling the functions from the script: the script is calling your functions. The only time that you are calling a script's function is when the script passes a function to one of your functions that accepts a callback argument -- but even then the
Rather it's "imported" and any of the functions may be called from any part of the exim ACL's at any time.
interface of the function is regulated by you, and it is you who prescribe how many and what kind of arguments it will accept.
I had to do a little work to figure out how many arguments the function is written to accept, and then double check that the call from the exim ACL's is using the correct number, and only then convert the variables and make the function call.
Here is a more real-world example of the way I or an exim admin would use it. This is how I use the embedded perl to count the number of a specifc header and set a variable which will later in the SMTP session be used to reject this email:
warn !sender_domains = lsearch;/etc/exim/quarantine_skip_domains set acl_m_h_count = ${perl{count_headers}{from}} condition = ${if >{$acl_m_h_count}{1}} control = fakereject/Quarantined: RFC 5322 limits an email to one From header, yours had $acl_m_h_count.\nYour email is being kept for evaluation. If it was a legitimate\nmessage, it may still be delivered to the target recipient(s).\nRejected sender domain: $sender_address_domain set acl_m_quarantine = 1
The perl function count_headers() gets passed one variable: "from", stored in a variable named $header. The perl function is written to load the headers from the message (a big text blob), convert it to an array, then loop through the array looking for /^$header:/, counting how many it finds. Then it returns the number.
The "condition" line simply compares the returned value to 1, and if it's greater than one, it will set the fakereject and quarantine settings.
My goal with this explanation is to show that the intended usage of python is very simple. It will not be used for threading. It will not be used for large amounts of the processing of the email. It is intended to do small calculations (reputation querying, reputation scoring, detecting anomalies in the email, etc). The goal is not to write a massive python system. The goal is to make parts of the exim ACL configuration easier by implementing some of the more complicated-to-do-in-exim parts in a language you are familiar with. I use the embedded perl a lot, and I am adding embedded python since a few people have asked for the same python functionality.
My code currently does not handle exceptions that occur in the python functions. I'm still working on that.
What would be the point of such a function, before or after the edit? You cannot just call any random function from a script: you need to execute the script, providing a way for the script to call *you*.
The random function would be called from exim's ACL as it's processing a message.
...Todd
The total budget at all receivers for solving senders' problems is $0. If you want them to accept your mail and manage it the way you want, send it the way the spec says to. --John Levine