示例#1
0
def matches_p(sender, nonmembers, listname):
    # First strip out all the regular expressions and listnames
    plainaddrs = [addr for addr in nonmembers if not (addr.startswith('^')
                                                 or addr.startswith('@'))]
    addrdict = Utils.List2Dict(plainaddrs, foldcase=1)
    if addrdict.has_key(sender):
        return 1
    # Now do the regular expression matches
    for are in nonmembers:
        if are.startswith('^'):
            try:
                cre = re.compile(are, re.IGNORECASE)
            except re.error:
                continue
            if cre.search(sender):
                return 1
        elif are.startswith('@'):
            # XXX Needs to be reviewed for list@domain names.
            try:
                mname = are[1:].lower().strip()
                if mname == listname:
                    # don't reference your own list
                    syslog('error',
                        '*_these_nonmembers in %s references own list',
                        listname)
                else:
                    mother = MailList(mname, lock=0)
                    if mother.isMember(sender):
                        return 1
            except Errors.MMUnknownListError:
                syslog('error',
                  '*_these_nonmembers in %s references non-existent list %s',
                  listname, mname)
    return 0
示例#2
0
 def _create(self, team_name):
     """Create a new mailing list."""
     # Create the mailing list and set the defaults.
     mlist = MailList()
     try:
         # Use a fake list admin password; Mailman will never be
         # administered from its web u/i.  Nor will the mailing list
         # require an owner that's different from the site owner.  Also by
         # default, only English is supported.
         try:
             mlist.Create(team_name, mm_cfg.SITE_LIST_OWNER, " no password ")
             # Additional hard coded list defaults.
             # - Personalize regular delivery so that we can VERP these.
             # - Turn off RFC 2369 headers; we'll do them differently
             # - enable $-string substitutions in headers/footers
             mlist.personalize = 1
             mlist.include_rfc2369_headers = False
             mlist.use_dollar_strings = True
             mlist.held_message_ids = {}
             mlist.Save()
             # Now create the archive directory for MHonArc.
             path = os.path.join(mm_cfg.VAR_PREFIX, "mhonarc", team_name)
             os.makedirs(path)
         # We have to use a bare except here because of the legacy string
         # exceptions that Mailman can raise.
         except:
             log_exception("List creation error for team: %s", team_name)
             return False
         else:
             return True
     finally:
         mlist.Unlock()
示例#3
0
def process(res, args):
    mlist = res.mlist
    if args:
        res.results.append(_('Usage:'))
        res.results.append(gethelp(mlist))
        return STOP
    hostname = mlist.host_name
    res.results.append(_('Public mailing lists at %(hostname)s:'))
    lists = Utils.list_names()
    lists.sort()
    i = 1
    for listname in lists:
        if listname == mlist.internal_name():
            xlist = mlist
        else:
            xlist = MailList(listname, lock=0)
        # We can mention this list if you already know about it
        if not xlist.advertised and xlist is not mlist:
            continue
        # Skip the list if it isn't in the same virtual domain.  BAW: should a
        # message to the site list include everything regardless of domain?
        if mm_cfg.VIRTUAL_HOST_OVERVIEW and \
               xlist.host_name != mlist.host_name:
            continue
        realname = xlist.real_name
        description = xlist.description or _('n/a')
        requestaddr = xlist.GetRequestEmail()
        if i > 1:
            res.results.append('')
        res.results.append(_('%(i)3d. List name:   %(realname)s'))
        res.results.append(_('     Description: %(description)s'))
        res.results.append(_('     Requests to: %(requestaddr)s'))
        i += 1
示例#4
0
def do_include(mlist, msg, msgdata, recips):
    # regular_include_lists are the other mailing lists on this mailman
    # installation whose members are included in the regular (non-digest)
    # delivery if those list addresses don't appear in To: or Cc: headers.
    if not mlist.regular_include_lists:
        return recips
    recips = set(recips)
    destinations = email.Utils.getaddresses(msg.get_all('to', []) +
                                            msg.get_all('cc', []))
    destinations = [y.lower() for x,y in destinations]
    for listname in mlist.regular_include_lists:
        listname = listname.lower()
        if listname in destinations:
            continue
        listlhs, hostname = listname.split('@')
        if listlhs == mlist.internal_name():
            syslog('error', 'Include list %s is a self reference.',
                    listname)
            continue
        try:
            slist = MailList(listlhs, lock=False)
        except MMUnknownListError:
            syslog('error', 'Include list %s not found.', listname)
            continue
        if not mm_cfg.ALLOW_CROSS_DOMAIN_SIBLING \
           and slist.host_name != hostname:
            syslog('error', 'Include list %s is not in the same domain.',
                    listname)
            continue
        srecips = set([slist.getMemberCPAddress(m)
                   for m in slist.getRegularMemberKeys()
                   if slist.getDeliveryStatus(m) == ENABLED])
        recips |= srecips
    return list(recips)
示例#5
0
 def _create(self, team_name):
     """Create a new mailing list."""
     # Create the mailing list and set the defaults.
     mlist = MailList()
     try:
         # Use a fake list admin password; Mailman will never be
         # administered from its web u/i.  Nor will the mailing list
         # require an owner that's different from the site owner.  Also by
         # default, only English is supported.
         try:
             mlist.Create(team_name, mm_cfg.SITE_LIST_OWNER,
                          ' no password ')
             # Additional hard coded list defaults.
             # - Personalize regular delivery so that we can VERP these.
             # - Turn off RFC 2369 headers; we'll do them differently
             # - enable $-string substitutions in headers/footers
             mlist.personalize = 1
             mlist.include_rfc2369_headers = False
             mlist.use_dollar_strings = True
             mlist.held_message_ids = {}
             mlist.Save()
             # Now create the archive directory for MHonArc.
             path = os.path.join(mm_cfg.VAR_PREFIX, 'mhonarc', team_name)
             os.makedirs(path)
         # We have to use a bare except here because of the legacy string
         # exceptions that Mailman can raise.
         except:
             log_exception('List creation error for team: %s', team_name)
             return False
         else:
             return True
     finally:
         mlist.Unlock()
