aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Mailman/Bouncer.py24
-rw-r--r--Mailman/Defaults.py.in10
-rw-r--r--Mailman/MailList.py1
-rw-r--r--Mailman/Pending.py26
-rw-r--r--Mailman/Utils.py30
-rw-r--r--Mailman/htmlformat.py2
6 files changed, 70 insertions, 23 deletions
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<BODY %s>' % (tab, SPACE.join(quals)))