Пример #1
0
    def FormatDisabledNotice(self, user):
        status = self.getDeliveryStatus(user)
        reason = None
        info = self.getBounceInfo(user)
        if status == MemberAdaptor.BYUSER:
            reason = _('; it was disabled by you')
        elif status == MemberAdaptor.BYADMIN:
            reason = _('; it was disabled by the list administrator')
        elif status == MemberAdaptor.BYBOUNCE:
            date = time.strftime('%d-%b-%Y',
                                 time.localtime(Utils.midnight(info.date)))
            reason = _('''; it was disabled due to excessive bounces.  The
            last bounce was received on %(date)s''')
        elif status == MemberAdaptor.UNKNOWN:
            reason = _('; it was disabled for unknown reasons')
        if reason:
            note = FontSize(
                '+1',
                _('Note: your list delivery is currently disabled%(reason)s.')
            ).Format()
            link = Link('#disable', _('Mail delivery')).Format()
            mailto = Link('mailto:' + self.GetOwnerEmail(),
                          _('the list administrator')).Format()
            return _('''<p>%(note)s

            <p>You may have disabled list delivery intentionally,
            or it may have been triggered by bounces from your email
            address.  In either case, to re-enable delivery, change the
            %(link)s option below.  Contact %(mailto)s if you have any
            questions or need assistance.''')
        elif info and info.score > 0:
            # Provide information about their current bounce score.  We know
            # their membership is currently enabled.
            score = info.score
            total = self.bounce_score_threshold
            return _('''<p>We have received some recent bounces from your
            address.  Your current <em>bounce score</em> is %(score)s out of a
            maximum of %(total)s.  Please double check that your subscribed
            address is correct and that there are no problems with delivery to
            this address.  Your bounce score will be automatically reset if
            the problems are corrected soon.''')
        else:
            return ''
    def FormatDisabledNotice(self, user):
        status = self.getDeliveryStatus(user)
        reason = None
        info = self.getBounceInfo(user)
        if status == MemberAdaptor.BYUSER:
            reason = _('; it was disabled by you')
        elif status == MemberAdaptor.BYADMIN:
            reason = _('; it was disabled by the list administrator')
        elif status == MemberAdaptor.BYBOUNCE:
            date = time.strftime('%d-%b-%Y',
                                 time.localtime(Utils.midnight(info.date)))
            reason = _('''; it was disabled due to excessive bounces.  The
            last bounce was received on %(date)s''')
        elif status == MemberAdaptor.UNKNOWN:
            reason = _('; it was disabled for unknown reasons')
        if reason:
            note = FontSize('+1', _(
                'Note: your list delivery is currently disabled%(reason)s.'
                )).Format()
            link = Link('#disable', _('Mail delivery')).Format()
            mailto = Link('mailto:' + self.GetOwnerEmail(),
                          _('the list administrator')).Format()
            return _('''<p>%(note)s

            <p>You may have disabled list delivery intentionally,
            or it may have been triggered by bounces from your email
            address.  In either case, to re-enable delivery, change the
            %(link)s option below.  Contact %(mailto)s if you have any
            questions or need assistance.''')
        elif info and info.score > 0:
            # Provide information about their current bounce score.  We know
            # their membership is currently enabled.
            score = info.score
            total = self.bounce_score_threshold
            return _('''<p>We have received some recent bounces from your
            address.  Your current <em>bounce score</em> is %(score)s out of a
            maximum of %(total)s.  Please double check that your subscribed
            address is correct and that there are no problems with delivery to
            this address.  Your bounce score will be automatically reset if
            the problems are corrected soon.''')
        else:
            return ''