示例#6
0
 def _apply_list_defaults(self, team_name, list_defaults):
     """Apply mailing list defaults and tie the new list into the MTA."""
     mlist = MailList(team_name)
     try:
         for key, value in list_defaults.items():
             setattr(mlist, key, value)
         # Do MTA specific creation steps.
         if mm_cfg.MTA:
             modname = 'Mailman.MTA.' + mm_cfg.MTA
             __import__(modname)
             sys.modules[modname].create(mlist, quiet=True)
         mlist.Save()
     finally:
         mlist.Unlock()
 def getListInfo(self):
     """Return a list of 4-tuples of Mailman mailing list info."""
     list_info = []
     for list_name in sorted(list_names()):
         if list_name == mm_cfg.MAILMAN_SITE_LIST:
             continue
         mailing_list = MailList(list_name, lock=False)
         list_address = mailing_list.getListAddress()
         if self.naked_email_address_set.getByEmail(list_address) is None:
             email = '%s not found' % list_address
         else:
             email = list_address
         list_info.append(
             (mailing_list.internal_name(), mailing_list.host_name,
              mailing_list.web_page_url, email))
     return list_info
 def getListInfo(self):
     """Return a list of 4-tuples of Mailman mailing list info."""
     list_info = []
     for list_name in sorted(list_names()):
         if list_name == mm_cfg.MAILMAN_SITE_LIST:
             continue
         mailing_list = MailList(list_name, lock=False)
         list_address = mailing_list.getListAddress()
         if self.naked_email_address_set.getByEmail(list_address) is None:
             email = '%s not found' % list_address
         else:
             email = list_address
         list_info.append(
             (mailing_list.internal_name(), mailing_list.host_name,
              mailing_list.web_page_url, email))
     return list_info
示例#9
0
def main():
    for x in list_names():
        ml = MailList(x, True)
        try:
            changed = False
            for o in ml.owner:
                if not o in ALLOWED_OWNERS:
                    print 'Removing %s from %s' % (o, x)
                    ml.owner.remove(o)
                    changed = True
            if not ml.owner:
                ml.owner.append(ALLOWED_OWNERS[0])
                changed = True
            if changed:
                ml.Save()
        finally:
            ml.Unlock()
def main():
    for x in list_names():
        ml = MailList(x, True)
        try:
            changed = False
            if ml.host_name != settings.LISTS_MAILDOMAIN:
                print 'Updating host_name of %s' % x
                ml.host_name = settings.LISTS_MAILDOMAIN
                changed = True
            if ml.from_is_list != 1:
                print 'Updating from_is_list of %s' % x
                ml.from_is_list = 1
                changed = True
            if changed:
                print 'Saving %s' % x
                ml.Save()
        finally:
            ml.Unlock()
示例#11
0
def check_geinteresseerden():
    print "GEINTERESSEERDEN"
    es = frozenset(
        map(lambda m: m.email.lower(),
            OldKnGroup.objects.get(name=MEMBER_GROUP).user_set.all()))
    ml = MailList('geinteresseerden', False)
    for m in ml.members:
        if m.lower() in es:
            print "%s in geinteresseerden" % m
示例#12
0
    def _resynchronize(self, actions, statuses):
        """Process resynchronization actions.

        actions is a sequence of 2-tuples specifying what needs to be
        resynchronized.  The tuple is of the form (listname, current-status).

        statuses is a dictionary mapping team names to one of the strings
        'success' or 'failure'.
        """
        syslog('xmlrpc', 'resynchronizing: %s',
               COMMASPACE.join(sorted(name for (name, status) in actions)))
        for name, status in actions:
            # There's no way to really know whether the original action
            # succeeded or not, however, it's unlikely that an action would
            # fail leaving the mailing list in a usable state.  Therefore, if
            # the list is loadable and lockable, we'll say it succeeded.
            try:
                mlist = MailList(name)
            except Errors.MMUnknownListError:
                # The list doesn't exist on the Mailman side, so if its status
                # is CONSTRUCTING, we can create it now.
                if status == 'constructing':
                    if self._create(name):
                        statuses[name] = ('resynchronize', 'success')
                    else:
                        statuses[name] = ('resynchronize', 'failure')
                else:
                    # Any other condition leading to an unknown list is a
                    # failure state.
                    statuses[name] = ('resynchronize', 'failure')
            except:
                # Any other exception is also a failure.
                statuses[name] = ('resynchronize', 'failure')
                log_exception('Mailing list does not load: %s', name)
            else:
                # The list loaded just fine, so it successfully
                # resynchronized.  Be sure to unlock it!
                mlist.Unlock()
                statuses[name] = ('resynchronize', 'success')
示例#13
0
    def _modify(self, actions, statuses):
        """Process mailing list modification actions.

        actions is a sequence of (team_name, modifications) tuples where the
        team_name is the name of the mailing list to create and modifications
        is a dictionary of values to set on the mailing list.

        statuses is a dictionary mapping team names to one of the strings
        'success' or 'failure'.
        """
        for team_name, modifications in actions:
            # First, validate the modification keywords.
            list_settings = {}
            for key in attrmap:
                if key in modifications:
                    list_settings[attrmap[key]] = modifications[key]
                    del modifications[key]
            if modifications:
                statuses[team_name] = ('modify', 'failure')
                syslog('xmlrpc', 'Unexpected modify settings: %s',
                       COMMASPACE.join(modifications))
                continue
            try:
                mlist = MailList(team_name)
                try:
                    for key, value in list_settings.items():
                        setattr(mlist, key, value)
                    mlist.Save()
                finally:
                    mlist.Unlock()
            # We have to use a bare except here because of the legacy string
            # exceptions that Mailman can raise.
            except:
                log_exception('List modification error for team: %s',
                              team_name)
                statuses[team_name] = ('modify', 'failure')
            else:
                statuses[team_name] = ('modify', 'success')
