示例#1
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()
示例#2
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()
示例#3
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()
示例#5
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')
示例#6
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')
示例#7
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()
示例#8
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)
示例#9
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()
示例#10
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()
示例#11
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)