From 59aadbb537da3961579e8cc8ec7e497886438253 Mon Sep 17 00:00:00 2001
From: Patrick Cernko <pcernko@mpi-klsb.mpg.de>
Date: Wed, 17 Jan 2024 10:17:58 +0100
Subject: [PATCH] reading member config exported from `mailman-subscribers3.py
 --json` generating member.dump from member config added moderated resp.
 whitelisted members to corresponding list if list is "open" resp. moderated,
 thus writing out modlist.txt set creation_email to first owner in XML
 normalize all emails for LDAP users to their primary mail address to allow
 mapping membership/ownership for logged in users

---
 gen_config.py | 67 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 64 insertions(+), 3 deletions(-)

diff --git a/gen_config.py b/gen_config.py
index 145cff1..293a130 100755
--- a/gen_config.py
+++ b/gen_config.py
@@ -5,16 +5,31 @@ import sys
 
 import argparse
 import xml.etree.ElementTree as ET
+import json
+import mpildap
 
+def normalize_email(email):
+    lp, domain = email.split('@')
+    ldap_mail = mpildap.ldaps(f'&(istEmailName={lp})(istMailDomainReceive={domain})', 'mail', unique=True)
+    if ldap_mail:
+        return ldap_mail[0]
+    return email
 
 parser = argparse.ArgumentParser(description='import mailman list to sympa')
 parser.add_argument('input')
+parser.add_argument('membersin')
 parser.add_argument('output')
 parser.add_argument('info')
 parser.add_argument('blocklist')
 parser.add_argument('whitelist')
+parser.add_argument('modlist')
+parser.add_argument('membersout')
 args = parser.parse_args()
 
+members = {}
+with open(args.membersin) as fd:
+    for m, d in json.load(fd).items():
+        members[normalize_email(m)] = d
 
 old_vars = {}
 with open(args.input) as fd:
@@ -94,11 +109,13 @@ if old_vars['anonymous_list']:
     ET.SubElement(xml, 'anonymous_sender').text = old_vars['new_listname'] + '@' + old_vars['robot']
 
 # send
+moderated = False
 if old_vars['generic_nonmember_action'] == 0: # accept
     ET.SubElement(xml, 'send').text = 'public'
 elif old_vars['generic_nonmember_action'] >= 1: # hold, reject, discard
     if old_vars['default_member_moderation']:
         ET.SubElement(xml, 'send').text = 'editorkey'
+        moderated = True
     else:
         ET.SubElement(xml, 'send').text = 'privateoreditorkey'
 
@@ -150,14 +167,14 @@ else:
 # digest_footer not used
 
 
-# XXX: does this work?
+ET.SubElement(xml, "creation_email").text = normalize_email(old_vars['owner'][0])
 for owner in old_vars['owner']:
     o = ET.SubElement(xml, "owner", attrib={'multiple':'1'})
-    ET.SubElement(o, "email").text = owner
+    ET.SubElement(o, "email").text = normalize_email(owner)
 
 for mod in old_vars['moderator']:
     o = ET.SubElement(xml, "editor", attrib={'multiple':'1'})
-    ET.SubElement(o, "email").text = mod
+    ET.SubElement(o, "email").text = normalize_email(mod)
 
 
 tree = ET.ElementTree(xml)
@@ -167,6 +184,9 @@ except Exception as e:
     print(f'Failed to write XML for sympa: {e}', file=sys.stderr)
     exit(1)
 
+with open(args.info, mode='w') as fd:
+    fd.write(old_vars['info'])
+
 blocklist = []
 for black in sorted(set(old_vars['ban_list'] + old_vars['hold_these_nonmembers'] + old_vars['reject_these_nonmembers'] + old_vars['discard_these_nonmembers'])):
     for char in '*^$':
@@ -186,11 +206,52 @@ for white in sorted(set(old_vars['accept_these_nonmembers'])):
         if char in white:
             print(f"whitelist entry '{white}' contains regexp char '{char}', this won't work!", file=sys.stderr)
     whitelist.append(white)
+if moderated:
+    for m, d in members.items():
+        if d['_mod'] == 'off':
+            print(f'Whitelisting member "{m}" for list with moderated policy', file=sys.stderr)
+            whitelist.append(m)
 if whitelist:
     # ensure non-empty file ends with newline
     whitelist.append('')
 with open(args.whitelist, mode='w') as fd:
     fd.write('\n'.join(whitelist))
 
+modlist = []
+if not moderated:
+    for m, d in members.items():
+        if d['_mod'] == 'on':
+            print(f'Mod-listing member "{m}" for list with open policy', file=sys.stderr)
+            modlist.append(m)
+if modlist:
+    # ensure non-empty file ends with newline
+    modlist.append('')
+with open(args.modlist, mode='w') as fd:
+    fd.write('\n'.join(modlist))
+
 with open(args.info, mode='w') as fd:
     fd.write(old_vars['info'])
+
+with open(args.membersout, mode='w') as fd:
+    for m, d in members.items():
+        email = m
+        gecos = d['_realname']
+        reception = 'mail'
+        if d['_nomail'] != "off":
+            reception = 'nomail'
+        elif d['_digest'] == 'on':
+            if d['_plain'] == 'on':
+                reception = 'digestplain'
+            else:
+                reception = 'digest'
+        elif d['_notmetoo'] == 'on':
+            reception = 'not_me'
+        visibility = 'noconceal'
+        if d['_hide'] == 'on':
+            visibility = 'conceal'
+        print(f"""email {email}
+gecos {gecos}
+reception {reception}
+subscribed 1
+visibility {visibility}
+""", file=fd)
-- 
GitLab