示例#14
0
def main():
    url = 'https://%s/mailman/' % settings.MAILDOMAIN
    for x in list_names():
        ml = MailList(x, True)
        try:
            changed = False
            if ml.host_name != settings.LISTS_MAILDOMAIN:
                print 'Updating host_name of %s (was %s)' % (x, ml.host_name)
                ml.host_name = settings.LISTS_MAILDOMAIN
                changed = True
            if ml.from_is_list != 1:
                print 'Updating from_is_list of %s' % x
                ml.from_is_list = 1
                changed = True
            if ml.web_page_url != url:
                print 'Updating url_host of %s (was %s)' % (x, ml.web_page_url)
                ml.web_page_url = url
                changed = True
            # if changed:
            #    print 'Saving %s' % x
            #    ml.Save()
        finally:
            ml.Unlock()
示例#15
0
def matches_p(sender, nonmembers, listname):
    # First strip out all the regular expressions and listnames
    plainaddrs = [
        addr for addr in nonmembers
        if not (addr.startswith('^') or addr.startswith('@'))
    ]
    addrdict = Utils.List2Dict(plainaddrs, foldcase=1)
    if addrdict.has_key(sender):
        return 1
    # Now do the regular expression matches
    for are in nonmembers:
        if are.startswith('^'):
            try:
                cre = re.compile(are, re.IGNORECASE)
            except re.error:
                continue
            if cre.search(sender):
                return 1
        elif are.startswith('@'):
            # XXX Needs to be reviewed for list@domain names.
            try:
                mname = are[1:].lower().strip()
                if mname == listname:
                    # don't reference your own list
                    syslog('error',
                           '*_these_nonmembers in %s references own list',
                           listname)
                else:
                    mother = MailList(mname, lock=0)
                    if mother.isMember(sender):
                        return 1
            except Errors.MMUnknownListError:
                syslog(
                    'error',
                    '*_these_nonmembers in %s references non-existent list %s',
                    listname, mname)
    return 0
示例#16
0
 def dump(self, listnames, password_scheme):
     print >> self._fp, '<?xml version="1.0" encoding="UTF-8"?>'
     self._push_element(
         'mailman', **{
             'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
             'xsi:noNamespaceSchemaLocation': 'ssi-1.0.xsd',
         })
     for listname in sorted(listnames):
         try:
             mlist = MailList(listname, lock=False)
         except Errors.MMUnknownListError:
             print >> sys.stderr, _('No such list: %(listname)s')
             continue
         self._dump_list(mlist, password_scheme)
     self._pop_element('mailman')
示例#17
0
    def _deactivate(self, actions, statuses):
        """Process mailing list deactivation actions.

        actions is a sequence of team names for the mailing lists to
        deactivate.

        statuses is a dictionary mapping team names to one of the strings
        'success' or 'failure'.
        """
        for team_name in actions:
            try:
                mlist = MailList(team_name, lock=False)
                if mm_cfg.MTA:
                    modname = 'Mailman.MTA.' + mm_cfg.MTA
                    __import__(modname)
                    sys.modules[modname].remove(mlist, quiet=True)
                # The archives are always persistent, so all we need to do to
                # deactivate a list is to delete the 'lists/team_name'
                # directory.  However, in order to support easy reactivation,
                # and to provide a backup in case of error, we create a gzip'd
                # tarball of the list directory.
                lists_dir = os.path.join(mm_cfg.VAR_PREFIX, 'lists')
                # To make reactivation easier, we temporarily cd to the
                # $var/lists directory and make the tarball from there.
                old_cwd = os.getcwd()
                # XXX BarryWarsaw 2007-08-02: Should we watch out for
                # collisions on the tar file name?  This can only happen if
                # the team is resurrected but the old archived tarball backup
                # wasn't removed.
                tgz_file_name = os.path.join(mm_cfg.VAR_PREFIX, 'backups',
                                             team_name + '.tgz')
                tgz_file = tarfile.open(tgz_file_name, 'w:gz')
                try:
                    os.chdir(lists_dir)
                    # .add() works recursively by default.
                    tgz_file.add(team_name)
                    # Now delete the list's directory.
                    shutil.rmtree(team_name)
                finally:
                    tgz_file.close()
                    os.chdir(old_cwd)
            # We have to use a bare except here because of the legacy string
            # exceptions that Mailman can raise.
            except:
                log_exception('List deletion error for team: %s', team_name)
                statuses[team_name] = ('deactivate', 'failure')
            else:
                statuses[team_name] = ('deactivate', 'success')
示例#18
0
 def wrapper(context, request):
     params = {}
     try:
         body = request.json_body
     except:
         raise HTTPBadRequest('Not JSON', body_template='${detail}')
     params['mail'] = body.get('mail')
     # TODO: check if mail is a valid mail
     if params['mail'] is None:
         raise HTTPBadRequest('Missing e-mail', body_template='${detail}')
     try:
         params['liste'] = MailList(request.matchdict.get('liste'), lock=1)
     except Errors.MMUnknownListError:
         raise HTTPNotFound('Unknown list')
     
     return func(request, params)
示例#19
0
def main():
    for x in list_names():
        ml = MailList(x, True)
        try:
            changed = False
            if ml.host_name != settings.LISTS_MAILDOMAIN:
                print 'Updating host_name of %s' % x
                ml.host_name = settings.LISTS_MAILDOMAIN
                changed = True
            if ml.from_is_list != 1:
                print 'Updating from_is_list of %s' % x
                ml.from_is_list = 1
                changed = True
            if changed:
                print 'Saving %s' % x
                ml.Save()
        finally:
            ml.Unlock()
