diff --git a/gen_config.py b/gen_config.py
index e5babf2d30e17d6aa3a3541be62fc73daf312ebc..92755090f60df957e7907b7734bd9961f0dbb3e2 100755
--- a/gen_config.py
+++ b/gen_config.py
@@ -1,16 +1,21 @@
-#!/usr/bin/env python2
+#!/usr/bin/python3
 # -*- coding: utf-8 -*-
 
+import sys
+
 import argparse
 import xml.etree.ElementTree as ET
 
 parser = argparse.ArgumentParser(description='import mailman list to sympa')
 parser.add_argument('input')
 parser.add_argument('output')
+parser.add_argument('blacklist')
+parser.add_argument('whitelist')
 args = parser.parse_args()
 
 old_vars = {}
-execfile(args.input, old_vars)
+with open(args.input) as fd:
+    exec(fd.read(), old_vars)
 
 xml = ET.Element("list")
 
@@ -20,67 +25,94 @@ ET.SubElement(xml, "type").text = 'private_working_group'
 ET.SubElement(xml, "topic").text = 'other'
 # subject is required in sympa
 if old_vars['description']:
-	ET.SubElement(xml, "subject").text = old_vars['description']
+    ET.SubElement(xml, "subject").text = old_vars['description']
 else:
-	ET.SubElement(xml, "subject").text = old_vars['real_name']
+    ET.SubElement(xml, "subject").text = old_vars['real_name']
 ET.SubElement(xml, "custom_subject").text = old_vars['subject_prefix'].strip().replace('[', '').replace(']', '')
 if old_vars['advertised']:
-	ET.SubElement(xml, "visibility").text = 'noconceal'
+    ET.SubElement(xml, "visibility").text = 'noconceal'
 else:
-	ET.SubElement(xml, "visibility").text = 'conceal'
+    ET.SubElement(xml, "visibility").text = 'conceal'
 
 if old_vars['subscribe_policy'] == 1:
-	ET.SubElement(xml, "subscribe").text = 'auth'
+    ET.SubElement(xml, "subscribe").text = 'auth'
 else:
-	ET.SubElement(xml, "subscribe").text = 'owner'
+    ET.SubElement(xml, "subscribe").text = 'owner'
 
 if old_vars['archive']:
-	if old_vars['archive_private']:
-		ET.SubElement(xml, "archive").text = 'private'
-	else:
-		ET.SubElement(xml, "archive").text = 'public'
-
-if old_vars['private_roster']:
-	ET.SubElement(xml, 'user_visibility').text = 'conceal'
+    ET.SubElement(xml, "process_archive").text = 'on'
+    a = ET.SubElement(xml, "archive")
+    if old_vars['archive_private']:
+        ET.SubElement(a, "web_access").text  = 'private'
+        ET.SubElement(a, "mail_access").text = 'private'
+    else:
+        ET.SubElement(a, "web_access").text  = 'public'
+        ET.SubElement(a, "mail_access").text = 'public'
 else:
-	ET.SubElement(xml, 'user_visibility').text = 'noconceal'
+    ET.SubElement(xml, "process_archive").text = 'off'
 
+r = ET.SubElement(xml, "reply_to_header")
 if old_vars['reply_goes_to_list'] == 0:
-	ET.SubElement(xml, 'reply_to').text = 'sender'
+    ET.SubElement(r, 'value').text = 'sender'
 elif old_vars['reply_goes_to_list'] == 1:
-	ET.SubElement(xml, 'reply_to').text = 'list'
+    ET.SubElement(r, 'value').text = 'list'
 elif old_vars['reply_goes_to_list'] == 2:
-	if old_vars['reply_to_address'] == old_vars['new_listname'] + '@' + old_vars['robot']:
-		ET.SubElement(xml, 'reply_to').text = 'list'
-	else:
-		ET.SubElement(xml, 'reply_to').text = 'other_email'
-		ET.SubElement(xml, 'reply_to_address').text = old_vars['reply_to_address']
+    if old_vars['reply_to_address'] == old_vars['new_listname'] + '@' + old_vars['robot']:
+        ET.SubElement(r, 'value').text = 'list'
+    else:
+        ET.SubElement(xml, 'value').text = 'other_email'
+        ET.SubElement(xml, 'other_email').text = old_vars['reply_to_address']
 
 if old_vars['first_strip_reply_to']:
-	ET.SubElement(xml, 'strip_reply_to').text = 'forced'
+    ET.SubElement(r, 'apply').text = 'forced'
 else:
-	ET.SubElement(xml, 'strip_reply_to').text = 'respect'
+    ET.SubElement(r, 'apply').text = 'respect'
 
 # send
 if old_vars['generic_nonmember_action'] == 0: # accept
-	ET.SubElement(xml, 'send').text = 'public'
+    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'
-	else:
-		ET.SubElement(xml, 'send').text = 'privateoreditorkey'
+    if old_vars['default_member_moderation']:
+        ET.SubElement(xml, 'send').text = 'editorkey'
+    else:
+        ET.SubElement(xml, 'send').text = 'privateoreditorkey'
 
 # privateoreditorkey
 # public
 # private
 
 for owner in old_vars['owner']:
-	o = ET.SubElement(xml, "owner", attrib={'multiple':'1'})
-	ET.SubElement(o, "email").text = owner
+    o = ET.SubElement(xml, "owner", attrib={'multiple':'1'})
+    ET.SubElement(o, "email").text = owner
 
 for mod in old_vars['moderator']:
-	o = ET.SubElement(xml, "editor", attrib={'multiple':'1'})
-	ET.SubElement(o, "email").text = mod
+    o = ET.SubElement(xml, "editor", attrib={'multiple':'1'})
+    ET.SubElement(o, "email").text = mod
 
 tree = ET.ElementTree(xml)
 tree.write(args.output, encoding='utf-8', xml_declaration=True)
+
+blacklist = []
+for black in sorted(set(old_vars['ban_list'] + old_vars['discard_these_nonmembers'])):
+    for char in '*^$':
+        if char in black:
+            print(f"blacklist entry '{black}' contains regexp char '{char}', this won't work!", file=sys.stderr)
+    blacklist.append(black)
+if blacklist:
+    # ensure non-empty file ends with newline
+    blacklist.append('')
+with open(args.blacklist, mode='w') as fd:
+    fd.write('\n'.join(blacklist))
+
+whitelist = []
+for white in sorted(set(old_vars['accept_these_nonmembers'])):
+    for char in '*^$':
+        if char in white:
+            print(f"whitelist entry '{white}' contains regexp char '{char}', this won't work!", file=sys.stderr)
+    whitelist.append(white)
+if whitelist:
+    # ensure non-empty file ends with newline
+    whitelist.append('')
+with open(args.whitelist, mode='w') as fd:
+    fd.write('\n'.join(whitelist))
+