https://wiki.zimbra.com/api.php?action=feedcontributions&user=Jbala99&feedformat=atomZimbra :: Tech Center - User contributions [en]2024-03-29T13:34:11ZUser contributionsMediaWiki 1.39.0https://wiki.zimbra.com/index.php?title=ZCS_Mailman_Integration&diff=21070ZCS Mailman Integration2010-09-20T05:18:33Z<p>Jbala99: </p>
<hr />
<div>{{Unsupported}}<br />
<br />
HOWTO: Integrate Zimbra Collaboration Suite with Mailman mailing lists<br />
----------------------------------------------------------------------<br />
<br />
=1. Overview=<br />
<br />
Many administrators would like to have robust mailing list management in<br />
conjunction with Zimbra. ZCS has distribution lists which works well<br />
for small groups with a central management interface; however, it does<br />
not provide support for self-help subscription, moderation or digests.<br />
Using ZCS together with Mailman is one way to fulfill the requirements,<br />
but requires a bit of massaging to work as one would expect.<br />
<br />
One method of integration can be found on our forums at http://www.zimbra.com/forums/administrators/1380-solved-zimbra-mailman-howto.html<br />
<br />
The above guide will allow a single-node ZCS install to coexist<br />
with Mailman on a single server, however, your mileage may vary.<br />
The referenced setup requires manual building of Mailman, integration with<br />
Zimbra/Apache-HTTPD, Zimbra-Postfix config changes and the work required<br />
to make it work does not appear to be insignificant. The guide also<br />
places restrictions on the email-domain that can be served by Mailman<br />
lists on the server.<br />
<br />
In this guide, we will elaborate on a dual/multi-node ZCS/Mailman setup<br />
with step-by-step instructions as well provide patches to Mailman with<br />
a Python library to automatically interface with Zimbra. There will be<br />
no restriction on the domain which Mailman can serve. In addition to<br />
integrating Mailman, one can take the general ideas introduced by this<br />
document and apply it to any other list-serv of choice.<br />
<br />
<br />
=2. Getting started=<br />
<br />
Our supported configuration for using Mailman with Zimbra is to have<br />
Mailman on an external ''dedicated'' node. We do not want to have the<br />
Mailman setup interfering with the ZCS configuration.<br />
<br />
This guide assumes knowledge of working with ZCS and the ability to<br />
get the basics of Mailman configured (such as installation, Apache<br />
HTTPD configuration, and mailing list management). Additionally, for<br />
convenience, we will assume that the setup will be performed on RHEL.<br />
<br />
To continue, we will need:<br />
<br />
# An additional server where we will run Mailman<br />
# The name of the MTA for Zimbra (ZIMBRA_MTA_HOST)<br />
# The name of the host on which Mailman will run (MAILMAN_HOSTNAME)<br />
# The domain name for which we are receiving email (EMAIL_DOMAIN)<br />
# To create an admin password for Mailman (MAILMAN_ADMIN_PASSWORD)<br />
# To create a list creator password for Mailman (MAILMAN_CREATOR_PASSWORD)<br />
<br />
<br />
=3. Configuring Mailman=<br />
<br />
First, install Mailman and postfix; on RHEL 5, we do:<br />
<br />
$ rpm -e --nodeps sendmail<br />
$ yum install mailman postfix<br />
<br />
which pulls down all dependencies such as httpd if it was not already<br />
present. It also forcibly removes sendmail and replaces it with postfix.<br />
<br />
Edit /etc/mailman/mm_cfg.py set the variables:<br />
<br />
DEFAULT_URL_HOST = 'MAILMAN_HOSTNAME'<br />
DEFAULT_EMAIL_HOST = 'EMAIL_DOMAIN'<br />
<br />
Run<br />
<br />
$ /usr/lib/mailman/bin/update<br />
<br />
Create the site-wide mailing list (default is 'mailman') and any other<br />
desired mailing lists:<br />
<br />
$ /usr/lib/mailman/bin/newlist mailman<br />
<br />
specify any valid email address as the list owner (including any address<br />
that is hosted on ZCS).<br />
<br />
<br />
Edit and update /etc/aliases as directed by the output of the 'newlist'<br />
command. Run<br />
<br />
$ /usr/bin/newaliases<br />
<br />
to update the aliases table to include the mailing list addresses.<br />
<br />
<br />
Setup the Mailman administrative passwords:<br />
<br />
$ /usr/lib/mailman/bin/mmsitepass MAILMAN_ADMIN_PASSWORD<br />
$ /usr/lib/mailman/bin/mmsitepass -c MAILMAN_CREATOR_PASSWORD<br />
<br />
<br />
Configure postfix to accept email on EMAIL_DOMAIN and redirect unknown<br />
addresses to user@EMAIL_DOMAIN back into Zimbra (this will cover the<br />
cases where users on ZCS will also be included in the mailing lists)<br />
<br />
Edit /etc/postfix/main.cf and set:<br />
<br />
mydomain = EMAIL_DOMAIN<br />
myorigin = $mydomain<br />
mydestination = $mydomain<br />
local_recipient_maps =<br />
fallback_transport = smtp:ZIMBRA_MTA_HOST<br />
<br />
<br />
We are now ready to (re)start the mail, httpd and mailman services,<br />
do the following:<br />
<br />
$ chkconfig httpd on<br />
$ chkconfig postfix on<br />
$ chkconfig mailman on<br />
$ service httpd restart<br />
$ service postfix restart<br />
$ service mailman restart<br />
<br />
<br />
=4. Configuring ZCS=<br />
<br />
NOTE: This section can be bypassed if one follows the directions in<br />
'5. Further integration with Zimbra' below.<br />
<br />
<br />
Now that Mailman has been properly configured, we will need to setup<br />
the addresses that will forward from ZCS to the Mailman host.<br />
<br />
$ zmprov ca LIST_NAME@EMAIL_DOMAIN ANY_RANDOM_PASSWORD<br />
$ zmprov ma LIST_NAME@EMAIL_DOMAIN zimbraMailTransport smtp:MAILMAN_HOSTNAME:25<br />
<br />
$ zmprov ca LIST_NAME-admin@EMAIL_DOMAIN ANY_RANDOM_PASSWORD<br />
$ zmprov ma LIST_NAME-admin@EMAIL_DOMAIN zimbraMailTransport smtp:MAILMAN_HOSTNAME:25<br />
<br />
$ zmprov ca LIST_NAME-bounces@EMAIL_DOMAIN ANY_RANDOM_PASSWORD<br />
$ zmprov ma LIST_NAME-bounces@EMAIL_DOMAIN zimbraMailTransport smtp:MAILMAN_HOSTNAME:25<br />
<br />
Repeat the ca/ma commands above for the remaining -confirm, -join,<br />
-leave, -owner, -request, -subscribe, and -unsubscribe aliases.<br />
<br />
With the above configuration complete, we may now use fully-featured<br />
mailing lists at LIST_NAME@EMAIL_DOMAIN.<br />
<br />
=5. Further integration with Zimbra=<br />
<br />
We can offer tighter integration of Mailman with ZCS using the attached<br />
ZimbraIntegration.py (to be installed in MAILMAN_HOME/Mailman). A simple<br />
patch to the Mailman installation in conjunction with the aforementioned<br />
ZimbraIntegration.py will enable us to ignore section 4 above altogether.<br />
<br />
With the zimbra_mailman_integration.patch applied against Mailman every<br />
invocation of 'newlist', 'rmlist' and create/delete list from the web<br />
interface will automatically issue the appropriate zmprov commands to<br />
ZCS. Zimbra accounts with the zimbraMailTransport setting will be<br />
configured as necessary for the specified LIST_NAME.<br />
<br />
To apply the integration, copy ZimbraIntegration.py into<br />
MAILMAN_HOME/Mailman, on systems where we installed Mailman using the RPM,<br />
it will be /usr/lib/mailman. Apply the patch:<br />
<br />
$ cd /usr/lib/mailman; patch -p0 < zimbra_mailman_integration.patch<br />
<br />
Edit /etc/mailman/mm_cfg.py, add the variables:<br />
<br />
OWNERS_CAN_DELETE_THEIR_OWN_LISTS = True # enable deleting lists from web UI<br />
ZIMBRA_ADMIN_SOAP_SERVICE = 'https://ZCSHOST:7071/service/admin/soap/'<br />
MAILMAN_SMTP_TRANSPORT = 'MAILMAN_HOSTNAME'<br />
ZIMBRA_ADMIN_USERNAME = 'YOUR_ZCS_ADMIN_USERNAME'<br />
ZIMBRA_ADMIN_PASSWORD = 'YOUR_ZCS_ADMIN_PASSWORD'<br />
<br />
ZimbraIntegration.py exposes some APIs that can be re-used by non-Mailman<br />
list-servs and can serve as an example for users that might want to<br />
integrate ZCS with another list-serv.<br />
<br />
<br />
=6. Possible configurations (unsupported)=<br />
<br />
For small configurations, it is possible to run Mailman on the same host<br />
as ZCS, although strictly unsupported.<br />
<br />
This is possible because Zimbra installs its own postfix into<br />
/opt/zimbra/postfix, the system may retain its own postfix<br />
installation and use that configuration to communicate to Mailman<br />
on the same node. The system-version of postfix must listen<br />
on a different port (ALT_SMTP_PORT) from the Zimbra-version of<br />
postfix. The zimbraMailTransport directive above would become<br />
smtp:ZIMBRA_MTA_HOST:ALT_SMTP_PORT<br />
<br />
The remaining integration into Zimbra's Apache HTTPD configuration is<br />
left as an exercise to the reader.<br />
<br />
<br />
==6.1 Using a different mailing list manager (other than Mailman)==<br />
<br />
Following this guide presented thus-far, similar principles may be<br />
applied to any list-serv with respect to email routing and integration<br />
using ZimbraIntegration.py.<br />
<br />
=ZimbraIntegration.py=<br />
<pre><br />
import urllib2<br />
# try to import the ElementTree API included with py 2.5 first<br />
# otherwise fallback on third-party install (prereq)<br />
try:<br />
from xml.etree import ElementTree<br />
except:<br />
from elementtree import ElementTree<br />
<br />
# load mailman config<br />
try:<br />
from Mailman import mm_cfg<br />
except:<br />
print "WARNING: Cannot find mailman config"<br />
<br />
MAILMAN_LIST_ACCOUNTS = (<nowiki>''</nowiki>,<br />
'-admin', '-bounces', '-confirm', '-join', '-leave',<br />
'-owner', '-request', '-subscribe', '-unsubscribe')<br />
ZIMBRA_SOAP_HEADERS = {<br />
'Content-type': 'text/xml; charset=utf-8',<br />
}<br />
<br />
ZIMBRA_AUTH_REQUEST = \<br />
'<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">' \<br />
' <soap:Body>' \<br />
' <AuthRequest xmlns="urn:zimbraAdmin">' \<br />
' <name>%s</name>' \<br />
' <password>%s</password>' \<br />
' </AuthRequest>' \<br />
' </soap:Body>' \<br />
'</soap:Envelope>'<br />
<br />
ZIMBRA_REQUEST = \<br />
'<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">' \<br />
' <soap:Header>' \<br />
' <context xmlns="urn:zimbra">' \<br />
' <authToken>%s</authToken>' \<br />
' <sessionId id="%s" type="admin">%s</sessionId>' \<br />
' </context>' \<br />
' </soap:Header>' \<br />
' <soap:Body>%s</soap:Body>' \<br />
'</soap:Envelope>'<br />
<br />
ZIMBRA_CREATE_REQUEST = \<br />
'<CreateAccountRequest xmlns="urn:zimbraAdmin">' \<br />
' <name>%s</name>' \<br />
' <a n="zimbraMailTransport">smtp:%s</a>' \<br />
'</CreateAccountRequest>'<br />
ZIMBRA_GET_REQUEST = \<br />
'<GetAccountRequest xmlns="urn:zimbraAdmin">' \<br />
' <account by="name">%s</account>' \<br />
'</GetAccountRequest>'<br />
ZIMBRA_DELETE_REQUEST = \<br />
'<DeleteAccountRequest xmlns="urn:zimbraAdmin">' \<br />
' <id>%s</id>' \<br />
'</DeleteAccountRequest>'<br />
<br />
class ZimbraIntegration:<br />
def __init__(self):<br />
self.__authToken = None<br />
self.__sessionId = None<br />
try:<br />
self.__url = mm_cfg.ZIMBRA_ADMIN_SOAP_SERVICE<br />
self.__transport = mm_cfg.MAILMAN_SMTP_TRANSPORT<br />
self.__username = mm_cfg.ZIMBRA_ADMIN_USERNAME<br />
self.__password = mm_cfg.ZIMBRA_ADMIN_PASSWORD<br />
except: # testing configuration<br />
self.__url = "https://192.168.230.130:7071/service/admin/soap/"<br />
self.__transport = 'rhel5-testn.testdomain.com'<br />
self.__username = 'admin@testdomain.com'<br />
self.__password = 'password'<br />
<br />
def __envelope(self, request):<br />
if self.__authToken is None:<br />
self.__getAuthToken()<br />
<br />
envelope = ZIMBRA_REQUEST % (self.__authToken, self.__sessionId,<br />
self.__sessionId, request)<br />
return envelope<br />
<br />
def __sendRequest(self, request):<br />
req = urllib2.Request(self.__url, request, ZIMBRA_SOAP_HEADERS)<br />
try:<br />
resp = urllib2.urlopen(req)<br />
tree = ElementTree.parse(resp)<br />
except urllib2.HTTPError, e:<br />
tree = ElementTree.parse(e.fp)<br />
error = tree.findtext(<br />
"//{http://www.w3.org/2003/05/soap-envelope}Text")<br />
raise ZimbraIntegrationException(error)<br />
return tree<br />
<br />
def __getAuthToken(self):<br />
requestBody = ZIMBRA_AUTH_REQUEST % (self.__username, self.__password)<br />
tree = self.__sendRequest(requestBody)<br />
self.__authToken = tree.findtext("//{urn:zimbraAdmin}authToken")<br />
self.__sessionId = tree.findtext("//{urn:zimbraAdmin}sessionId")<br />
<br />
def __checkAccounts(self, name, domain):<br />
found = False<br />
for i in MAILMAN_LIST_ACCOUNTS:<br />
address = "%s%s@%s" % (name, i, domain)<br />
try:<br />
self.getAccount(address)<br />
found = True<br />
except:<br />
pass<br />
if found is True: # can't just immediately raise above<br />
raise ZimbraIntegrationException(<br />
"accounts already exist for: " + name)<br />
<br />
def createAccounts(self, name, domain):<br />
self.__checkAccounts(name, domain)<br />
<br />
try:<br />
for i in MAILMAN_LIST_ACCOUNTS:<br />
address = "%s%s@%s" % (name, i, domain)<br />
self.createAccount(address)<br />
except ZimbraIntegrationException, e:<br />
self.__rollbackCreation(name, domain)<br />
raise ZimbraIntegrationException(e.reason)<br />
<br />
def __rollbackCreation(self, name, domain):<br />
self.deleteAccounts(name, domain)<br />
<br />
def getAccount(self, name):<br />
requestBody = ZIMBRA_GET_REQUEST % name<br />
requestBody = self.__envelope(requestBody)<br />
tree = self.__sendRequest(requestBody)<br />
node = tree.find("//{urn:zimbraAdmin}account")<br />
return node.get("id")<br />
<br />
def deleteAccounts(self, name, domain):<br />
for i in MAILMAN_LIST_ACCOUNTS:<br />
try:<br />
address = "%s%s@%s" % (name, i, domain)<br />
id = self.getAccount(address)<br />
self.deleteAccount(id)<br />
except:<br />
pass<br />
<br />
def deleteAccount(self, id):<br />
requestBody = self.__envelope(ZIMBRA_DELETE_REQUEST % id)<br />
self.__sendRequest(requestBody)<br />
<br />
def createAccount(self, name):<br />
requestBody = ZIMBRA_CREATE_REQUEST % (name, self.__transport)<br />
requestBody = self.__envelope(requestBody)<br />
self.__sendRequest(requestBody)<br />
<br />
class ZimbraIntegrationException(Exception):<br />
def __init__(self, reason):<br />
self.reason = reason<br />
def __str__(self):<br />
return self.reason<br />
</pre><br />
=zimbra_mailman.patch=<br />
<pre><br />
diff -ur orig/Mailman/Cgi/create.py Mailman/Cgi/create.py<br />
--- orig/Mailman/Cgi/create.py 2007-01-06 00:54:44.000000000 -0800<br />
+++ Mailman/Cgi/create.py 2008-04-20 13:07:50.000000000 -0700<br />
@@ -31,6 +31,7 @@<br />
from Mailman import i18n<br />
from Mailman.htmlformat import *<br />
from Mailman.Logging.Syslog import syslog<br />
+from Mailman.ZimbraIntegration import ZimbraIntegrationException<br />
<br />
# Set up i18n<br />
_ = i18n._<br />
@@ -217,6 +218,10 @@<br />
_('''Some unknown error occurred while creating the list.<br />
Please contact the site administrator for assistance.'''))<br />
return<br />
+ except ZimbraIntegrationException, e:<br />
+ request_creation(doc, cgidata,<br />
+ "Unable to create the list, zimbra error: %s" % e)<br />
+ return<br />
<br />
# Initialize the host_name and web_page_url attributes, based on<br />
# virtual hosting settings and the request environment variables.<br />
diff -ur orig/Mailman/Cgi/rmlist.py Mailman/Cgi/rmlist.py<br />
--- orig/Mailman/Cgi/rmlist.py 2007-01-06 00:54:44.000000000 -0800<br />
+++ Mailman/Cgi/rmlist.py 2008-04-20 12:58:14.000000000 -0700<br />
@@ -29,6 +29,7 @@<br />
from Mailman import i18n<br />
from Mailman.htmlformat import *<br />
from Mailman.Logging.Syslog import syslog<br />
+from Mailman.ZimbraIntegration import ZimbraIntegration<br />
<br />
# Set up i18n<br />
_ = i18n._<br />
@@ -157,6 +158,9 @@<br />
'directory %s not deleted due to permission problems',<br />
dir)<br />
<br />
+ zi = ZimbraIntegration()<br />
+ zi.deleteAccounts(listname, mlist.host_name)<br />
+<br />
title = _('Mailing list deletion results')<br />
doc.SetTitle(title)<br />
table = Table(border=0, width='100%')<br />
diff -ur orig/Mailman/MailList.py Mailman/MailList.py<br />
--- orig/Mailman/MailList.py 2007-01-06 00:54:44.000000000 -0800<br />
+++ Mailman/MailList.py 2008-04-20 12:45:10.000000000 -0700<br />
@@ -46,6 +46,7 @@<br />
from Mailman import Errors<br />
from Mailman import LockFile<br />
from Mailman.UserDesc import UserDesc<br />
+from Mailman.ZimbraIntegration import ZimbraIntegration<br />
<br />
# base classes<br />
from Mailman.Archiver import Archiver<br />
@@ -486,6 +487,11 @@<br />
raise Errors.BadListNameError, postingaddr<br />
# Validate the admin's email address<br />
Utils.ValidateEmail(admin)<br />
+<br />
+ # Integrate with Zimbra<br />
+ zi = ZimbraIntegration()<br />
+ zi.createAccounts(name, emailhost)<br />
+<br />
self._internal_name = name<br />
self._full_path = Site.get_listpath(name, create=1)<br />
# Don't use Lock() since that tries to load the non-existant config.pck<br />
diff -ur orig/Mailman/mm_cfg.py Mailman/mm_cfg.py<br />
--- orig/Mailman/mm_cfg.py 2008-04-19 09:20:44.000000000 -0700<br />
+++ Mailman/mm_cfg.py 2008-04-20 13:04:05.000000000 -0700<br />
@@ -80,6 +80,7 @@<br />
<br />
DEFAULT_URL_HOST = 'YOUR_MAILMAN_WEB_HOSTNAME'<br />
DEFAULT_EMAIL_HOST = 'YOUR_EMAIL_DOMAINNAME'<br />
+OWNERS_CAN_DELETE_THEIR_OWN_LISTS = True # delete to disable delete from web<br />
<br />
# Because we've overriden the virtual hosts above add_virtualhost<br />
# MUST be called after they have been defined.<br />
@@ -94,3 +95,8 @@<br />
<br />
# Note - if you're looking for something that is imported from mm_cfg, but you<br />
# didn't find it above, it's probably in Defaults.py.<br />
+<br />
+ZIMBRA_ADMIN_SOAP_SERVICE = 'https://YOURZCSHOST:7071/service/admin/soap/'<br />
+MAILMAN_SMTP_TRANSPORT = 'YOUR_MAILMAN_SMTP_HOSTNAME'<br />
+ZIMBRA_ADMIN_USERNAME = 'YOUR_ZCS_ADMIN_USERNAME'<br />
+ZIMBRA_ADMIN_PASSWORD = 'YOUR_ZCS_ADMIN_PASSWORD'<br />
</pre><br />
<br />
I haven't tested other versions, but at least with Mailman 2.1.9 you need also to patch MAILMAN_DIR/bin/rmlist. For reasons unknown to me, MAILMAN_DIR/bin/newlist will create the Zimbra mailboxes but MAILMAN_DIR/bin/rmlist, unpatched, will not remove them.<br />
<pre><br />
diff -ur bin/rmlist.orig bin/rmlist<br />
--- bin/rmlist.orig 2010-09-19 22:12:11.000000000 -0700<br />
+++ bin/rmlist 2010-09-19 22:08:42.000000000 -0700<br />
@@ -46,6 +46,7 @@<br />
from Mailman import Utils<br />
from Mailman import MailList<br />
from Mailman.i18n import _<br />
+from Mailman.ZimbraIntegration import ZimbraIntegration<br />
<br />
try:<br />
True, False<br />
@@ -113,6 +114,8 @@<br />
REMOVABLES = []<br />
if Utils.list_exists(listname):<br />
mlist = MailList.MailList(listname, lock=0)<br />
+ zi = ZimbraIntegration()<br />
+ zi.deleteAccounts(listname, mlist.host_name)<br />
<br />
# Do the MTA-specific list deletion tasks<br />
if mm_cfg.MTA:<br />
</pre><br />
{{Article Footer|unknown|4/24/2008}}<br />
<br />
[[Category:Customizing ZCS]]</div>Jbala99