示例#20
0
    def SendHostileSubscriptionNotice(self, listname, address):
        # Some one was invited to one list but tried to confirm to a different
        # list.  We inform both list owners of the bogosity, but be careful
        # not to reveal too much information.
        selfname = self.internal_name()
        syslog('mischief', '%s was invited to %s but confirmed to %s', address,
               listname, selfname)
        # First send a notice to the attacked list
        msg = Message.OwnerNotification(
            self, _('Hostile subscription attempt detected'),
            Utils.wrap(
                _("""%(address)s was invited to a different mailing
list, but in a deliberate malicious attempt they tried to confirm the
invitation to your list.  We just thought you'd like to know.  No further
action by you is required.""")))
        msg.send(self)
        # Now send a notice to the invitee list
        try:
            # Avoid import loops
            from Mailman.MailList import MailList
            mlist = MailList(listname, lock=False)
        except Errors.MMListError:
            # Oh well
            return
        otrans = i18n.get_translation()
        i18n.set_language(mlist.preferred_language)
        try:
            msg = Message.OwnerNotification(
                mlist, _('Hostile subscription attempt detected'),
                Utils.wrap(
                    _("""You invited %(address)s to your list, but in a
deliberate malicious attempt, they tried to confirm the invitation to a
different list.  We just thought you'd like to know.  No further action by you
is required.""")))
            msg.send(mlist)
        finally:
            i18n.set_translation(otrans)
示例#21
0
def main():
    url = 'https://%s/mailman/' % settings.MAILDOMAIN
    for x in list_names():
        ml = MailList(x, True)
        try:
            changed = False
            if ml.host_name != settings.LISTS_MAILDOMAIN:
                print('Updating host_name of %s (was %s)' % (x, ml.host_name))
                ml.host_name = settings.LISTS_MAILDOMAIN
                changed = True
            if ml.from_is_list != 1:
                print('Updating from_is_list of %s' % x)
                ml.from_is_list = 1
                changed = True
            if ml.web_page_url != url:
                print('Updating url_host of %s (was %s)' %
                      (x, ml.web_page_url))
                ml.web_page_url = url
                changed = True
            if changed:
                print('Saving %s' % x)
            #    ml.Save()
        finally:
            ml.Unlock()
示例#22
0
    def _update_list_subscriptions(self, list_name, subscription_info):
        """Update the subscription information for a single mailing list.

        :param list_name: The name of the mailing list to update.
        :type list_name: string
        :param subscription_info: The mailing list's new subscription
            information.
        :type subscription_info: a list of 4-tuples containing the address,
            real name, flags, and status of each person in the list's
            subscribers.
        """
        ## syslog('xmlrpc', '%s subinfo: %s', list_name, subscription_info)
        # Start with an unlocked list.
        mlist = MailList(list_name, lock=False)
        # Create a mapping of email address to the member's real name,
        # flags, and status.  Note that flags is currently unused.
        member_map = dict(
            (address, (realname, flags, status)) for address, realname, flags, status in subscription_info
        )
        # In Mailman parlance, the 'member key' is the lower-cased
        # address.  We need a mapping from the member key to
        # case-preserved address for all future members of the list.
        key_to_case_preserved = dict((address.lower(), address) for address in member_map)
        # The following modifications to membership may be made:
        # - Current members whose address is changing case
        # - New members who need to be added to the mailing list
        # - Old members who need to be removed from the mailing list
        # - Current members whose settings are being changed
        #
        # Start by getting the case-folded membership sets of all current
        # and future members.
        current_members = set(mlist.getMembers())
        future_members = set(key_to_case_preserved)
        # Additions are all those addresses in future_members who are not
        # in current_members.
        additions = future_members - current_members
        # Deletions are all those addresses in current_members who are not
        # in future_members.
        deletions = current_members - future_members
        # Any address in both current and future members is either
        # changing case, updating settings, or both.
        updates = current_members & future_members
        # If there's nothing to do, just skip this list.
        if not additions and not deletions and not updates:
            # Why did we get here?  This list should not have shown up in
            # getMembershipInformation().
            syslog("xmlrpc", "Strange subscription information for list: %s", list_name)
            return
        # Lock the list and make the modifications.  Don't worry if the list
        # couldn't be locked, we'll just log that and try again the next time
        # through the loop.  Eventually any existing lock will expire anyway.
        try:
            mlist.Lock(2)
        except TimeOutError:
            syslog("xmlrpc", "Could not lock the list to update subscriptions: %s", list_name)
            return
        try:
            # Handle additions first.
            if len(additions) > 0:
                syslog("xmlrpc", "Adding to %s: %s", list_name, additions)
            for address in additions:
                # When adding the new member, be sure to use the
                # case-preserved email address.
                original_address = key_to_case_preserved[address]
                realname, flags, status = member_map[original_address]
                mlist.addNewMember(original_address, realname=realname)
                mlist.setDeliveryStatus(original_address, status)
            # Handle deletions next.
            if len(deletions) > 0:
                syslog("xmlrpc", "Removing from %s: %s", list_name, deletions)
            for address in deletions:
                mlist.removeMember(address)
            # Updates can be either a settings update, a change in the
            # case of the subscribed address, or both.
            found_updates = []
            for address in updates:
                # See if the case is changing.
                current_address = mlist.getMemberCPAddress(address)
                future_address = key_to_case_preserved[address]
                if current_address != future_address:
                    mlist.changeMemberAddress(address, future_address)
                    found_updates.append("%s -> %s" % (address, future_address))
                # flags are ignored for now.
                realname, flags, status = member_map[future_address]
                if realname != mlist.getMemberName(address):
                    mlist.setMemberName(address, realname)
                    found_updates.append("%s new name: %s" % (address, realname))
                if status != mlist.getDeliveryStatus(address):
                    mlist.setDeliveryStatus(address, status)
                    found_updates.append("%s new status: %s" % (address, status))
            if len(found_updates) > 0:
                syslog("xmlrpc", "Membership updates for %s: %s", list_name, found_updates)
            # We're done, so flush the changes for this mailing list.
            mlist.Save()
        finally:
            mlist.Unlock()