Пример #3
0
 def sendNextNotification(self, member):
     info = self.getBounceInfo(member)
     if info is None:
         return
     reason = self.getDeliveryStatus(member)
     if info.noticesleft <= 0:
         # BAW: Remove them now, with a notification message
         self.ApprovedDeleteMember(
             member,
             'disabled address',
             admin_notif=self.bounce_notify_owner_on_removal,
             userack=1)
         # Expunge the pending cookie for the user.  We throw away the
         # returned data.
         self.pend_confirm(info.cookie)
         if reason == MemberAdaptor.BYBOUNCE:
             syslog('bounce', '%s: %s deleted after exhausting notices',
                    self.internal_name(), member)
         syslog(
             'subscribe', '%s: %s auto-unsubscribed [reason: %s]',
             self.internal_name(), member, {
                 MemberAdaptor.BYBOUNCE: 'BYBOUNCE',
                 MemberAdaptor.BYUSER: '******',
                 MemberAdaptor.BYADMIN: 'BYADMIN',
                 MemberAdaptor.UNKNOWN: 'UNKNOWN'
             }.get(reason, 'invalid value'))
         return
     # Send the next notification
     confirmurl = '%s/%s' % (self.GetScriptURL('confirm',
                                               absolute=1), info.cookie)
     optionsurl = self.GetOptionsURL(member, absolute=1)
     reqaddr = self.GetRequestEmail()
     lang = self.getMemberLanguage(member)
     txtreason = REASONS.get(reason)
     if txtreason is None:
         txtreason = _('for unknown reasons')
     else:
         txtreason = _(txtreason)
     # Give a little bit more detail on bounce disables
     if reason == MemberAdaptor.BYBOUNCE:
         date = time.strftime('%d-%b-%Y',
                              time.localtime(Utils.midnight(info.date)))
         extra = _(' The last bounce received from you was dated %(date)s')
         txtreason += extra
     text = Utils.maketext('disabled.txt', {
         'listname': self.real_name,
         'noticesleft': info.noticesleft,
         'confirmurl': confirmurl,
         'optionsurl': optionsurl,
         'password': self.getMemberPassword(member),
         'owneraddr': self.GetOwnerEmail(),
         'reason': txtreason,
     },
                           lang=lang,
                           mlist=self)
     msg = Message.UserNotification(member, reqaddr, text=text, lang=lang)
     # BAW: See the comment in MailList.py ChangeMemberAddress() for why we
     # set the Subject this way.
     del msg['subject']
     msg['Subject'] = 'confirm ' + info.cookie
     # Send without Precedence: bulk.  Bug #808821.
     msg.send(self, noprecedence=True)
     info.noticesleft -= 1
     info.lastnotice = time.localtime()[:3]
     # In case the MemberAdaptor stores bounce info externally to
     # the list, we need to tell it to update
     self.setBounceInfo(member, info)
Пример #4
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)
Пример #5
0
 def sendNextNotification(self, member):
     info = self.getBounceInfo(member)
     if info is None:
         return
     reason = self.getDeliveryStatus(member)
     if info.noticesleft <= 0:
         # BAW: Remove them now, with a notification message
         self.ApprovedDeleteMember(
             member, "disabled address", admin_notif=self.bounce_notify_owner_on_removal, userack=1
         )
         # Expunge the pending cookie for the user.  We throw away the
         # returned data.
         self.pend_confirm(info.cookie)
         if reason == MemberAdaptor.BYBOUNCE:
             syslog("bounce", "%s: %s deleted after exhausting notices", self.internal_name(), member)
         syslog(
             "subscribe",
             "%s: %s auto-unsubscribed [reason: %s]",
             self.internal_name(),
             member,
             {
                 MemberAdaptor.BYBOUNCE: "BYBOUNCE",
                 MemberAdaptor.BYUSER: "******",
                 MemberAdaptor.BYADMIN: "BYADMIN",
                 MemberAdaptor.UNKNOWN: "UNKNOWN",
             }.get(reason, "invalid value"),
         )
         return
     # Send the next notification
     confirmurl = "%s/%s" % (self.GetScriptURL("confirm", absolute=1), info.cookie)
     optionsurl = self.GetOptionsURL(member, absolute=1)
     reqaddr = self.GetRequestEmail()
     lang = self.getMemberLanguage(member)
     txtreason = REASONS.get(reason)
     if txtreason is None:
         txtreason = _("for unknown reasons")
     else:
         txtreason = _(txtreason)
     # Give a little bit more detail on bounce disables
     if reason == MemberAdaptor.BYBOUNCE:
         date = time.strftime("%d-%b-%Y", time.localtime(Utils.midnight(info.date)))
         extra = _(" The last bounce received from you was dated %(date)s")
         txtreason += extra
     text = Utils.maketext(
         "disabled.txt",
         {
             "listname": self.real_name,
             "noticesleft": info.noticesleft,
             "confirmurl": confirmurl,
             "optionsurl": optionsurl,
             "password": self.getMemberPassword(member),
             "owneraddr": self.GetOwnerEmail(),
             "reason": txtreason,
         },
         lang=lang,
         mlist=self,
     )
     msg = Message.UserNotification(member, reqaddr, text=text, lang=lang)
     # BAW: See the comment in MailList.py ChangeMemberAddress() for why we
     # set the Subject this way.
     del msg["subject"]
     msg["Subject"] = "confirm " + info.cookie
     # Send without Precedence: bulk.  Bug #808821.
     msg.send(self, noprecedence=True)
     info.noticesleft -= 1
     info.lastnotice = time.localtime()[:3]
     # In case the MemberAdaptor stores bounce info externally to
     # the list, we need to tell it to update
     self.setBounceInfo(member, info)
