From 680008b74212ead3d6178e29880a0c823d142f83 Mon Sep 17 00:00:00 2001 From: bwarsaw <> Date: Mon, 22 Sep 2003 02:58:13 +0000 Subject: Backporting from the HEAD -- Mailman package --- Mailman/Bouncer.py | 24 +++++++++++++----------- Mailman/Defaults.py.in | 10 +++++++++- Mailman/MailList.py | 1 + Mailman/Pending.py | 26 ++++++++++++++++++++------ Mailman/Utils.py | 30 ++++++++++++++++++++++++++---- Mailman/htmlformat.py | 2 +- 6 files changed, 70 insertions(+), 23 deletions(-) (limited to 'Mailman') diff --git a/Mailman/Bouncer.py b/Mailman/Bouncer.py index 34bd21d4..196d6fa3 100644 --- a/Mailman/Bouncer.py +++ b/Mailman/Bouncer.py @@ -102,16 +102,18 @@ class Bouncer: # New style delivery status self.delivery_status = {} - def registerBounce(self, member, msg, weight=1.0): + def registerBounce(self, member, msg, weight=1.0, day=None): if not self.isMember(member): return info = self.getBounceInfo(member) - today = time.localtime()[:3] + 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 cookie = Pending.new(Pending.RE_ENABLE, self.internal_name(), member) - info = _BounceInfo(member, weight, today, + info = _BounceInfo(member, weight, day, self.bounce_you_are_disabled_warnings, cookie) self.setBounceInfo(member, info) @@ -125,26 +127,26 @@ class Bouncer: syslog('bounce', '%s: %s residual bounce received', self.internal_name(), member) return - elif info.date == today: - # We've already scored any bounces for today, so ignore this one. - syslog('bounce', '%s: %s already scored a bounce for today', - self.internal_name(), member) + 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,)*6)) # Continue to check phase below else: # See if this member's bounce information is stale. - now = Utils.midnight(today) + 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, today, - self.bounce_you_are_disabled_warnings) + 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 = today + info.date = day syslog('bounce', '%s: %s current bounce score: %s', member, self.internal_name(), info.score) # Continue to the check phase below diff --git a/Mailman/Defaults.py.in b/Mailman/Defaults.py.in index 819a9062..e901bea6 100644 --- a/Mailman/Defaults.py.in +++ b/Mailman/Defaults.py.in @@ -233,7 +233,11 @@ DEFAULT_DIGEST_VOLUME_FREQUENCY = 1 # attributes) the internal Pipermail archiver is used. This is the default if # both of these variables are set to No. When either is set, the value should # be a shell command string which will get passed to os.popen(). This string -# can contain %(listname)s for dictionary interpolation. The name of the list +# can contain the following substitution strings: +# +# %(listname)s -- gets the internal name of the list +# %(hostname)s -- gets the email hostname for the list +# # being archived will be substituted for this. Please note that os.popen() is # used. # @@ -646,6 +650,7 @@ QRUNNERS = [ ('NewsRunner', 1), # outgoing messages to the nntpd ('OutgoingRunner', 1), # outgoing messages to the smtpd ('VirginRunner', 1), # internally crafted (virgin birth) messages + ('RetryRunner', 1), # retry temporarily failed deliveries ] # Set this to Yes to use the `Maildir' delivery option. If you change this @@ -1227,6 +1232,7 @@ ARCHQUEUE_DIR = os.path.join(QUEUE_DIR, 'archive') SHUNTQUEUE_DIR = os.path.join(QUEUE_DIR, 'shunt') VIRGINQUEUE_DIR = os.path.join(QUEUE_DIR, 'virgin') BADQUEUE_DIR = os.path.join(QUEUE_DIR, 'bad') +RETRYQUEUE_DIR = os.path.join(QUEUE_DIR, 'retry') MAILDIR_DIR = os.path.join(QUEUE_DIR, 'maildir') # Other useful files @@ -1271,6 +1277,8 @@ add_language('pl', _('Polish'), 'iso-8859-2') add_language('pt', _('Portuguese'), 'iso-8859-1') add_language('pt_BR', _('Portuguese (Brazil)'), 'iso-8859-1') add_language('ru', _('Russian'), 'koi8-r') +add_language('sr', _('Serbian'), 'utf-8') add_language('sv', _('Swedish'), 'iso-8859-1') +add_language('uk', _('Ukrainian'), 'utf-8') del _ diff --git a/Mailman/MailList.py b/Mailman/MailList.py index e0bcc893..9979d221 100644 --- a/Mailman/MailList.py +++ b/Mailman/MailList.py @@ -683,6 +683,7 @@ class MailList(HTMLFormatter, Deliverer, ListAdmin, crafting a message to the member informing them of the invitation. """ invitee = userdesc.address + Utils.ValidateEmail(invitee) requestaddr = self.GetRequestEmail() # Hack alert! Squirrel away a flag that only invitations have, so # that we can do something slightly different when an invitation diff --git a/Mailman/Pending.py b/Mailman/Pending.py index 0d6986cf..82a067cf 100644 --- a/Mailman/Pending.py +++ b/Mailman/Pending.py @@ -49,6 +49,12 @@ _ALLKEYS = [(x,) for x in (SUBSCRIPTION, UNSUBSCRIPTION, RE_ENABLE, )] +try: + True, False +except NameError: + True = 1 + False = 0 + def new(*content): @@ -73,12 +79,20 @@ def new(*content): continue # Load the current database db = _load() - # Calculate a unique cookie - while 1: - n = random.random() + # Calculate a unique cookie. Algorithm vetted by the Timbot. + # time() has high resolution on Linux, clock() on Windows. random + # gives us about 45 bits in Python 2.2, 53 bits on Python 2.3. + # The time and clock values basically help obscure the random + # number generator, as does the hash calculation. The integral + # parts of the time values are discarded because they're the most + # predictable bits. + while True: now = time.time() - hashfood = str(now) + str(n) + str(content) + x = random.random() + now % 1.0 + time.clock() % 1.0 + hashfood = repr(x) cookie = sha.new(hashfood).hexdigest() + # We'll never get a duplicate, but we'll be anal about + # checking anyway. if not db.has_key(cookie): break # Store the content, plus the time in the future when this entry @@ -101,10 +115,10 @@ def new(*content): -def confirm(cookie, expunge=1): +def confirm(cookie, expunge=True): """Return data for cookie, or None if not found. - If optional expunge is true (the default), the record is also removed from + If optional expunge is True (the default), the record is also removed from the database. """ if not expunge: diff --git a/Mailman/Utils.py b/Mailman/Utils.py index ceb63f66..13e751a7 100644 --- a/Mailman/Utils.py +++ b/Mailman/Utils.py @@ -360,6 +360,17 @@ def websafe(s): return cgi.escape(s, quote=1) +def nntpsplit(s): + parts = s.split(':', 1) + if len(parts) == 2: + try: + return parts[0], int(parts[1]) + except ValueError: + pass + # Use the defaults + return s, 119 + + # Just changing these two functions should be enough to control the way # that email address obscuring is handled. @@ -381,7 +392,7 @@ def UnobscureEmail(addr): -def maketext(templatefile, dict=None, raw=False, lang=None, mlist=None): +def findtext(templatefile, dict=None, raw=False, lang=None, mlist=None): # Make some text from a template file. The order of searches depends on # whether mlist and lang are provided. Once the templatefile is found, # string substitution is performed by interpolation in `dict'. If `raw' @@ -426,6 +437,12 @@ def maketext(templatefile, dict=None, raw=False, lang=None, mlist=None): # on the above description. Mailman's upgrade script cannot do this for # you. # + # The function has been revised and renamed as it now returns both the + # template text and the path from which it retrieved the template. The + # original function is now a wrapper which just returns the template text + # as before, by calling this renamed function and discarding the second + # item returned. + # # Calculate the languages to scan languages = [] if lang is not None: @@ -460,7 +477,8 @@ def maketext(templatefile, dict=None, raw=False, lang=None, mlist=None): # Try one last time with the distro English template, which, unless # you've got a really broken installation, must be there. try: - fp = open(os.path.join(mm_cfg.TEMPLATE_DIR, 'en', templatefile)) + filename = os.path.join(mm_cfg.TEMPLATE_DIR, 'en', templatefile) + fp = open(filename) except IOError, e: if e.errno <> errno.ENOENT: raise # We never found the template. BAD! @@ -483,8 +501,12 @@ def maketext(templatefile, dict=None, raw=False, lang=None, mlist=None): syslog('error', 'broken template: %s\n%s', filename, e) pass if raw: - return text - return wrap(text) + return text, filename + return wrap(text), filename + + +def maketext(templatefile, dict=None, raw=False, lang=None, mlist=None): + return findtext(templatefile, dict, raw, lang, mlist)[0] diff --git a/Mailman/htmlformat.py b/Mailman/htmlformat.py index 0175bccb..f41c0b43 100644 --- a/Mailman/htmlformat.py +++ b/Mailman/htmlformat.py @@ -323,7 +323,7 @@ class Document(Container): if mm_cfg.WEB_ALINK_COLOR: kws.setdefault('alink', mm_cfg.WEB_ALINK_COLOR) if mm_cfg.WEB_LINK_COLOR: - kws.setdefault('alink', mm_cfg.WEB_LINK_COLOR) + kws.setdefault('link', mm_cfg.WEB_LINK_COLOR) for k, v in kws.items(): quals.append('%s="%s"' % (k, v)) output.append('%s' % (tab, SPACE.join(quals))) -- cgit v1.2.3