示例#23
0
 def registerBounce(self, member, msg, weight=1.0, day=None, sibling=False):
     if not self.isMember(member):
         # check regular_include_lists, only one level
         if not self.regular_include_lists or sibling:
             return
         from Mailman.MailList import MailList
         for listaddr in self.regular_include_lists:
             listname, hostname = listaddr.split('@')
             listname = listname.lower()
             if listname == self.internal_name():
                 syslog('error', 'Bouncer: %s: Include list self reference',
                        listname)
                 continue
             try:
                 siblist = None
                 try:
                     siblist = MailList(listname)
                 except MMUnknownListError:
                     syslog('error',
                            'Bouncer: %s: Include list "%s" not found.',
                            self.real_name, listname)
                     continue
                 siblist.registerBounce(member,
                                        msg,
                                        weight,
                                        day,
                                        sibling=True)
                 siblist.Save()
             finally:
                 if siblist and siblist.Locked():
                     siblist.Unlock()
         return
     info = self.getBounceInfo(member)
     first_today = True
     if day is None:
         # Use today's date
         day = time.localtime()[:3]
     if not isinstance(info, _BounceInfo):
         # This is the first bounce we've seen from this member
         info = _BounceInfo(member, weight, day,
                            self.bounce_you_are_disabled_warnings)
         # setBounceInfo is now called below after check phase.
         syslog('bounce', '%s: %s bounce score: %s', self.internal_name(),
                member, info.score)
         # Continue to the check phase below
     elif self.getDeliveryStatus(member) <> MemberAdaptor.ENABLED:
         # The user is already disabled, so we can just ignore subsequent
         # bounces.  These are likely due to residual messages that were
         # sent before disabling the member, but took a while to bounce.
         syslog('bounce', '%s: %s residual bounce received',
                self.internal_name(), member)
         return
     elif info.date == day:
         # We've already scored any bounces for this day, so ignore it.
         first_today = False
         syslog('bounce', '%s: %s already scored a bounce for date %s',
                self.internal_name(), member,
                time.strftime('%d-%b-%Y', day + (0, 0, 0, 0, 1, 0)))
         # Continue to check phase below
     else:
         # See if this member's bounce information is stale.
         now = Utils.midnight(day)
         lastbounce = Utils.midnight(info.date)
         if lastbounce + self.bounce_info_stale_after < now:
             # Information is stale, so simply reset it
             info.reset(weight, day, self.bounce_you_are_disabled_warnings)
             syslog('bounce', '%s: %s has stale bounce info, resetting',
                    self.internal_name(), member)
         else:
             # Nope, the information isn't stale, so add to the bounce
             # score and take any necessary action.
             info.score += weight
             info.date = day
             syslog('bounce', '%s: %s current bounce score: %s',
                    self.internal_name(), member, info.score)
         # Continue to the check phase below
     #
     # Now that we've adjusted the bounce score for this bounce, let's
     # check to see if the disable-by-bounce threshold has been reached.
     if info.score >= self.bounce_score_threshold:
         if mm_cfg.VERP_PROBES:
             syslog('bounce',
                    'sending %s list probe to: %s (score %s >= %s)',
                    self.internal_name(), member, info.score,
                    self.bounce_score_threshold)
             self.sendProbe(member, msg)
             info.reset(0, info.date, info.noticesleft)
         else:
             self.disableBouncingMember(member, info, msg)
     elif self.bounce_notify_owner_on_bounce_increment and first_today:
         self.__sendAdminBounceNotice(member,
                                      msg,
                                      did=_('bounce score incremented'))
     # We've set/changed bounce info above.  We now need to tell the
     # MemberAdaptor to set/update it.  We do it here in case the
     # MemberAdaptor stores bounce info externally to the list object to
     # be sure updated information is stored, but we have to be sure the
     # member wasn't removed.
     if self.isMember(member):
         self.setBounceInfo(member, info)
示例#24
0
        elif opt in ('-n', '--dry-run'):
            dry_run_p = True
            verbose_p = True

    if len(args) < 1:
        pdie(1, C_('listname is required'))

    listname = args[0].lower().strip()
    num = int(args[1])
    if num < 1:
        pdie(1, 'Number (>0) required')

    mlist = None
    try:
        try:
            mlist = MailList(listname, lock=True)
        except Errors.MMListError, e:
            pdie(2, C_('No such list "%(listname)s"\n%(e)s'))

        index = os.path.join(mlist.archive_dir(), 'pipermail.pck')
        if not os.path.isfile(index):
            if verbose_p:
                print("No archives", file=sys.stderr)
            return
        with open(index, 'r') as f:
            d = pickle.load(f)

        if not (len(d['archives']) > num):
            if verbose_p:
                print("No expirations", file=sys.stderr)
            return
示例#25
0
def _addvirtual(mlist, fp):
    listname = mlist.internal_name()
    fieldsz = len(listname) + len('-unsubscribe')
    hostname = mlist.host_name
    # Set up the mailman-loop address
    loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
    loopdest = Utils.ParseEmail(loopaddr)[0]
    # And the site list posting address.
    siteaddr = Utils.get_site_email(mlist.host_name)
    sitedest = Utils.ParseEmail(siteaddr)[0]
    # And the site list -owner address.
    siteowneraddr = Utils.get_site_email(mlist.host_name, extra='owner')
    siteownerdest = Utils.ParseEmail(siteowneraddr)[0]
    if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
        loopdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        sitedest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        siteownerdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
    # If the site list's host_name is a virtual domain, adding the list and
    # owner addresses to the SITE ADDRESSES will duplicate the entries in the
    # stanza for the list.  Postfix doesn't like dups so we try to comment them
    # here, but only for the actual site list domain.
    if (MailList(mm_cfg.MAILMAN_SITE_LIST,
                 lock=False).host_name.lower() == hostname.lower()):
        siteaddr = '#' + siteaddr
        siteowneraddr = '#' + siteowneraddr
    # Seek to the end of the text file, but if it's empty write the standard
    # disclaimer, and the loop catch address and site address.
    fp.seek(0, 2)
    if not fp.tell():
        print >> fp, """\
# This file is generated by Mailman, and is kept in sync with the binary hash
# file virtual-mailman.db.  YOU SHOULD NOT MANUALLY EDIT THIS FILE unless you
# know what you're doing, and can keep the two files properly in sync.  If you
# screw it up, you're on your own.
#
# Note that you should already have this virtual domain set up properly in
# your Postfix installation.  See README.POSTFIX for details.

# LOOP ADDRESSES START
%s\t%s
# LOOP ADDRESSES END

# We also add the site list address in each virtual domain as that address
# is exposed on admin and listinfo overviews, and we add the site list-owner
# address as it is exposed in the list created email notice.

# SITE ADDRESSES START
%s\t%s
%s\t%s
# SITE ADDRESSES END
""" % (loopaddr, loopdest, siteaddr, sitedest, siteowneraddr, siteownerdest)
    # The text file entries get a little extra info
    print >> fp, '# STANZA START:', listname
    print >> fp, '# CREATED:', time.ctime(time.time())
    # Now add all the standard alias entries
    for k, v in makealiases(listname):
        fqdnaddr = '%s@%s' % (k, hostname)
        if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
            localaddr = '%s@%s' % (k, mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN)
        else:
            localaddr = k
        # Format the text file nicely
        print >> fp, fqdnaddr, ((fieldsz - len(k)) * ' '), localaddr
    # Finish the text file stanza
    print >> fp, '# STANZA END:', listname
    print >> fp
示例#26
0
    def registerBounce(self, member, msg, weight=1.0, day=None, sibling=False):
        if not self.isMember(member):
            # check regular_include_lists, only one level
            if not self.regular_include_lists or sibling:
                return
            from Mailman.MailList import MailList

            for listaddr in self.regular_include_lists:
                listname, hostname = listaddr.split("@")
                listname = listname.lower()
                if listname == self.internal_name():
                    syslog("error", "Bouncer: %s: Include list self reference", listname)
                    continue
                try:
                    siblist = None
                    try:
                        siblist = MailList(listname)
                    except MMUnknownListError:
                        syslog("error", 'Bouncer: %s: Include list "%s" not found.', self.real_name, listname)
                        continue
                    siblist.registerBounce(member, msg, weight, day, sibling=True)
                    siblist.Save()
                finally:
                    if siblist and siblist.Locked():
                        siblist.Unlock()
            return
        info = self.getBounceInfo(member)
        if day is None:
            # Use today's date
            day = time.localtime()[:3]
        if not isinstance(info, _BounceInfo):
            # This is the first bounce we've seen from this member
            info = _BounceInfo(member, weight, day, self.bounce_you_are_disabled_warnings)
            # setBounceInfo is now called below after check phase.
            syslog("bounce", "%s: %s bounce score: %s", self.internal_name(), member, info.score)
            # Continue to the check phase below
        elif self.getDeliveryStatus(member) <> MemberAdaptor.ENABLED:
            # The user is already disabled, so we can just ignore subsequent
            # bounces.  These are likely due to residual messages that were
            # sent before disabling the member, but took a while to bounce.
            syslog("bounce", "%s: %s residual bounce received", self.internal_name(), member)
            return
        elif info.date == day:
            # We've already scored any bounces for this day, so ignore it.
            syslog(
                "bounce",
                "%s: %s already scored a bounce for date %s",
                self.internal_name(),
                member,
                time.strftime("%d-%b-%Y", day + (0, 0, 0, 0, 1, 0)),
            )
            # Continue to check phase below
        else:
            # See if this member's bounce information is stale.
            now = Utils.midnight(day)
            lastbounce = Utils.midnight(info.date)
            if lastbounce + self.bounce_info_stale_after < now:
                # Information is stale, so simply reset it
                info.reset(weight, day, self.bounce_you_are_disabled_warnings)
                syslog("bounce", "%s: %s has stale bounce info, resetting", self.internal_name(), member)
            else:
                # Nope, the information isn't stale, so add to the bounce
                # score and take any necessary action.
                info.score += weight
                info.date = day
                syslog("bounce", "%s: %s current bounce score: %s", self.internal_name(), member, info.score)
            # Continue to the check phase below
        #
        # Now that we've adjusted the bounce score for this bounce, let's
        # check to see if the disable-by-bounce threshold has been reached.
        if info.score >= self.bounce_score_threshold:
            if mm_cfg.VERP_PROBES:
                syslog(
                    "bounce",
                    "sending %s list probe to: %s (score %s >= %s)",
                    self.internal_name(),
                    member,
                    info.score,
                    self.bounce_score_threshold,
                )
                self.sendProbe(member, msg)
                info.reset(0, info.date, info.noticesleft)
            else:
                self.disableBouncingMember(member, info, msg)
        # We've set/changed bounce info above.  We now need to tell the
        # MemberAdaptor to set/update it.  We do it here in case the
        # MemberAdaptor stores bounce info externally to the list object to
        # be sure updated information is stored, but we have to be sure the
        # member wasn't removed.
        if self.isMember(member):
            self.setBounceInfo(member, info)
