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 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')
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)) # ----------------------------------------------------------- # loop through the umbrella lists defined in the .ini file # create the list if it does not exist # update the lists with the information from the .ini file for list in cp_lists.sections(): listname = helpers.cleanStr(cp_lists[list]['name'])
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()
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))
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']) else: ml.discard_request(request['token'])
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, }