Пример #6
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)
Пример #7
0
 def sendNextNotification(self, member):
     info = self.getBounceInfo(member)
     if info is None:
         return
     reason = self.getDeliveryStatus(member)
     if info.noticesleft <= 0:
         # BAW: Remove them now, with a notification message
         self.ApprovedDeleteMember(
             member, 'disabled address',
             admin_notif=self.bounce_notify_owner_on_removal,
             userack=1)
         # Expunge the pending cookie for the user.  We throw away the
         # returned data.
         self.pend_confirm(info.cookie)
         if reason == MemberAdaptor.BYBOUNCE:
             syslog('bounce', '%s: %s deleted after exhausting notices',
                    self.internal_name(), member)
         syslog('subscribe', '%s: %s auto-unsubscribed [reason: %s]',
                self.internal_name(), member,
                {MemberAdaptor.BYBOUNCE: 'BYBOUNCE',
                 MemberAdaptor.BYUSER: '******',
                 MemberAdaptor.BYADMIN: 'BYADMIN',
                 MemberAdaptor.UNKNOWN: 'UNKNOWN'}.get(
             reason, 'invalid value'))
         return
     # Send the next notification
     confirmurl = '%s/%s' % (self.GetScriptURL('confirm', absolute=1),
                             info.cookie)
     optionsurl = self.GetOptionsURL(member, absolute=1)
     reqaddr = self.GetRequestEmail()
     lang = self.getMemberLanguage(member)
     txtreason = REASONS.get(reason)
     if txtreason is None:
         txtreason = _('for unknown reasons')
     else:
         txtreason = _(txtreason)
     # Give a little bit more detail on bounce disables
     if reason == MemberAdaptor.BYBOUNCE:
         date = time.strftime('%d-%b-%Y',
                              time.localtime(Utils.midnight(info.date)))
         extra = _(' The last bounce received from you was dated %(date)s')
         txtreason += extra
     text = Utils.maketext(
         'disabled.txt',
         {'listname'   : self.real_name,
          'noticesleft': info.noticesleft,
          'confirmurl' : confirmurl,
          'optionsurl' : optionsurl,
          'password'   : self.getMemberPassword(member),
          'owneraddr'  : self.GetOwnerEmail(),
          'reason'     : txtreason,
          }, lang=lang, mlist=self)
     msg = Message.UserNotification(member, reqaddr, text=text, lang=lang)
     # BAW: See the comment in MailList.py ChangeMemberAddress() for why we
     # set the Subject this way.
     del msg['subject']
     msg['Subject'] = 'confirm ' + info.cookie
     msg.send(self)
     info.noticesleft -= 1
     info.lastnotice = time.localtime()[:3]
     # In case the MemberAdaptor stores bounce info externally to
     # the list, we need to tell it to update
     self.setBounceInfo(member, info)
Пример #8
0
 def registerBounce(self, member, msg, weight=1.0, day=None):
     if not self.isMember(member):
         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)