示例#27
0
def _check_for_virtual_loopaddr(mlist, filename):
    loopaddr = Utils.get_site_email(mlist.host_name, extra='loop')
    loopdest = Utils.ParseEmail(loopaddr)[0]
    siteaddr = Utils.get_site_email(mlist.host_name)
    sitedest = Utils.ParseEmail(siteaddr)[0]
    siteowneraddr = Utils.get_site_email(mlist.host_name, extra='owner')
    siteownerdest = Utils.ParseEmail(siteowneraddr)[0]
    if mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN:
        loopdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        sitedest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
        siteownerdest += '@' + mm_cfg.VIRTUAL_MAILMAN_LOCAL_DOMAIN
    # If the site list's host_name is a virtual domain, adding the list and
    # owner addresses to the SITE ADDRESSES will duplicate the entries in the
    # stanza for the list.  Postfix doesn't like dups so we try to comment them
    # here, but only for the actual site list domain.
    if (MailList(mm_cfg.MAILMAN_SITE_LIST,
                 lock=False).host_name.lower() == mlist.host_name.lower()):
        siteaddr = '#' + siteaddr
        siteowneraddr = '#' + siteowneraddr
    infp = open(filename)
    omask = os.umask(007)
    try:
        outfp = open(filename + '.tmp', 'w')
    finally:
        os.umask(omask)
    try:
        # Find the start of the loop address block
        while True:
            line = infp.readline()
            if not line:
                break
            outfp.write(line)
            if line.startswith('# LOOP ADDRESSES START'):
                break
        # Now see if our domain has already been written
        while True:
            line = infp.readline()
            if not line:
                break
            if line.startswith('# LOOP ADDRESSES END'):
                # It hasn't
                print >> outfp, '%s\t%s' % (loopaddr, loopdest)
                outfp.write(line)
                break
            elif line.startswith(loopaddr):
                # We just found it
                outfp.write(line)
                break
            else:
                # This isn't our loop address, so spit it out and continue
                outfp.write(line)
        # Now do it all again for the site list address. It must follow the
        # loop addresses.
        while True:
            line = infp.readline()
            if not line:
                break
            outfp.write(line)
            if line.startswith('# SITE ADDRESSES START'):
                break
        # Now see if our domain has already been written
        while True:
            line = infp.readline()
            if not line:
                break
            if line.startswith('# SITE ADDRESSES END'):
                # It hasn't
                print >> outfp, '%s\t%s' % (siteaddr, sitedest)
                print >> outfp, '%s\t%s' % (siteowneraddr, siteownerdest)
                outfp.write(line)
                break
            elif line.startswith(siteaddr) or line.startswith('#' + siteaddr):
                # We just found it
                outfp.write(line)
                break
            else:
                # This isn't our loop address, so spit it out and continue
                outfp.write(line)
        outfp.writelines(infp.readlines())
    finally:
        infp.close()
        outfp.close()
    os.rename(filename + '.tmp', filename)
#!/usr/bin/python2

import imp
import json
import sys

paths = imp.load_source('paths', '/usr/lib/mailman/bin/paths.py')

from Mailman.MailList import MailList
from Mailman.Utils import list_names

json.dump(
    {
        'ansible_facts': {
            'mailman': {
                'lists': [
                    MailList(name, lock=0).internal_name()
                    for name in list_names()
                ],
            },
        },
    },
    sys.stdout,
    indent=2)
示例#29
0
    def fixHostnames(self):
        """Fix up the host names in Mailman and the LP database."""
        # These can't be done at module global scope.
        from Mailman import Utils
        from Mailman import mm_cfg
        from Mailman.MailList import MailList

        # Grab a couple of useful components.
        email_address_set = getUtility(IEmailAddressSet)
        mailing_list_set = getUtility(IMailingListSet)

        # Clean things up per mailing list.
        for list_name in Utils.list_names():
            # Skip the site list.
            if list_name == mm_cfg.MAILMAN_SITE_LIST:
                continue

            # The first thing to clean up is the mailing list pickles.  There
            # are things like host names in some attributes that need to be
            # converted.  The following opens a locked list.
            mailing_list = MailList(list_name)
            try:
                mailing_list.host_name = mm_cfg.DEFAULT_EMAIL_HOST
                mailing_list.web_page_url = (
                    mm_cfg.DEFAULT_URL_PATTERN % mm_cfg.DEFAULT_URL_HOST)
                mailing_list.Save()
            finally:
                mailing_list.Unlock()

            # Patch up the email address for the list in the Launchpad
            # database.
            lp_mailing_list = mailing_list_set.get(list_name)
            if lp_mailing_list is None:
                # We found a mailing list in Mailman that does not exist in
                # the Launchpad database.  This can happen if we rsync'd the
                # Mailman directories after the lists were created, but we
                # copied the LP database /before/ the lists were created.
                # If we don't delete the Mailman lists, we won't be able to
                # create the mailing lists on staging.
                self.logger.error('No LP mailing list for: %s', list_name)
                self.deleteMailmanList(list_name)
                continue

            # Clean up the team email addresses corresponding to their mailing
            # lists.  Note that teams can have two email addresses if they
            # have a different contact address.
            team = getUtility(IPersonSet).getByName(list_name)
            mlist_addresses = email_address_set.getByPerson(team)
            if mlist_addresses.count() == 0:
                self.logger.error('No LP email address for: %s', list_name)
            else:
                # Teams can have both a mailing list and a contact address.
                old_address = '%s@%s' % (list_name, self.options.hostname)
                for email_address in mlist_addresses:
                    if email_address.email == old_address:
                        new_address = lp_mailing_list.address
                        removeSecurityProxy(email_address).email = new_address
                        self.logger.info('%s -> %s', old_address, new_address)
                        break
                else:
                    self.logger.error('No change to LP email address for: %s',
                                      list_name)
