Commit b6799fd0 authored by Julien Muchembled's avatar Julien Muchembled

Rewrite sendMailToERP5

Previous version used CMFMailIn and didn't work anymore.
Remove useless sendMailToZope.py script.

Like old sendMailToERP5.py, it can be used directly to deliver mails from
postfix to ERP5.

Main improvements over previous implementation are:
- fix bug losing mail silently if ERP5 instance returns a HTTP status >= 300
- ingestion map to get options according to the recipient

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@36717 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 33745d9f
#!/usr/bin/python
import os, subprocess, sys, textwrap, traceback, urllib, urlparse
# Example of configuration of postfix to deliver to ERP5:
# - Add the following lines to master.cf:
# erp5 unix - n n - - pipe
# flags=FR user=erp5 argv=/path_to/sendMailToERP5 --ingestion_map=...
# recipient=${recipient}
# - Tell smtpd service to use the new filter, by adding:
# -o content_filter=erp5:
class HTTPError(IOError):
def __init__(self, errcode, errmsg, result):
self.__dict__.update(errcode=errcode, errmsg=errmsg, result=result)
def __str__(self):
return '%s %s' % (self.errcode, self.errmsg)
class urlopen(urllib.FancyURLopener, object):
"""Open a network object denoted by a URL for reading
Raise a HTTPError exception if HTTP error code is not 200.
"""
def __new__(cls, *args, **kw):
self = object.__new__(cls)
self.__init__()
return self.open(*args, **kw)
def http_error(self, url, fp, errcode, errmsg, headers, data=None):
raise HTTPError(errcode, errmsg,
self.http_error_default(url, fp, errcode, errmsg, headers))
class Message(object):
def __init__(self, *args):
for arg in args:
k, v = arg.split('=', 1)
if not k.islower():
raise ValueError
old = getattr(self, k, None)
if old is not None:
if type(old) is list:
old.append(v)
continue
v = [old, v]
setattr(self, k, v)
recipient_list = self.__dict__.pop('recipient', [])
if isinstance(recipient_list, basestring):
recipient_list = [recipient_list]
self.recipient_list = recipient_list
def __call__(self, portal=None, **kw):
if portal is not None:
scheme, netloc, path, query, fragment = urlparse.urlsplit(portal)
if query or fragment:
raise ValueError
user, host = urllib.splituser(netloc)
if user is None:
password = None
else:
user, password = urllib.splitpasswd(user)
user = kw.pop('user', user)
if user is not None:
password = kw.pop('password', password)
if password is not None:
user = '%s:%s' % (user, password)
host = '%s@%s' % (user, host)
url = urlparse.urlunsplit((scheme, host, path.rstrip('/'), '', '')) + \
'/portal_contributions/newContent'
kw['data'] = sys.stdin.read()
try:
result = urlopen(url, urllib.urlencode(kw))
except HTTPError, e:
if e.errcode >= 300:
raise
result = e.result
result.read() # ERP5 does not return useful information
# Now, we could reinject the message to postfix for local delivery,
# using /usr/sbin/sendmail, depending on a 'sendmail' option. However,
# we would get duplicate mails if either ERP5 or sendmail fail.
# It is better to do this from the ERP5 instance itself, by activity.
class SimpleIngestionMap(object):
"""Simple implementation of ingestion map, using a Python file as database
This class maps recipients to parameters for portal_contributions/newContent
"""
def __init__(self, ingestion_map_filename):
fd = file(ingestion_map_filename)
g = {}
try:
exec fd in g
finally:
fd.close()
self._map = g['ingestion_map']
def __call__(self, message, **kw):
for recipient in message.recipient_list:
recipient = self._map.get(recipient)
if recipient:
kw.update(recipient)
break
return message(**kw)
def getOptionParser():
from optparse import IndentedHelpFormatter, OptionGroup, OptionParser
class Formatter(IndentedHelpFormatter):
"""Subclass IndentedHelpFormatter to preserve line breaks in description"""
def format_description(self, description):
return ''.join(IndentedHelpFormatter.format_description(self, x)
for x in description.split('\n'))
parser = OptionParser(usage="%prog [options] [<key>=<value>]...",
formatter=Formatter(), description="""Positional \
arguments defines variables that are used by ingestion maps to determine \
options to send to ERP5. Currently, only 'recipient' key is used.
This tool can be used directly to deliver mails from postfix to ERP5, \
by using it as a filter (cf document of /etc/postfix/master.cf).""")
_ = parser.add_option
_("--portal", help="URL of ERP5 instance to connect to")
_("--user", help="use this user to connect to ERP5")
_("--password", help="use this password to connect to ERP5")
_("--file_name", help="ERP5 requires a file name to guess content type")
_("--container_path", help="define where to contribute the content"
" (by default, it is guessed by ERP5)")
#_("--portal_type", default="Mail Message")
group = OptionGroup(parser, "Ingestion map", """Above options can be \
overridden according to recipients, using a Python module as ingestion map \
database. The module must define an 'ingestion_map' variable implementing \
'get(recipient) -> option_dict'. Example:
ingestion_map = {
'foo@bar.com': dict(user='foo', password='12345'),
'patches@prj1.org': dict(file_name='prj1.patch'),
'spam@example.invalid': dict(portal=None), # drop
}""")
group.add_option("--ingestion_map", help="get options from this file,"
" according to recipients")
parser.add_option_group(group)
_ = group.add_option
parser.set_defaults(file_name="unnamed.eml")
return parser
def main():
parser = getOptionParser()
options, args = parser.parse_args()
message = Message(*args)
default = {}
for option in parser.option_list:
dest = option.dest
if dest not in (None, 'ingestion_map'):
value = getattr(options, dest)
if value is not None:
default[dest] = value
if options.ingestion_map:
SimpleIngestionMap(options.ingestion_map)(message, **default)
else:
message(**default)
if __name__ == '__main__':
try:
main()
except SystemExit:
raise
except:
traceback.print_exc()
sys.exit(os.EX_TEMPFAIL)
#!/usr/bin/python
# This python module will send a mail message to a ERP5 site
# Taken from sendMailToZope.py in CMFMailin.
# $Id: sendMailToZope.py,v 1.1.1.1 2002/05/31 09:13:06 andyd Exp $
__version__='$Revision: 1.1.1.1 $'[11:-2]
import sys, urllib
import rfc822, StringIO, string
def sendMail(url, messageText):
if url:
if not url[-len('/postUTF8MailMessage'):] == '/postUTF8MailMessage':
url = url + '/postUTF8MailMessage'
try:
result = urllib.urlopen(url, urllib.urlencode({'file':messageText})).read()
except (IOError,EOFError),e:
print "ZMailIn Error: Problem Connecting to server",e
sys.exit(73)
# if the ZMailIn Client's method returned anything, then 'something bad' happened.
if result:
print result
sys.exit(1)
sys.exit(0)
print "ZMailIn Error: No ZMailIn Client URL found or specified."
sys.exit(1)
if __name__ == '__main__':
# This gets called by the MTA when a new message arrives.
# The mail message file gets passed in on the stdin
# First get a handle on the message file
f = sys.stdin
messageText = f.read()
url = ''
if len(sys.argv)>1:
url = sys.argv[1]
if not url:
print "ZMailIn Error: You must specify the URL" \
" to the ERP5 instance in the First arguement. " \
"i.e. python sendMailToERP5.py http://www.myserver.com/erp5/"
sys.exit(1)
sendMail(url, messageText)
#!/usr/bin/python
##############################################################################
#
# Copyright (c) 2007 Nexedi SA and Contributors. All Rights Reserved.
# Jean-Paul Smets <jp@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import sys
import urllib
# Configurable parameters
USER = ''
PASSWORD = ''
CONTRIBUTION_TOOL_URL = 'http://%s:%s@localhost:9080/erp5/portal_contributions/newContent' % (USER, PASSWORD)
PORTAL_TYPE = 'Mail Message'
FILE_NAME = 'postfix_mail.eml'
CONTAINER_PATH = 'event_module'
# Main program
if __name__ == '__main__':
f = sys.stdin
message_text = f.read()
try:
result = urllib.urlopen(CONTRIBUTION_TOOL_URL, urllib.urlencode(
{'data': message_text,
'portal_type': PORTAL_TYPE,
'container_path': CONTAINER_PATH,
'file_name': FILE_NAME,
}
)).read()
except (IOError,EOFError), e:
print "Zope Email Ingestion Error: Problem Connecting to server", e
sys.exit(73)
if result:
print result
sys.exit(1)
sys.exit(0)
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment