Python filter for postfix - python

I am trying to make a simple Python filter for postfix, to add in a 'Reply-to' header to certain messages.
What I've done so far is to take the email from stdin, and parse it into an email object like so:
raw = sys.stdin.readlines()
msg = email.message_from_string(''.join(raw))
Then I've played with headers etc.
msg.add_header('Reply-to', 'foo#bar.com')
And now want to re-inject that back into postfix. Reading the filter readme associated with postfix, I should pass it back using the 'sendmail' command. However, I'm not sure how to pass the email object over to sendmail, for example using subprocess's 'call()' or whether I should use the smtplib's 'smtplib.SMTP()'?
What would be the 'correct' method?
Thanks

You should be able to use both methods, but smtplib.SMTP() is more flexible and makes the error handling easier.
If you need an example, have a look at my framework for python filters:
https://github.com/gryphius/fuglu/blob/master/fuglu/src/fuglu/connectors/smtpconnector.py#L67
the re_inject method does exactly that (FUSMTPClient is a subclass of smtplib.SMTP), so basically it boils down to:
client = smtplib.SMTP('127.0.0.1',<yourportnumber for the receiving postfix instance>)
client.sendmail(<envelope from>, <envelope to>, <yourmessageobject>.as_string())

Related

How can I fetch the SMTP headers after sending email?

I'm able to send emails using smtplib with ease. What I'm struggling with is reading the actual headers that were sent one. Specifically, I'm looking to read the Message-ID and References.
I thought at first that sendmail() would return them, but it does not.
Found that I'm able to redirect smtpilb.stderr to my own function and parse out the data that I need. Is there a better way that would allow me to do say:
headers['References']
If you use sendmail() I am not sure how to access the headers, because you don't have a Message object in that case. However, if you use send_message instead - which is very similar to sendmail() - and pass it an email.message.Message object, then all of the email message headers and their values are stored in a dict in your Message object. So e.g., Message-ID can be accessed from an email message object msg with msg['Message-ID'], subject can be accessed using msg['Subject'], etc. I don't think anything will be stored in message-id unless you put it there yourself though. You can 'roll your own' Message-ID using make_msgid() from email.utils:
from email.utils import make_msgid
msg['Message-ID'] = make_msgid()

Forwarded Email parsing in Python/Any other language?

