def __init__(self, **args): self.client = Client(args['rest_url'], args['rest_username'], args['rest_password']) self.domain = self.client.get_domain(args['domain']) self.logger = logging.getLogger(__name__) self.logger.setLevel(logging.DEBUG)
def subscribe(list_address, user): client = Client('%s/3.0' % settings.MAILMAN_REST_SERVER, settings.MAILMAN_API_USER, settings.MAILMAN_API_PASS) rest_list = client.get_list(list_address) try: member = rest_list.get_member(user.email) except ValueError: # not subscribed yet, subscribe the user without email delivery member = rest_list.subscribe( user.email, "%s %s" % (user.first_name, user.last_name)) member.preferences["delivery_status"] = "by_user" member.preferences.save()
def subscribe(list_address, user): client = Client('%s/3.0' % settings.MAILMAN_REST_SERVER, settings.MAILMAN_API_USER, settings.MAILMAN_API_PASS) rest_list = client.get_list(list_address) try: member = rest_list.get_member(user.email) except ValueError: # not subscribed yet, subscribe the user without email delivery member = rest_list.subscribe(user.email, "%s %s" % (user.first_name, user.last_name)) member.preferences["delivery_status"] = "by_user" member.preferences.save()
def __init__(self): # Create settings & check connection try: self.client = Client(settings.MAILMAN_URL, settings.MAILMAN_USER, settings.MAILMAN_PASS) logger.debug('Connected to mailman %(mailman_version)s' % self.client.system) except: logger.error('Connection to mailman failed on %s' % settings.MAILMAN_URL) return None self.connected = True
class TestDomains(unittest.TestCase): def setUp(self): self._client = Client('http://localhost:9001/3.0', 'restadmin', 'restpass') def test_no_domain(self): # Trying to get a non-existent domain returns a 404. # # We can't use `with self.assertRaises()` until we drop Python 2.6 try: self._client.get_domain('example.org') except HTTPError as error: self.assertEqual(error.code, 404) else: raise AssertionError('Expected HTTPError 404')
def process_view(self, request, view_func, view_args, view_kwargs): if not request.user.is_authenticated(): return if not request.user.email: return # Can this really happen? if self.session_key in request.session: return # Already set client = MailmanClient( "%s/3.0" % settings.MAILMAN_REST_SERVER, settings.MAILMAN_API_USER, settings.MAILMAN_API_PASS ) try: user = client.get_user(request.user.email) except MailmanConnectionError: return request.session[self.session_key] = [s.address for s in user.subscriptions]
class TestDomains(unittest.TestCase): def setUp(self): self._client = Client( 'http://localhost:9001/3.0', 'restadmin', 'restpass') def test_no_domain(self): # Trying to get a non-existent domain returns a 404. # # We can't use `with self.assertRaises()` until we drop Python 2.6 try: self._client.get_domain('example.org') except HTTPError as error: self.assertEqual(error.code, 404) else: raise AssertionError('Expected HTTPError 404')
class TestUnicode(unittest.TestCase): def setUp(self): self._client = Client( 'http://*****:*****@example.com' self.unicode_string = u'Jérôme' def tearDown(self): try: self._client.get_user(self.email).delete() except HTTPError as error: if error.status_code == 404: pass def test_create_user(self): user = self._client.create_user( email=self.email, password='******', display_name=self.unicode_string) self.assertEqual(user.display_name, self.unicode_string)
def process_view(self, request, view_func, view_args, view_kwargs): if not request.user.is_authenticated(): return if not request.user.email: return # Can this really happen? if "subscribed" in request.session and "user_id" in request.session: return # Already set client = MailmanClient('%s/3.0' % settings.MAILMAN_REST_SERVER, settings.MAILMAN_API_USER, settings.MAILMAN_API_PASS) try: user = client.get_user(request.user.email) except MailmanConnectionError: return except HTTPError, err: if err.code == 404: user = client.create_user(request.user.email, "") else: return
def get_mailman_client(): """Return a mailman client instance to be used by the model. .. note:: Printing the instance will print the API crendential """ return Client( '{0}/{1}'.format(current_app.config['MAILMAN_REST_API_URL'], current_app.config['MAILMAN_REST_API_VERSION']), current_app.config['MAILMAN_REST_API_USER'], current_app.config['MAILMAN_REST_API_PASS'])
def list_properties(request): """Get JSON encoded list properties""" store = get_store(request) lists = store.get_lists() client = Client('%s/3.0' % settings.MAILMAN_REST_SERVER, settings.MAILMAN_API_USER, settings.MAILMAN_API_PASS) props = {} for ml in lists: try: mm_list = client.get_list(ml.name) except urllib2.HTTPError: continue props[ml.name] = { "display_name": mm_list.display_name, "description": mm_list.settings["description"], } # Update KittyStore if necessary if ml.display_name != mm_list.display_name: ml.display_name = mm_list.display_name return HttpResponse(json.dumps(props), mimetype='application/javascript')
def valid_config(self): # Reads the config file indicated by configpath argument config = configparser.ConfigParser() try: config.read(self.configpath) except Exception as exc: printerr(_T('Exception reading {}').format(self.configpath)) printerr(exc) return (False) if 'webservice' not in config: printerr( _T('No webservice section found: {}').format(self.configpath)) printerr(_T('Are you sure this is the correct path?')) return (False) # Get parameters for Mailman3 REST API URL: https = config['webservice'].getboolean('use_https') host = config['webservice']['hostname'] port = config['webservice'].getint('port') user = config['webservice']['admin_user'] pw = config['webservice']['admin_pass'] s = 'http' if https: s = 'https' # Now build the URL: url = '{schema}://{host}:{port}'.format(schema=s, host=host, port=port) # And test it using the credentials: try: client = Client('http://localhost:8001/3.1', user, pw) except Exception as exc: printerr(_T('Exception accesing REST API URL = {}').format(url)) printerr(exc) return (False) if 'api_version' in client.system.keys(): self.mmclient = client return (True) return (False)
def connect(self, *args, **kwargs): """ Connect to Mailman REST API using the arguments specified. Missing arguments are decided from the mailman.cfg file return a client object. """ host, port, username, password = self.get_credentials_from_config() if 'host' in kwargs and kwargs['host']: host = kwargs['host'] if 'port' in kwargs and kwargs['port']: port = kwargs['port'] if 'username' in kwargs and kwargs['username']: username = kwargs['username'] if 'password' in kwargs and kwargs['password']: password = kwargs['password'] client = Client('%s:%s/3.0' % (host, port), username, password) try: client.system except MailmanConnectionError as e: self.error(e) exit(1) return client
def get_client(): return Client('{0}/3.0'.format(settings.MAILMAN_API_URL), settings.MAILMAN_USER, settings.MAILMAN_PASS)
class MailMan(object): ''' Use official mailman 3.0 api client ''' client = None connected = False lists = [] def __init__(self): # Create settings & check connection try: self.client = Client(settings.MAILMAN_URL, settings.MAILMAN_USER, settings.MAILMAN_PASS) logger.debug('Connected to mailman %(mailman_version)s' % self.client.system) except: logger.error('Connection to mailman failed on %s' % settings.MAILMAN_URL) return None self.connected = True def get_list(self, list_name): ''' Retrieve a list using only its name ''' if not self.connected: raise Exception('No mailman connection') list_name += '@%s' % settings.MAILMAN_DOMAIN ml = self.client.get_list(list_name) if not ml: raise Exception('Mailing list %s not found' % list_name) return ml def subscribe(self, list_name, email, full_name): ''' Subscribe a member to a mailing list With full approval directly ''' if not self.connected: raise Exception('No mailman connection') ml = self.get_list(list_name) return ml.subscribe(email, full_name, pre_verified=True, pre_confirmed=True, pre_approved=True) def unsubscribe(self, list_name, email): ''' Unsubscribe a member from a mailing list ''' if not self.connected: raise Exception('No mailman connection') ml = self.get_list(list_name) return ml.unsubscribe(email) def create_list(self, list_name, full_name, extra_settings=None): ''' Create a new mailing list properly configured ''' # Retrieve domain domain = self.client.get_domain(settings.MAILMAN_DOMAIN) if not domain: raise Exception('No mailman domain %s' % settings.MAILMAN_DOMAIN) # Get or create list on domain try: ml = domain.create_list(list_name) except: ml = self.get_list(list_name) # Configure mailing mls = ml.settings mls['default_member_action'] = 'accept' mls['default_nonmember_action'] = 'accept' mls['send_welcome_message'] = False mls['advertised'] = False mls['display_name'] = full_name mls['subject_prefix'] = '[%s] ' % full_name mls['reply_to_address'] = ml.fqdn_listname # Override if extra_settings: # No update on mls for k, v in extra_settings.items(): mls[k] = v mls.save() return ml def delete_list(self, list_name): ''' Delete a mailing list ''' if not self.connected: raise Exception('No mailman connection') ml = self.get_list(list_name) return ml.delete()
CORE_URI = os.environ.get('MAILMAN_CORE_URI', 'http://mailman-core:8001/3.1') CORE_USER = os.environ.get('MAILMAN_REST_USER', 'restadmin') CORE_PASS = os.environ.get('MAILMAN_REST_PASSWORD', 'restpass') parser.add_argument('list_fqdn') parser.add_argument('--members-file', dest='list', default=None) parser.add_argument('--core-uri', dest='core_uri', default=CORE_URI) parser.add_argument('--rest-user', dest='core_user', default=CORE_USER) parser.add_argument('--rest-password', dest='core_password', default=CORE_PASS) args = parser.parse_args() client = Client(args.core_uri, args.core_user, args.core_password) ml_fqdn = args.list_fqdn ml = client.get_list(ml_fqdn) current_members = [str(m.address) for m in ml.members] new_member_list = [] new_member_names = {} if args.list: if args.list == '-': list_file = sys.stdin else: list_file = open(args.list, 'r') else: list_file = sys.stdin
def get_all_mailman_lists(): result = check_output(['/usr/sbin/list_lists', '-b']) mailman2_lists = str(result,"ascii").split('\n') c = Client(MAILMAN3_API_URL, MAILMAN3_API_USER, MAILMAN3_API_PASSWORD) mailman3_lists = [ls.list_name for ls in c.get_lists()] return list(set(mailman2_lists) | set(mailman3_lists))
def prepare_list(): # pre-check before handling mailman core service templates_path = os.path.join(os.getcwd(), 'app-mailman/mail', TEMPLATE_FOLDER_PATH) if not os.path.exists(templates_path): print( "The template file folder 'TEMPLATE_FOLDER_PATH' must exits on local." ) sys.exit(1) if not MAILMAN_CORE_PASSWORD: print("MAILMAN_CORE_PASSWORD required to login.") sys.exit(1) client = Client(MAILMAN_CORE_ENDPOINT, MAILMAN_CORE_USER, MAILMAN_CORE_PASSWORD) domains = client.domains for domain in domains: common_path = os.path.join(templates_path, domain.mail_host, 'common') common_templates = list( filter(lambda x: x.endswith('.txt'), os.listdir(common_path))) if common_templates: for txt_file in common_templates: template_name = txt_file.rsplit('.txt')[0].replace('-', ':') uri = MAILMAN_CORE_ENDPOINT + os.path.abspath(txt_file) try: domain.set_template(template_name, uri) print('set common template' 'domain:{} \r\n' 'template name:{} \r\n' 'uri:{}'.format(template_name, os.path.abspath(txt_file), uri)) except Exception as e: print(e) sys.exit(1) existing_lists = domain.lists list_dirs = os.listdir(os.path.join(templates_path, domain.mail_host)) list_dirs.remove('common') for list_dir in list_dirs: if list_dir not in existing_lists: domain.create_list(list_dir) print('create list \r\n' 'domain: {} \r\n' 'list: {}'.format(domain.mail_host, list_dir)) for maillist in domain.lists: try: list_text_dirs = os.listdir( os.path.join(templates_path, domain.mail_host, maillist.list_name)) except FileNotFoundError: continue list_text_dirs = list( filter(lambda x: x.endswith('.txt'), list_text_dirs)) for file in list_text_dirs: template_name = file.rsplit('.txt')[0].replace('-', ':') uri = MAILMAN_CORE_ENDPOINT + os.path.abspath(file) try: maillist.set_template(template_name, uri) print('set list template \r\n' 'list: {} \r\n' 'template name: {} \r\n ' 'uri: {}'.format(maillist, os.path.abspath(file), uri)) except Exception as e: print(e) sys.exit(1) templates = maillist.templates for template in templates: if (template.name.replace(':', '-') + '.txt') not in list_text_dirs: maillist.set_template(template.name, '') print('remove list template \r\n' 'list: {} \r\n' 'template name: {}'.format(maillist.list_name, template.name))
def get_mailman_client(): # easier to patch during unit tests client = Client('%s/3.1' % settings.MAILMAN_REST_API_URL, settings.MAILMAN_REST_API_USER, settings.MAILMAN_REST_API_PASS) return client
# read the list configuration cp_lists = configparser.ConfigParser() cp_lists.clear() cp_lists.read(cp_config['cp_listconfig']['fileName']) log.debug('read configuration from file: ' + cp_config['cp_listconfig']['fileName']) if not cp_lists.sections(): log.warning('no lists to process') raise SystemExit() else: log.info('umbrella lists to process: ' + str(cp_lists.sections())) # ----------------------------------------------------------- # connect to the mailman list server client = Client(cp_config['mm_settings']['url'], cp_config['mm_settings']['user'], cp_config['mm_settings']['pass']) try: log.debug(client.system) except (MailmanConnectionError): log.critical(MailmanConnectionError) raise SystemExit(0) mm_domain = client.get_domain(cp_config['mm_settings']['domain']) mm_lists = mm_domain.get_lists() log.info('domain: ' + str(mm_domain)) log.debug('all lists in domain: ' + str(mm_lists)) log.debug('connected to : ' + str(client))
# parser.add_argument('--owners', action='store_true', default=False) # parser.add_argument('--nonmembers', action='store_true', default=False) # parser.add_argument('--moderators', action='store_true', default=False) parser.add_argument('--list-fqdn', dest='list_fqdn', action='append', default=None) parser.add_argument('--core-uri', dest='core_uri', default=CORE_URI) parser.add_argument('--rest-user', dest='core_user', default=CORE_USER) parser.add_argument('--rest-password', dest='core_password', default=CORE_PASS) args = parser.parse_args() # client = Client(CORE_URI, CORE_USER, CORE_PASS) client = Client(args.core_uri, args.core_user, args.core_password) results = {} for ml in client.get_lists(): list_name = ml.settings['list_name'] list_fqdn_name = ml.settings['fqdn_listname'] if args.list_fqdn: if not (list_fqdn_name in args.list_fqdn): # if list name doesn't match required # skip along continue try: messages_held = len(ml.held) except: messages_held = -1 try:
def setUp(self): self._client = Client( 'http://*****:*****@example.com' self.unicode_string = u'Jérôme'
#!/usr/bin/env python3 import os import sys import getopt import getpass import urllib.request os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") from django.core.wsgi import get_wsgi_application application = get_wsgi_application() from django.contrib.auth.models import User from mailmanclient import Client client = Client('http://localhost:8001/3.1', 'restadmin', 'bRaPCVVUJCbl+uZNuEnxASvni3LhQgFSPqDUtEmj1K5tmIm6') def main(argv): try: opts, args = getopt.getopt(argv, "e:") except getopt.GetoptError as err: print(err) print(sys.argv[0], '-e <email>') sys.exit(2) for opt, arg in opts: if opt == '-e': mail = arg try: user = User.objects.get(email=mail) #print(user.username)
class maillist(object): def __init__(self, **args): self.client = Client(args['rest_url'], args['rest_username'], args['rest_password']) self.domain = self.client.get_domain(args['domain']) self.logger = logging.getLogger(__name__) self.logger.setLevel(logging.DEBUG) def get_lists(self): mailists = [] for list in self.domain.lists: mailists.append(list.fqdn_listname) return mailists def create_list(self, **args): list = args['list'] name = list.split('@')[0] self.logger.debug(list) self.logger.debug(name) self.domain.create_list(name) self.set_settings(list) def set_settings(self, list_fqdn): list = self.client.get_list(list_fqdn) for key, value in self.default_settings().items(): list.settings[key] = value list.settings.save() def get_list_members(self, list_fqdn): members = [] list = self.client.get_list(list_fqdn) for member in list.members: members.append(member.address.email.lower()) return members def sync_members(self, list_fqdn, wannabe_members): members = self.get_list_members(list_fqdn) list = self.client.get_list(list_fqdn) todo_add = set(wannabe_members) - set(members) if len(todo_add) > 0: self.logger.info('Will add users to {}'.format(list_fqdn)) self.logger.info(str(todo_add)) todo_remove = set(members) - set(wannabe_members) if len(todo_remove) > 0: self.logger.info('Will remove users from {}'.format(list_fqdn)) self.logger.info(str(todo_remove)) for user in todo_remove: list.unsubscribe(user, pre_approved=True) for user in todo_add: list.subscribe(user, pre_verified=True, pre_confirmed=True, pre_approved=True) return True def default_settings(self): return { "acceptable_aliases": [], "accept_these_nonmembers": [], "admin_immed_notify": True, "admin_notify_mchanges": False, "administrivia": False, "advertised": False, "allow_list_posts": True, "anonymous_list": False, "archive_policy": "private", "archive_rendering_mode": "text", "autorespond_owner": "none", "autorespond_postings": "none", "autorespond_requests": "none", "autoresponse_grace_period": "90d", "autoresponse_owner_text": "", "autoresponse_postings_text": "", "autoresponse_request_text": "", "bounce_info_stale_after": "7d", "bounce_notify_owner_on_disable": True, "bounce_notify_owner_on_removal": True, "bounce_score_threshold": 5, "bounce_you_are_disabled_warnings": 3, "bounce_you_are_disabled_warnings_interval": "7d", "collapse_alternatives": True, "convert_html_to_plaintext": False, "default_member_action": "defer", "default_nonmember_action": "hold", "description": "Wannabe list", "digest_send_periodic": False, "digest_size_threshold": 30.0, "digest_volume_frequency": "monthly", "digests_enabled": False, "discard_these_nonmembers": [], "dmarc_mitigate_action": "munge_from", "dmarc_mitigate_unconditionally": True, "dmarc_moderation_notice": "", "dmarc_wrapped_message_text": "", "emergency": False, "filter_action": "discard", "filter_content": False, "filter_extensions": [], "filter_types": [], "first_strip_reply_to": False, "forward_unrecognized_bounces_to": "administrators", "gateway_to_mail": False, "gateway_to_news": False, "hold_these_nonmembers": [], "include_rfc2369_headers": True, "info": "", "linked_newsgroup": "", "max_message_size": 128, "max_num_recipients": 12, "max_days_to_hold": 0, "member_roster_visibility": "moderators", "moderator_password": None, "newsgroup_moderation": "none", "nntp_prefix_subject_too": True, "pass_types": [], "pass_extensions": [], "personalize": "none", "posting_pipeline": "default-posting-pipeline", "preferred_language": "no", "process_bounces": True, "reject_these_nonmembers": [], "reply_goes_to_list": "no_munging", "reply_to_address": "", "require_explicit_destination": False, "respond_to_post_requests": True, "send_welcome_message": False, "subscription_policy": "moderate", "unsubscription_policy": "moderate", "usenet_watermark": None, }
parser.add_argument('--list-fqdn', dest='list_fqdn', required=True) parser.add_argument('--members', dest='members', action='append', default=None) parser.add_argument('--members-file', dest='list', default=None) parser.add_argument('--core-uri', dest='core_uri', default=CORE_URI) parser.add_argument('--rest-user', dest='core_user', default=CORE_USER) parser.add_argument('--rest-password', dest='core_password', default=CORE_PASS) args = parser.parse_args() # client = Client(CORE_URI, CORE_USER, CORE_PASS) client = Client(args.core_uri, args.core_user, args.core_password) ml_fqdn = args.list_fqdn member_list = args.members member_file = None if args.list: if args.list == '-': member_file = sys.stdin else: member_file = open(args.list, 'r') for m in member_file: member_list.append(m.strip()) if args.list and args.list != '-': member_file.close()
def setUp(self): self._client = Client('http://localhost:9001/3.0', 'restadmin', 'restpass')
def prepare_list(): # pre-check before handling mailman core service if DEFAULT_DOMAIN_NAME == "": print("Must specify 'DEFAULT_DOMAIN_NAME' for mail list preparation.") exit(1) lists = str.split(str(DEFAULT_MAIL_LISTS).lower(), ",") if not os.path.exists(TEMPLATE_FOLDER_PATH): print("The template file folder 'TEMPLATE_FOLDER_PATH' must exits on" " local.") exit(1) if len(lists) == 0: # find out all of the lists from local folder. local_file = [] for _, _, f in os.walk(os.path.join(os.getcwd(), TEMPLATE_FOLDER_PATH)): for file in f: if file.endswith(".txt") and not file.endswith("base.txt"): local_file.append(os.path.splitext(file)[0]) lists = list(set(local_file)) client = Client(MAILMAN_CORE_ENDPOINT, MAILMAN_CORE_USER, MAILMAN_CORE_PASSWORD) try: # Create default domain if not exists default_domain = client.get_domain(DEFAULT_DOMAIN_NAME) except HTTPError as err: if err.code == 404: default_domain = client.create_domain(DEFAULT_DOMAIN_NAME) else: print("unable to find domain {0}".format(err)) exit(1) # Create default mail lists existing_lists = [el.list_name for el in client.lists] for l in lists: if l in existing_lists: print("skip creating list {0}, since it's already exist".format(l)) continue else: print("starting to create mail list {0}".format(l)) default_domain.create_list(l) # Patch template for lists for l in lists: # browse all of the dirs and find out the template files existing_folders = [ f for f in os.listdir( os.path.join(os.getcwd(), TEMPLATE_FOLDER_PATH)) ] for d in existing_folders: if not os.path.isdir( os.path.join(os.getcwd(), TEMPLATE_FOLDER_PATH, d)): continue # check the list file exists local_file = get_template_file(d, l) if os.path.exists(local_file): patch_content = { convert_name_to_substitution(d): get_templates_url(d, l) } elif os.path.exists(get_base_template_file(d)): patch_content = { convert_name_to_substitution(d): get_templates_url(d, "base") } else: continue patch_uri = "{0}/lists/{1}.{2}/uris".format( MAILMAN_CORE_ENDPOINT, l, DEFAULT_DOMAIN_NAME) response = requests.patch(patch_uri, patch_content, auth=(MAILMAN_CORE_USER, MAILMAN_CORE_PASSWORD)) print("patching list {0} with template file {1}, result {2} {3}" "".format(l, local_file, response.status_code, response.text))
for row in spamreader: if row: ml = row[0].split('@')[0].strip() mailing = lists.setdefault(ml, {}) to_add = mailing.setdefault('add', set()) to_del = mailing.setdefault('delete', set()) action = row[1].strip() if action == 'add': members = to_add else: members = to_del mail = row[2].strip() if '@' in mail: members.add(mail) client = Client('http://localhost:8001/3.1', api_user, api_pass) try: dom = client.get_domain(domain) except (HTTPError, ) as exc: if exc.code != 404: raise dom = client.create_domain(domain) def accept_request(ml, member): done = None for request in ml.requests: if request['email'] != member: continue if member not in [m.email for m in ml.members]: ml.accept_request(request['token'])