I'm migrating a mail server (discussed previously) from qmail and vpopmail and ezmlm to exim and userdb and mailman, and discovered that mailman can't handle two lists in different domains with the same name (ie, users@domain1 and users@domain2). But we already have this situation with ezmlm, so I needed to get this to work. Enclosed is my patch and my exim configuration.

Here is my patch to mailman to support what I consider to be true virtual hosting. Pretty-printed here for convenience:

--- MailList.py.orig    Thu Feb 12 16:35:54 2004
+++ MailList.py Thu Feb 12 18:40:22 2004
@@ -183,9 +183,14 @@
         return self._full_path

     def getListAddress(self, extra=None):
+        posting_addr = self.internal_name()
+        try:
+            posting_addr = self.posting_addr
+        except:
+            pass
         if extra is None:
-            return '%s@%s' % (self.internal_name(), self.host_name)
-        return '%s-%s@%s' % (self.internal_name(), extra, self.host_name)
+            return '%s@%s' % (posting_addr, self.host_name)
+        return '%s-%s@%s' % (posting_addr, extra, self.host_name)

     # For backwards compatibility
     def GetBouncesEmail(self):
@@ -451,11 +456,17 @@
             raise Errors.BadListNameError, postingaddr
         # Validate the admin's email address
         Utils.ValidateEmail(admin)
-        self._internal_name = name
-        self._full_path = Site.get_listpath(name, create=1)
+        newname = "%s-%s" % (emailhost, name)
+        if Utils.list_exists(newname):
+            raise Errors.MMListAlreadyExistsError, name
+        self._internal_name = newname
+        self._full_path = Site.get_listpath(newname, create=1)
         # Don't use Lock() since that tries to load the non-existant config.pck
         self.__lock.lock()
-        self.InitVars(name, admin, crypted_password)
+        self.InitVars(newname, admin, crypted_password)
+        self.list_address = postingaddr
+        self.real_name = name
+        self.subject_prefix = mm_cfg.DEFAULT_SUBJECT_PREFIX % self.__dict__
         self.CheckValues()
         if langs is None:
             self.available_languages = [self.preferred_language]
@@ -1243,7 +1254,7 @@
         to or cc addrs."""
         # BAW: fall back to Utils.ParseAddr if the first test fails.
         # this is the list's full address
-        listfullname = '%s@%s' % (self.internal_name(), self.host_name)
+        listfullname = self.getListAddress()
         recips = []
         # check all recipient addresses against the list's explicit addresses,
         # specifically To: Cc: and Resent-to:

Here's my exim configuration to handle this.

Routers:

mailman_router:
  driver = accept
  require_files = MAILMAN_HOME/lists/$local_part/config.pck
  local_part_suffix_optional
  local_part_suffix = -bounces : -bounces+* : \
                      -confirm+* : -join : -leave : \
                      -owner : -request : -admin
  transport = mailman_transport


mailman_rewriter:
  driver = redirect
  require_files = MAILMAN_HOME/lists/$domain-$local_part/config.pck
  local_part_suffix_optional
  local_part_suffix = -bounces : -bounces+* : \
                      -confirm+* : -join : -leave : \
                      -owner : -request : -admin
  data = $domain-$local_part$local_part_suffix@$domain

Transport:

mailman_transport:
  driver = pipe
  command = MAILMAN_WRAP \
    '${if def:local_part_suffix \
      {${sg{$local_part_suffix}{-(\\w+)(\\+.*)?}{\$1}}} \
      {post}}' \
    $local_part
  current_directory = MAILMAN_HOME
  home_directory = MAILMAN_HOME
  user = MAILMAN_USER
  group = MAILMAN_GROUP