示例#30
0
 def _check_held_messages(self):
     """See if any held messages have been accepted or rejected."""
     try:
         dispositions = self._proxy.getMessageDispositions()
     except (xmlrpclib.ProtocolError, socket.error) as error:
         log_exception('Cannot talk to Launchpad:\n%s', error)
         return
     except xmlrpclib.Fault as error:
         log_exception('Launchpad exception: %s', error)
         return
     if dispositions:
         syslog('xmlrpc', 'Received dispositions for these message-ids: %s',
                COMMASPACE.join(dispositions))
     else:
         return
     # For each message that has been acted upon in Launchpad, handle the
     # message in here in Mailman.  We need to resort the dispositions so
     # that we can handle all of them for a particular mailing list at the
     # same time.
     by_list = {}
     for message_id, (team_name, action) in dispositions.items():
         accepts, declines, discards = by_list.setdefault(
             team_name, ([], [], []))
         if action == 'accept':
             accepts.append(message_id)
         elif action == 'decline':
             declines.append(message_id)
         elif action == 'discard':
             discards.append(message_id)
         else:
             syslog('xmlrpc',
                    'Skipping invalid disposition "%s" for message-id: %s',
                    action, message_id)
     # Now cycle through the dispositions for every mailing list.
     for team_name in by_list:
         try:
             mlist = MailList(team_name)
         except Errors.MMUnknownListError:
             log_exception('Skipping dispositions for unknown list: %s',
                           team_name)
             continue
         try:
             accepts, declines, discards = by_list[team_name]
             for message_id in accepts:
                 request_id = mlist.held_message_ids.pop(message_id, None)
                 if request_id is None:
                     syslog('xmlrpc', 'Missing accepted message-id: %s',
                            message_id)
                 else:
                     mlist.HandleRequest(request_id, mm_cfg.APPROVE)
                     syslog('xmlrpc', 'Approved: %s', message_id)
             for message_id in declines:
                 request_id = mlist.held_message_ids.pop(message_id, None)
                 if request_id is None:
                     syslog('xmlrpc', 'Missing declined message-id: %s',
                            message_id)
                 else:
                     mlist.HandleRequest(request_id, mm_cfg.REJECT)
                     syslog('xmlrpc', 'Rejected: %s', message_id)
             for message_id in discards:
                 request_id = mlist.held_message_ids.pop(message_id, None)
                 if request_id is None:
                     syslog('xmlrpc', 'Missing declined message-id: %s',
                            message_id)
                 else:
                     mlist.HandleRequest(request_id, mm_cfg.DISCARD)
                     syslog('xmlrpc', 'Discarded: %s', message_id)
             mlist.Save()
         finally:
             mlist.Unlock()
示例#31
0
    def _update_list_subscriptions(self, list_name, subscription_info):
        """Update the subscription information for a single mailing list.

        :param list_name: The name of the mailing list to update.
        :type list_name: string
        :param subscription_info: The mailing list's new subscription
            information.
        :type subscription_info: a list of 4-tuples containing the address,
            real name, flags, and status of each person in the list's
            subscribers.
        """
        ## syslog('xmlrpc', '%s subinfo: %s', list_name, subscription_info)
        # Start with an unlocked list.
        mlist = MailList(list_name, lock=False)
        # Create a mapping of email address to the member's real name,
        # flags, and status.  Note that flags is currently unused.
        member_map = dict(
            (address, (realname, flags, status))
            for address, realname, flags, status in subscription_info)
        # In Mailman parlance, the 'member key' is the lower-cased
        # address.  We need a mapping from the member key to
        # case-preserved address for all future members of the list.
        key_to_case_preserved = dict(
            (address.lower(), address) for address in member_map)
        # The following modifications to membership may be made:
        # - Current members whose address is changing case
        # - New members who need to be added to the mailing list
        # - Old members who need to be removed from the mailing list
        # - Current members whose settings are being changed
        #
        # Start by getting the case-folded membership sets of all current
        # and future members.
        current_members = set(mlist.getMembers())
        future_members = set(key_to_case_preserved)
        # Additions are all those addresses in future_members who are not
        # in current_members.
        additions = future_members - current_members
        # Deletions are all those addresses in current_members who are not
        # in future_members.
        deletions = current_members - future_members
        # Any address in both current and future members is either
        # changing case, updating settings, or both.
        updates = current_members & future_members
        # If there's nothing to do, just skip this list.
        if not additions and not deletions and not updates:
            # Why did we get here?  This list should not have shown up in
            # getMembershipInformation().
            syslog('xmlrpc', 'Strange subscription information for list: %s',
                   list_name)
            return
        # Lock the list and make the modifications.  Don't worry if the list
        # couldn't be locked, we'll just log that and try again the next time
        # through the loop.  Eventually any existing lock will expire anyway.
        try:
            mlist.Lock(2)
        except TimeOutError:
            syslog('xmlrpc',
                   'Could not lock the list to update subscriptions: %s',
                   list_name)
            return
        try:
            # Handle additions first.
            if len(additions) > 0:
                syslog('xmlrpc', 'Adding to %s: %s', list_name, additions)
            for address in additions:
                # When adding the new member, be sure to use the
                # case-preserved email address.
                original_address = key_to_case_preserved[address]
                realname, flags, status = member_map[original_address]
                mlist.addNewMember(original_address, realname=realname)
                mlist.setDeliveryStatus(original_address, status)
            # Handle deletions next.
            if len(deletions) > 0:
                syslog('xmlrpc', 'Removing from %s: %s', list_name, deletions)
            for address in deletions:
                mlist.removeMember(address)
            # Updates can be either a settings update, a change in the
            # case of the subscribed address, or both.
            found_updates = []
            for address in updates:
                # See if the case is changing.
                current_address = mlist.getMemberCPAddress(address)
                future_address = key_to_case_preserved[address]
                if current_address != future_address:
                    mlist.changeMemberAddress(address, future_address)
                    found_updates.append('%s -> %s' %
                                         (address, future_address))
                # flags are ignored for now.
                realname, flags, status = member_map[future_address]
                if realname != mlist.getMemberName(address):
                    mlist.setMemberName(address, realname)
                    found_updates.append('%s new name: %s' %
                                         (address, realname))
                if status != mlist.getDeliveryStatus(address):
                    mlist.setDeliveryStatus(address, status)
                    found_updates.append('%s new status: %s' %
                                         (address, status))
            if len(found_updates) > 0:
                syslog('xmlrpc', 'Membership updates for %s: %s', list_name,
                       found_updates)
            # We're done, so flush the changes for this mailing list.
            mlist.Save()
        finally:
            mlist.Unlock()