I have some mails in txt format, that have been forwarded multiple times.
I want to extract the content/the main body of the mail. This should be at the last position in the hierarchy..right? (Someone point this out if I'm wrong).
The email module doesn't give me a way to extract the content. if I make a message object, the object doesn't have a field for the content of the body.
Any idea on how to do it? Any module that exists for the same or any any particular way you can think of except the most naive one of-course of starting from the back of the text file and looking till you find the header.
If there is an easy or straightforward way/module with any other language ( I doubt), please let me know that as well!
Any help is much appreciated!
The email module doesn't give me a way to extract the content. if I make a message object, the object doesn't have a field for the content of the body.
Of course it does. Have a look at the Python documentation and examples. In particular, look at the walk and payload methods.
Try get_payload on the parsed Message object. If there is only one message, the return type will be string, otherwise it will be a list of Message objects.
Something like this:
messages = parsed_message.get_payload()
while type(messages) <> Types.StringType:
messages = messages[-1].get_payload()

How can I uniquely identify an IMAP message after copying it?

I want to move an IMAP message from INBOX to INBOX/Archive using python's imapclient library, which I'm doing basically like this:
def archive_message(imap, message_id):
imap.copy([message_id], getOptions().imap_archive_folder)
imap.delete_messages([message_id])
However, this loses my reference to the message. What I want to do is to store an identifier for the message that will allow me to look up the message later, using something like this:
def retrieve_message(imap, MYSTICAL_STORED_ID):
imap.select_folder(getOptions().imap_archive_folder)
return imap.fetch([MYSTICAL_STORED_ID], parts=["RFC822"])
What ID should I / can I use for this, and how would I do the lookup part of this?
The value in the Message-Id header is supposed to be unique per email message.

Passing a Python list using JSON and Django

I'm trying to send a Python list in to client side (encoded as JSON). This is the code snippet which I have written:
array_to_js = [vld_id, vld_error, False]
array_to_js[2] = True
jsonValidateReturn = simplejson.dumps(array_to_js)
return HttpResponse(jsonValidateReturn, mimetype='application/json')
How do I access it form client side? Can I access it like the following?
jsonValidateReturn[0]
Or how do I assign a name to the returned JSON array in order to access it?
Actually I'm trying to convert a server side Ajax script that returns an array (see Stack Overflow question Creating a JSON response using Django and Python that handles client side POST requests, so I wanted the same thing in return with Python, but it didn't go well.
The JSON array will be dumped without a name / assignment.
That is, in order to give it a name, in your JavaScript code you would do something like this:
var my_json_data_dump = function_that_gets_json_data();
If you want to visualize it, for example, substitute:
var my_json_data_dump = { 'first_name' : Bob, 'last_name': smith };
Also, like Iganacio said, you're going to need something like json2.js to parse the string into the object in the last example. You could wrap that parsing step inside of function_that_gets_json_data, or if you're using jQuery you can do it with a function like jQuery.getJSON().
json2.js is still nice to have, though.
In response to the comment (I need space and markup):
Yes, of course. All the Python side is doing is encoding a string representation (JSON) for you. You could do something like 'var blah = %s' % json.dumps(obj_to_encode) and then on the client side, instead of simply parsing the response as JSON, you parse it as JavaScript.
I wouldn't recommend this for a few reasons:
You're no longer outputting JSON. What if you want to use it in a context where you don't want the variable name, or can't parse JavaScript?
You're evaluating JavaScript instead of simply parsing JSON. It's an operation that's open to security holes (if someone can seed the data, they might be able to execute a XSS attack).
I guess you're facing something I think every Ajax developer runs in to. You want one place of truth in your application, but now you're being encouraged to define variables and whatnot in JavaScript. So you have to cross reference your Python code with the JavaScript code that uses it.
I wouldn't get too hung up on it. I can't see why you would absolutely need to control the name of the variable from Python in this manner. If you're counting on the variable name being the same so that you can reference it in subsequent JavaScript or Python code, it's something you might obviate by simply restructuring your code. I don't mean that as a criticism, just a really helpful (in general) suggestion!
If both client and server are in Python, here's what you need to know.
Server. Use a dictionary to get labels on the fields. Write this as the response.
>>> import json
>>> json.dumps( {'vld_id':1,'vls_error':2,'something_else':True} )
'{"vld_id": 1, "something_else": true, "vls_error": 2}'
Client. After reading the response string, create a Python dictionary this way.
>>> json.loads( '{"vld_id": 1, "something_else": true, "vls_error": 2}' )
{u'vld_id': 1, u'something_else': True, u'vls_error': 2}

How do I validate the MX record for a domain in python?

I have a large number of email addresses to validate. Initially I parse them with a regexp to throw out the completely crazy ones. I'm left with the ones that look sensible but still might contain errors.
I want to find which addresses have valid domains, so given me#abcxyz.com I want to know if it's even possible to send emails to abcxyz.com .
I want to test that to see if it corresponds to a valid A or MX record - is there an easy way to do it using only Python standard library? I'd rather not add an additional dependency to my project just to support this feature.
There is no DNS interface in the standard library so you will either have to roll your own or use a third party library.
This is not a fast-changing concept though, so the external libraries are stable and well tested.
The one I've used successful for the same task as your question is PyDNS.
A very rough sketch of my code is something like this:
import DNS, smtplib
DNS.DiscoverNameServers()
mx_hosts = DNS.mxlookup(hostname)
# Just doing the mxlookup might be enough for you,
# but do something like this to test for SMTP server
for mx in mx_hosts:
smtp = smtplib.SMTP()
#.. if this doesn't raise an exception it is a valid MX host...
try:
smtp.connect(mx[1])
except smtplib.SMTPConnectError:
continue # try the next MX server in list
Another library that might be better/faster than PyDNS is dnsmodule although it looks like it hasn't had any activity since 2002, compared to PyDNS last update in August 2008.
Edit: I would also like to point out that email addresses can't be easily parsed with a regexp. You are better off using the parseaddr() function in the standard library email.utils module (see my answer to this question for example).
The easy way to do this NOT in the standard library is to use the validate_email package:
from validate_email import validate_email
is_valid = validate_email('example#example.com', check_mx=True)
For faster results to process a large number of email addresses (e.g. list emails, you could stash the domains and only do a check_mx if the domain isn't there. Something like:
emails = ["email#example.com", "email#bad_domain", "email2#example.com", ...]
verified_domains = set()
for email in emails:
domain = email.split("#")[-1]
domain_verified = domain in verified_domains
is_valid = validate_email(email, check_mx=not domain_verified)
if is_valid:
verified_domains.add(domain)
An easy and effective way is to use a python package named as validate_email.
This package provides both the facilities. Check this article which will help you to check if your email actually exists or not.

Categories