1595
by Mark Sapiro
Merged and tweaked Jim P's mailman-auto-mod-verbose-members branch. |
1 |
# Copyright (C) 1998-2016 by the Free Software Foundation, Inc.
|
1
by
This commit was manufactured by cvs2svn to create branch |
2 |
#
|
3 |
# This program is free software; you can redistribute it and/or
|
|
4 |
# modify it under the terms of the GNU General Public License
|
|
5 |
# as published by the Free Software Foundation; either version 2
|
|
6 |
# of the License, or (at your option) any later version.
|
|
7 |
#
|
|
8 |
# This program is distributed in the hope that it will be useful,
|
|
9 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11 |
# GNU General Public License for more details.
|
|
12 |
#
|
|
13 |
# You should have received a copy of the GNU General Public License
|
|
14 |
# along with this program; if not, write to the Free Software
|
|
830
by bwarsaw
A cleansing pass, almost entirely cosmetic. Such things as whitespace |
15 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
16 |
# USA.
|
|
1
by
This commit was manufactured by cvs2svn to create branch |
17 |
|
18 |
||
19 |
"""Routines which rectify an old mailing list with current structure.
|
|
20 |
||
21 |
The MailList.CheckVersion() method looks for an old .data_version setting in
|
|
22 |
the loaded structure, and if found calls the Update() routine from this
|
|
23 |
module, supplying the list and the state last loaded from storage. The state
|
|
24 |
is necessary to distinguish from default assignments done in the .InitVars()
|
|
25 |
methods, before .CheckVersion() is called.
|
|
26 |
||
27 |
For new versions you should add sections to the UpdateOldVars() and the
|
|
28 |
UpdateOldUsers() sections, to preserve the sense of settings across structural
|
|
29 |
changes. Note that the routines have only one pass - when .CheckVersions()
|
|
30 |
finds a version change it runs this routine and then updates the data_version
|
|
31 |
number of the list, and then does a .Save(), so the transformations won't be
|
|
32 |
run again until another version change is detected.
|
|
33 |
"""
|
|
34 |
||
35 |
||
1018
by Mark Sapiro
versions.py - Moved imports to module scope. |
36 |
import email |
37 |
||
1
by
This commit was manufactured by cvs2svn to create branch |
38 |
from types import ListType, StringType |
39 |
||
40 |
from Mailman import mm_cfg |
|
41 |
from Mailman import Utils |
|
42 |
from Mailman import Message |
|
1018
by Mark Sapiro
versions.py - Moved imports to module scope. |
43 |
from Mailman.Bouncer import _BounceInfo |
1
by
This commit was manufactured by cvs2svn to create branch |
44 |
from Mailman.MemberAdaptor import UNKNOWN |
45 |
from Mailman.Logging.Syslog import syslog |
|
46 |
||
47 |
||
48 |
||
49 |
def Update(l, stored_state): |
|
50 |
"Dispose of old vars and user options, mapping to new ones when suitable."
|
|
51 |
ZapOldVars(l) |
|
52 |
UpdateOldUsers(l) |
|
53 |
NewVars(l) |
|
54 |
UpdateOldVars(l, stored_state) |
|
55 |
CanonicalizeUserOptions(l) |
|
56 |
NewRequestsDatabase(l) |
|
57 |
||
58 |
||
59 |
||
60 |
def ZapOldVars(mlist): |
|
61 |
for name in ('num_spawns', 'filter_prog', 'clobber_date', |
|
62 |
'public_archive_file_dir', 'private_archive_file_dir', |
|
63 |
'archive_directory', |
|
64 |
# Pre-2.1a4 bounce data
|
|
65 |
'minimum_removal_date', |
|
66 |
'minimum_post_count_before_bounce_action', |
|
67 |
'automatic_bounce_action', |
|
68 |
'max_posts_between_bounces', |
|
69 |
):
|
|
70 |
if hasattr(mlist, name): |
|
71 |
delattr(mlist, name) |
|
72 |
||
73 |
||
74 |
||
75 |
uniqueval = [] |
|
76 |
def UpdateOldVars(l, stored_state): |
|
77 |
"""Transform old variable values into new ones, deleting old ones.
|
|
78 |
stored_state is last snapshot from file, as opposed to from InitVars()."""
|
|
79 |
||
80 |
def PreferStored(oldname, newname, newdefault=uniqueval, |
|
81 |
l=l, state=stored_state): |
|
82 |
"""Use specified old value if new value is not in stored state.
|
|
83 |
||
84 |
If the old attr does not exist, and no newdefault is specified, the
|
|
85 |
new attr is *not* created - so either specify a default or be positive
|
|
86 |
that the old attr exists - or don't depend on the new attr.
|
|
87 |
||
88 |
"""
|
|
89 |
if hasattr(l, oldname): |
|
90 |
if not state.has_key(newname): |
|
91 |
setattr(l, newname, getattr(l, oldname)) |
|
92 |
delattr(l, oldname) |
|
93 |
if not hasattr(l, newname) and newdefault is not uniqueval: |
|
94 |
setattr(l, newname, newdefault) |
|
95 |
||
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
96 |
def recode(mlist, f, t): |
97 |
"""If the character set for a list's preferred_language has changed,
|
|
98 |
attempt to recode old string values into the new character set.
|
|
99 |
||
100 |
mlist is the list, f is the old charset and t is the new charset.
|
|
101 |
"""
|
|
102 |
for x in dir(mlist): |
|
103 |
if x.startswith('_'): |
|
104 |
continue
|
|
105 |
nv = doitem(getattr(mlist, x), f, t) |
|
106 |
if nv: |
|
107 |
setattr(mlist, x, nv) |
|
108 |
||
109 |
def doitem(v, f, t): |
|
110 |
"""Recursively process lists, tuples and dictionary values and
|
|
111 |
convert strings as needed. Return either the updated item or None
|
|
112 |
if no change."""
|
|
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
113 |
changed = False |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
114 |
if isinstance(v, str): |
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
115 |
return convert(v, f, t) |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
116 |
elif isinstance(v, list): |
117 |
for i in range(len(v)): |
|
118 |
nv = doitem(v[i], f, t) |
|
119 |
if nv: |
|
120 |
changed = True |
|
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
121 |
v[i] = nv |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
122 |
if changed: |
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
123 |
return v |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
124 |
else: |
125 |
return None |
|
126 |
elif isinstance(v, tuple): |
|
127 |
nt = () |
|
128 |
for i in range(len(v)): |
|
129 |
nv = doitem(v[i], f, t) |
|
130 |
if nv: |
|
131 |
changed = True |
|
132 |
nt += (nv,) |
|
133 |
else: |
|
134 |
nt += (v[i],) |
|
135 |
if changed: |
|
136 |
return nt |
|
137 |
else: |
|
138 |
return None |
|
139 |
elif isinstance(v, dict): |
|
140 |
for k, ov in v.items(): |
|
141 |
nv = doitem(ov, f, t) |
|
142 |
if nv: |
|
143 |
changed = True |
|
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
144 |
v[k] = nv |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
145 |
if changed: |
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
146 |
return v |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
147 |
else: |
148 |
return None |
|
149 |
else: |
|
150 |
return None |
|
151 |
||
152 |
def convert(s, f, t): |
|
153 |
"""This does the actual character set conversion of the string s
|
|
154 |
from charset f to charset t."""
|
|
155 |
||
156 |
try: |
|
157 |
u = unicode(s, f) |
|
158 |
is_f = True |
|
159 |
except ValueError: |
|
160 |
is_f = False |
|
161 |
try: |
|
162 |
unicode(s, t) |
|
163 |
is_t = True |
|
164 |
except ValueError: |
|
165 |
is_t = False |
|
166 |
if is_f and not is_t: |
|
167 |
return u.encode(t, 'replace') |
|
168 |
else: |
|
1537
by Mark Sapiro
Cleaned the code a bit. No change in results. |
169 |
return None |
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
170 |
|
1
by
This commit was manufactured by cvs2svn to create branch |
171 |
# Migrate to 2.1b3, baw 17-Aug-2001
|
172 |
if hasattr(l, 'dont_respond_to_post_requests'): |
|
173 |
oldval = getattr(l, 'dont_respond_to_post_requests') |
|
174 |
if not hasattr(l, 'respond_to_post_requests'): |
|
175 |
l.respond_to_post_requests = not oldval |
|
176 |
del l.dont_respond_to_post_requests |
|
177 |
||
178 |
# Migrate to 2.1b3, baw 13-Oct-2001
|
|
179 |
# Basic defaults for new variables
|
|
180 |
if not hasattr(l, 'default_member_moderation'): |
|
181 |
l.default_member_moderation = mm_cfg.DEFAULT_DEFAULT_MEMBER_MODERATION |
|
182 |
if not hasattr(l, 'accept_these_nonmembers'): |
|
183 |
l.accept_these_nonmembers = [] |
|
184 |
if not hasattr(l, 'hold_these_nonmembers'): |
|
185 |
l.hold_these_nonmembers = [] |
|
186 |
if not hasattr(l, 'reject_these_nonmembers'): |
|
187 |
l.reject_these_nonmembers = [] |
|
188 |
if not hasattr(l, 'discard_these_nonmembers'): |
|
189 |
l.discard_these_nonmembers = [] |
|
190 |
if not hasattr(l, 'forward_auto_discards'): |
|
191 |
l.forward_auto_discards = mm_cfg.DEFAULT_FORWARD_AUTO_DISCARDS |
|
192 |
if not hasattr(l, 'generic_nonmember_action'): |
|
193 |
l.generic_nonmember_action = mm_cfg.DEFAULT_GENERIC_NONMEMBER_ACTION |
|
194 |
# Now convert what we can... Note that the interaction between the
|
|
195 |
# MM2.0.x attributes `moderated', `member_posting_only', and `posters' is
|
|
196 |
# so confusing, it makes my brain really ache. Which is why they go away
|
|
197 |
# in MM2.1. I think the best we can do semantically is the following:
|
|
198 |
#
|
|
199 |
# - If moderated == yes, then any sender who's address is not on the
|
|
200 |
# posters attribute would get held for approval. If the sender was on
|
|
201 |
# the posters list, then we'd defer judgement to a later step
|
|
202 |
# - If member_posting_only == yes, then members could post without holds,
|
|
203 |
# and if there were any addresses added to posters, they could also post
|
|
204 |
# without holds.
|
|
205 |
# - If member_posting_only == no, then what happens depends on the value
|
|
206 |
# of the posters attribute:
|
|
207 |
# o If posters was empty, then anybody can post without their
|
|
208 |
# message being held for approval
|
|
209 |
# o If posters was non-empty, then /only/ those addresses could post
|
|
210 |
# without approval, i.e. members not on posters would have their
|
|
211 |
# messages held for approval.
|
|
212 |
#
|
|
213 |
# How to translate this mess to MM2.1 values? I'm sure I got this wrong
|
|
214 |
# before, but here's how we're going to do it, as of MM2.1b3.
|
|
215 |
#
|
|
216 |
# - We'll control member moderation through their Moderate flag, and
|
|
217 |
# non-member moderation through the generic_nonmember_action,
|
|
218 |
# hold_these_nonmembers, and accept_these_nonmembers.
|
|
219 |
# - If moderated == yes then we need to troll through the addresses on
|
|
220 |
# posters, and any non-members would get added to
|
|
221 |
# accept_these_nonmembers. /Then/ we need to troll through the
|
|
222 |
# membership and any member on posters would get their Moderate flag
|
|
223 |
# unset, while members not on posters would get their Moderate flag set.
|
|
224 |
# Then generic_nonmember_action gets set to 1 (hold) so nonmembers get
|
|
225 |
# moderated, and default_member_moderation will be set to 1 (hold) so
|
|
226 |
# new members will also get held for moderation. We'll stop here.
|
|
227 |
# - We only get to here if moderated == no.
|
|
228 |
# - If member_posting_only == yes, then we'll turn off the Moderate flag
|
|
229 |
# for members. We troll through the posters attribute and add all those
|
|
230 |
# addresses to accept_these_nonmembers. We'll also set
|
|
231 |
# generic_nonmember_action to 1 and default_member_moderation to 0.
|
|
232 |
# We'll stop here.
|
|
233 |
# - We only get to here if member_posting_only == no
|
|
234 |
# - If posters is empty, then anybody could post without being held for
|
|
235 |
# approval, so we'll set generic_nonmember_action to 0 (accept), and
|
|
236 |
# we'll turn off the Moderate flag for all members. We'll also turn off
|
|
237 |
# default_member_moderation so new members can post without approval.
|
|
238 |
# We'll stop here.
|
|
239 |
# - We only get here if posters is non-empty.
|
|
240 |
# - This means that /only/ the addresses on posters got to post without
|
|
241 |
# being held for approval. So first, we troll through posters and add
|
|
242 |
# all non-members to accept_these_nonmembers. Then we troll through the
|
|
243 |
# membership and if their address is on posters, we'll clear their
|
|
244 |
# Moderate flag, otherwise we'll set it. We'll turn on
|
|
245 |
# default_member_moderation so new members get moderated. We'll set
|
|
246 |
# generic_nonmember_action to 1 (hold) so all other non-members will get
|
|
247 |
# moderated. And I think we're finally done.
|
|
248 |
#
|
|
249 |
# SIGH.
|
|
250 |
if hasattr(l, 'moderated'): |
|
251 |
# We'll assume we're converting all these attributes at once
|
|
252 |
if l.moderated: |
|
253 |
#syslog('debug', 'Case 1')
|
|
254 |
for addr in l.posters: |
|
255 |
if not l.isMember(addr): |
|
256 |
l.accept_these_nonmembers.append(addr) |
|
257 |
for member in l.getMembers(): |
|
258 |
l.setMemberOption(member, mm_cfg.Moderate, |
|
259 |
# reset for explicitly named members
|
|
260 |
member not in l.posters) |
|
261 |
l.generic_nonmember_action = 1 |
|
262 |
l.default_member_moderation = 1 |
|
263 |
elif l.member_posting_only: |
|
264 |
#syslog('debug', 'Case 2')
|
|
265 |
for addr in l.posters: |
|
266 |
if not l.isMember(addr): |
|
267 |
l.accept_these_nonmembers.append(addr) |
|
268 |
for member in l.getMembers(): |
|
269 |
l.setMemberOption(member, mm_cfg.Moderate, 0) |
|
270 |
l.generic_nonmember_action = 1 |
|
271 |
l.default_member_moderation = 0 |
|
272 |
elif not l.posters: |
|
273 |
#syslog('debug', 'Case 3')
|
|
274 |
for member in l.getMembers(): |
|
275 |
l.setMemberOption(member, mm_cfg.Moderate, 0) |
|
276 |
l.generic_nonmember_action = 0 |
|
277 |
l.default_member_moderation = 0 |
|
278 |
else: |
|
279 |
#syslog('debug', 'Case 4')
|
|
280 |
for addr in l.posters: |
|
281 |
if not l.isMember(addr): |
|
282 |
l.accept_these_nonmembers.append(addr) |
|
283 |
for member in l.getMembers(): |
|
284 |
l.setMemberOption(member, mm_cfg.Moderate, |
|
285 |
# reset for explicitly named members
|
|
286 |
member not in l.posters) |
|
287 |
l.generic_nonmember_action = 1 |
|
288 |
l.default_member_moderation = 1 |
|
289 |
# Now get rid of the old attributes
|
|
290 |
del l.moderated |
|
291 |
del l.posters |
|
292 |
del l.member_posting_only |
|
293 |
if hasattr(l, 'forbidden_posters'): |
|
294 |
# For each of the posters on this list, if they are members, toggle on
|
|
295 |
# their moderation flag. If they are not members, then add them to
|
|
296 |
# hold_these_nonmembers.
|
|
297 |
forbiddens = l.forbidden_posters |
|
298 |
for addr in forbiddens: |
|
299 |
if l.isMember(addr): |
|
300 |
l.setMemberOption(addr, mm_cfg.Moderate, 1) |
|
301 |
else: |
|
302 |
l.hold_these_nonmembers.append(addr) |
|
303 |
del l.forbidden_posters |
|
304 |
||
305 |
# Migrate to 1.0b6, klm 10/22/1998:
|
|
306 |
PreferStored('reminders_to_admins', 'umbrella_list', |
|
307 |
mm_cfg.DEFAULT_UMBRELLA_LIST) |
|
308 |
||
309 |
# Migrate up to 1.0b5:
|
|
310 |
PreferStored('auto_subscribe', 'open_subscribe') |
|
311 |
PreferStored('closed', 'private_roster') |
|
312 |
PreferStored('mimimum_post_count_before_removal', |
|
313 |
'mimimum_post_count_before_bounce_action') |
|
314 |
PreferStored('bad_posters', 'forbidden_posters') |
|
315 |
PreferStored('automatically_remove', 'automatic_bounce_action') |
|
316 |
if hasattr(l, "open_subscribe"): |
|
317 |
if l.open_subscribe: |
|
318 |
if mm_cfg.ALLOW_OPEN_SUBSCRIBE: |
|
319 |
l.subscribe_policy = 0 |
|
320 |
else: |
|
321 |
l.subscribe_policy = 1 |
|
322 |
else: |
|
323 |
l.subscribe_policy = 2 # admin approval |
|
324 |
delattr(l, "open_subscribe") |
|
325 |
if not hasattr(l, "administrivia"): |
|
326 |
setattr(l, "administrivia", mm_cfg.DEFAULT_ADMINISTRIVIA) |
|
327 |
if not hasattr(l, "admin_member_chunksize"): |
|
328 |
setattr(l, "admin_member_chunksize", |
|
329 |
mm_cfg.DEFAULT_ADMIN_MEMBER_CHUNKSIZE) |
|
330 |
#
|
|
331 |
# this attribute was added then deleted, so there are a number of
|
|
332 |
# cases to take care of
|
|
333 |
#
|
|
334 |
if hasattr(l, "posters_includes_members"): |
|
335 |
if l.posters_includes_members: |
|
336 |
if l.posters: |
|
337 |
l.member_posting_only = 1 |
|
338 |
else: |
|
339 |
if l.posters: |
|
340 |
l.member_posting_only = 0 |
|
341 |
delattr(l, "posters_includes_members") |
|
342 |
elif l.data_version <= 10 and l.posters: |
|
343 |
# make sure everyone gets the behavior the list used to have, but only
|
|
344 |
# for really old versions of Mailman (1.0b5 or before). Any newer
|
|
345 |
# version of Mailman should not get this attribute whacked.
|
|
346 |
l.member_posting_only = 0 |
|
347 |
#
|
|
348 |
# transfer the list data type for holding members and digest members
|
|
349 |
# to the dict data type starting file format version 11
|
|
350 |
#
|
|
351 |
if type(l.members) is ListType: |
|
352 |
members = {} |
|
353 |
for m in l.members: |
|
354 |
members[m] = 1 |
|
355 |
l.members = members |
|
356 |
if type(l.digest_members) is ListType: |
|
357 |
dmembers = {} |
|
358 |
for dm in l.digest_members: |
|
359 |
dmembers[dm] = 1 |
|
360 |
l.digest_members = dmembers |
|
361 |
#
|
|
362 |
# set admin_notify_mchanges
|
|
363 |
#
|
|
364 |
if not hasattr(l, "admin_notify_mchanges"): |
|
365 |
setattr(l, "admin_notify_mchanges", |
|
366 |
mm_cfg.DEFAULT_ADMIN_NOTIFY_MCHANGES) |
|
367 |
#
|
|
368 |
# Convert the members and digest_members addresses so that the keys of
|
|
369 |
# both these are always lowercased, but if there is a case difference, the
|
|
370 |
# value contains the case preserved value
|
|
371 |
#
|
|
372 |
for k in l.members.keys(): |
|
373 |
if k.lower() <> k: |
|
374 |
l.members[k.lower()] = Utils.LCDomain(k) |
|
375 |
del l.members[k] |
|
376 |
elif type(l.members[k]) == StringType and k == l.members[k].lower(): |
|
377 |
# already converted
|
|
378 |
pass
|
|
379 |
else: |
|
380 |
l.members[k] = 0 |
|
381 |
for k in l.digest_members.keys(): |
|
382 |
if k.lower() <> k: |
|
383 |
l.digest_members[k.lower()] = Utils.LCDomain(k) |
|
384 |
del l.digest_members[k] |
|
385 |
elif type(l.digest_members[k]) == StringType and \ |
|
386 |
k == l.digest_members[k].lower(): |
|
387 |
# already converted
|
|
388 |
pass
|
|
389 |
else: |
|
390 |
l.digest_members[k] = 0 |
|
1516
by Mark Sapiro
A number of changes from the unofficial 2.2 branch have been backported to |
391 |
#
|
392 |
# Convert pre 2.2 topics regexps which were compiled in verbose mode
|
|
393 |
# to a non-verbose equivalent.
|
|
394 |
#
|
|
395 |
if stored_state['data_version'] < 106 and stored_state.has_key('topics'): |
|
396 |
l.topics = [] |
|
397 |
for name, pattern, description, emptyflag in stored_state['topics']: |
|
398 |
pattern = Utils.strip_verbose_pattern(pattern) |
|
399 |
l.topics.append((name, pattern, description, emptyflag)) |
|
1536
by Mark Sapiro
Added character set recoding to utf-8 for list attributes for Romanian and |
400 |
#
|
401 |
# Romanian and Russian had their character sets changed in 2.1.19
|
|
402 |
# to utf-8. If there are any strings in the old encoding, try to recode
|
|
403 |
# them.
|
|
404 |
#
|
|
405 |
if stored_state['data_version'] < 108: |
|
406 |
if l.preferred_language == 'ro': |
|
407 |
if Utils.GetCharSet('ro') == 'utf-8': |
|
408 |
recode(l, 'iso-8859-2', 'utf-8') |
|
409 |
if l.preferred_language == 'ru': |
|
410 |
if Utils.GetCharSet('ru') == 'utf-8': |
|
411 |
recode(l, 'koi8-r', 'utf-8') |
|
412 |
#
|
|
1421
by Mark Sapiro
Renamed author_is_list to from_is_list. |
413 |
# from_is_list was called author_is_list in 2.1.16rc2 (only).
|
414 |
PreferStored('author_is_list', 'from_is_list', |
|
415 |
mm_cfg.DEFAULT_FROM_IS_LIST) |
|
1
by
This commit was manufactured by cvs2svn to create branch |
416 |
|
417 |
||
418 |
||
419 |
def NewVars(l): |
|
420 |
"""Add defaults for these new variables if they don't exist."""
|
|
421 |
def add_only_if_missing(attr, initval, l=l): |
|
422 |
if not hasattr(l, attr): |
|
423 |
setattr(l, attr, initval) |
|
424 |
# 1.2 beta 1, baw 18-Feb-2000
|
|
425 |
# Autoresponder mixin class attributes
|
|
426 |
add_only_if_missing('autorespond_postings', 0) |
|
427 |
add_only_if_missing('autorespond_admin', 0) |
|
428 |
add_only_if_missing('autorespond_requests', 0) |
|
429 |
add_only_if_missing('autoresponse_postings_text', '') |
|
430 |
add_only_if_missing('autoresponse_admin_text', '') |
|
431 |
add_only_if_missing('autoresponse_request_text', '') |
|
432 |
add_only_if_missing('autoresponse_graceperiod', 90) |
|
433 |
add_only_if_missing('postings_responses', {}) |
|
434 |
add_only_if_missing('admin_responses', {}) |
|
435 |
add_only_if_missing('reply_goes_to_list', '') |
|
436 |
add_only_if_missing('preferred_language', mm_cfg.DEFAULT_SERVER_LANGUAGE) |
|
437 |
add_only_if_missing('available_languages', []) |
|
438 |
add_only_if_missing('digest_volume_frequency', |
|
439 |
mm_cfg.DEFAULT_DIGEST_VOLUME_FREQUENCY) |
|
440 |
add_only_if_missing('digest_last_sent_at', 0) |
|
441 |
add_only_if_missing('mod_password', None) |
|
1297
by Mark Sapiro
A new list poster password has been implemented. This password may only |
442 |
add_only_if_missing('post_password', None) |
1
by
This commit was manufactured by cvs2svn to create branch |
443 |
add_only_if_missing('moderator', []) |
444 |
add_only_if_missing('topics', []) |
|
445 |
add_only_if_missing('topics_enabled', 0) |
|
446 |
add_only_if_missing('topics_bodylines_limit', 5) |
|
447 |
add_only_if_missing('one_last_digest', {}) |
|
448 |
add_only_if_missing('usernames', {}) |
|
449 |
add_only_if_missing('personalize', 0) |
|
450 |
add_only_if_missing('first_strip_reply_to', |
|
451 |
mm_cfg.DEFAULT_FIRST_STRIP_REPLY_TO) |
|
1516
by Mark Sapiro
A number of changes from the unofficial 2.2 branch have been backported to |
452 |
add_only_if_missing('subscribe_auto_approval', |
453 |
mm_cfg.DEFAULT_SUBSCRIBE_AUTO_APPROVAL) |
|
1
by
This commit was manufactured by cvs2svn to create branch |
454 |
add_only_if_missing('unsubscribe_policy', |
455 |
mm_cfg.DEFAULT_UNSUBSCRIBE_POLICY) |
|
456 |
add_only_if_missing('send_goodbye_msg', mm_cfg.DEFAULT_SEND_GOODBYE_MSG) |
|
457 |
add_only_if_missing('include_rfc2369_headers', 1) |
|
458 |
add_only_if_missing('include_list_post_header', 1) |
|
1242.1.1
by Malte S. Stretz
Added option include_sender_header to suppress rewrite of the Sender header which confuses Outlook (formerly known as FAQ 2.3). See also <http://mail.python.org/pipermail/mailman-developers/2006-July/019040.html>. Bug #266824. |
459 |
add_only_if_missing('include_sender_header', 1) |
1
by
This commit was manufactured by cvs2svn to create branch |
460 |
add_only_if_missing('bounce_score_threshold', |
461 |
mm_cfg.DEFAULT_BOUNCE_SCORE_THRESHOLD) |
|
462 |
add_only_if_missing('bounce_info_stale_after', |
|
463 |
mm_cfg.DEFAULT_BOUNCE_INFO_STALE_AFTER) |
|
464 |
add_only_if_missing('bounce_you_are_disabled_warnings', |
|
465 |
mm_cfg.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS) |
|
466 |
add_only_if_missing( |
|
467 |
'bounce_you_are_disabled_warnings_interval', |
|
468 |
mm_cfg.DEFAULT_BOUNCE_YOU_ARE_DISABLED_WARNINGS_INTERVAL) |
|
469 |
add_only_if_missing( |
|
470 |
'bounce_unrecognized_goes_to_list_owner', |
|
471 |
mm_cfg.DEFAULT_BOUNCE_UNRECOGNIZED_GOES_TO_LIST_OWNER) |
|
472 |
add_only_if_missing( |
|
1500
by Mark Sapiro
Implemented the ability to forward bounces to the list owner. |
473 |
'bounce_notify_owner_on_bounce_increment', |
474 |
mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_BOUNCE_INCREMENT) |
|
475 |
add_only_if_missing( |
|
1
by
This commit was manufactured by cvs2svn to create branch |
476 |
'bounce_notify_owner_on_disable', |
477 |
mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_DISABLE) |
|
478 |
add_only_if_missing( |
|
479 |
'bounce_notify_owner_on_removal', |
|
480 |
mm_cfg.DEFAULT_BOUNCE_NOTIFY_OWNER_ON_REMOVAL) |
|
481 |
add_only_if_missing('ban_list', []) |
|
482 |
add_only_if_missing('filter_mime_types', mm_cfg.DEFAULT_FILTER_MIME_TYPES) |
|
483 |
add_only_if_missing('pass_mime_types', mm_cfg.DEFAULT_PASS_MIME_TYPES) |
|
484 |
add_only_if_missing('filter_content', mm_cfg.DEFAULT_FILTER_CONTENT) |
|
485 |
add_only_if_missing('convert_html_to_plaintext', |
|
486 |
mm_cfg.DEFAULT_CONVERT_HTML_TO_PLAINTEXT) |
|
487 |
add_only_if_missing('filter_action', mm_cfg.DEFAULT_FILTER_ACTION) |
|
488 |
add_only_if_missing('delivery_status', {}) |
|
489 |
# This really ought to default to mm_cfg.HOLD, but that doesn't work with
|
|
490 |
# the current GUI description model. So, 0==Hold, 1==Reject, 2==Discard
|
|
491 |
add_only_if_missing('member_moderation_action', 0) |
|
492 |
add_only_if_missing('member_moderation_notice', '') |
|
1374.1.4
by Jim Popovitch
Incorporated some feedback from Mark S. |
493 |
add_only_if_missing('dmarc_moderation_action', |
1479
by Mark Sapiro
Make dmarc_quarantine_moderaction_action a list setting. |
494 |
mm_cfg.DEFAULT_DMARC_MODERATION_ACTION) |
495 |
add_only_if_missing('dmarc_quarantine_moderation_action', |
|
496 |
mm_cfg.DEFAULT_DMARC_QUARANTINE_MODERATION_ACTION) |
|
1600
by Mark Sapiro
Added dmarc_non_moderation_action to list settings. |
497 |
add_only_if_missing('dmarc_none_moderation_action', |
498 |
mm_cfg.DEFAULT_DMARC_NONE_MODERATION_ACTION) |
|
1374.1.1
by Jim Popovitch
Hold/Reject/Discard moderation support for Senders with a DMARC p=reject policy |
499 |
add_only_if_missing('dmarc_moderation_notice', '') |
1519
by Mark Sapiro
Implemented dmarc_wrapped_message_text to optionally add an explanitory |
500 |
add_only_if_missing('dmarc_wrapped_message_text', |
501 |
mm_cfg.DEFAULT_DMARC_WRAPPED_MESSAGE_TEXT) |
|
1585.1.3
by jimpop at template
Improvements based on feedback from Mark Sapiro |
502 |
add_only_if_missing('member_verbosity_threshold', |
503 |
mm_cfg.DEFAULT_MEMBER_VERBOSITY_THRESHOLD) |
|
1585.1.1
by jimpop at template
Auto-Moderate Verbose Members |
504 |
add_only_if_missing('member_verbosity_interval', |
505 |
mm_cfg.DEFAULT_MEMBER_VERBOSITY_INTERVAL) |
|
1518
by Mark Sapiro
Implemented the equivalent domains feature for list posting/moderation. |
506 |
add_only_if_missing('equivalent_domains', |
507 |
mm_cfg.DEFAULT_EQUIVALENT_DOMAINS) |
|
1
by
This commit was manufactured by cvs2svn to create branch |
508 |
add_only_if_missing('new_member_options', |
509 |
mm_cfg.DEFAULT_NEW_MEMBER_OPTIONS) |
|
510 |
# Emergency moderation flag
|
|
511 |
add_only_if_missing('emergency', 0) |
|
512 |
add_only_if_missing('hold_and_cmd_autoresponses', {}) |
|
513 |
add_only_if_missing('news_prefix_subject_too', 1) |
|
514 |
# Should prefixes be encoded?
|
|
515 |
if Utils.GetCharSet(l.preferred_language) == 'us-ascii': |
|
516 |
encode = 0 |
|
517 |
else: |
|
518 |
encode = 2 |
|
519 |
add_only_if_missing('encode_ascii_prefixes', encode) |
|
520 |
add_only_if_missing('news_moderation', 0) |
|
202
by bwarsaw
NewVars(): Add header_filter_rules if missing. |
521 |
add_only_if_missing('header_filter_rules', []) |
463
by tkikuchi
Merging SF patches: |
522 |
# Scrubber in regular delivery
|
523 |
add_only_if_missing('scrub_nondigest', 0) |
|
524 |
# ContentFilter by file extensions
|
|
525 |
add_only_if_missing('filter_filename_extensions', |
|
743
by bwarsaw
Whitespace normalization, and updates of copyright years. |
526 |
mm_cfg.DEFAULT_FILTER_FILENAME_EXTENSIONS) |
463
by tkikuchi
Merging SF patches: |
527 |
add_only_if_missing('pass_filename_extensions', []) |
468
by tkikuchi
[ 790494 ] built-in automatic discard |
528 |
# automatic discard
|
529 |
add_only_if_missing('max_days_to_hold', 0) |
|
608
by tkikuchi
sourceforge patch id=1107169, re-use member_moderation_notice ... |
530 |
add_only_if_missing('nonmember_rejection_notice', '') |
737
by tkikuchi
Introduce new attribute (collapse_alternatives) to allow HTML in |
531 |
# multipart/alternative collapse
|
743
by bwarsaw
Whitespace normalization, and updates of copyright years. |
532 |
add_only_if_missing('collapse_alternatives', |
533 |
mm_cfg.DEFAULT_COLLAPSE_ALTERNATIVES) |
|
1020.1.1
by Tokio Kikuchi
Add 'sibling list' feature: exclude and include lists are other mailing |
534 |
# exclude/include lists
|
535 |
add_only_if_missing('regular_exclude_lists', |
|
536 |
mm_cfg.DEFAULT_REGULAR_EXCLUDE_LISTS) |
|
537 |
add_only_if_missing('regular_include_lists', |
|
538 |
mm_cfg.DEFAULT_REGULAR_INCLUDE_LISTS) |
|
1345
by Mark Sapiro
Backported regular_exclude_ignore list attribute feature from 2.2 branch. |
539 |
add_only_if_missing('regular_exclude_ignore', |
540 |
mm_cfg.DEFAULT_REGULAR_EXCLUDE_IGNORE) |
|
1
by
This commit was manufactured by cvs2svn to create branch |
541 |
|
542 |
||
543 |
||
544 |
def UpdateOldUsers(mlist): |
|
545 |
"""Transform sense of changed user options."""
|
|
546 |
# pre-1.0b11 to 1.0b11. Force all keys in l.passwords to be lowercase
|
|
547 |
passwords = {} |
|
548 |
for k, v in mlist.passwords.items(): |
|
549 |
passwords[k.lower()] = v |
|
550 |
mlist.passwords = passwords |
|
551 |
# Go through all the keys in bounce_info. If the key is not a member, or
|
|
552 |
# if the data is not a _BounceInfo instance, chuck the bounce info. We're
|
|
553 |
# doing things differently now.
|
|
554 |
for m in mlist.bounce_info.keys(): |
|
555 |
if not mlist.isMember(m) or not isinstance(mlist.getBounceInfo(m), |
|
556 |
_BounceInfo): |
|
557 |
del mlist.bounce_info[m] |
|
558 |
||
559 |
||
560 |
||
561 |
def CanonicalizeUserOptions(l): |
|
562 |
"""Fix up the user options."""
|
|
563 |
# I want to put a flag in the list database which tells this routine to
|
|
564 |
# never try to canonicalize the user options again.
|
|
565 |
if getattr(l, 'useropts_version', 0) > 0: |
|
566 |
return
|
|
567 |
# pre 1.0rc2 to 1.0rc3. For all keys in l.user_options to be lowercase,
|
|
568 |
# but merge options for both cases
|
|
569 |
options = {} |
|
570 |
for k, v in l.user_options.items(): |
|
571 |
if k is None: |
|
572 |
continue
|
|
573 |
lcuser = k.lower() |
|
574 |
flags = 0 |
|
575 |
if options.has_key(lcuser): |
|
576 |
flags = options[lcuser] |
|
577 |
flags |= v |
|
578 |
options[lcuser] = flags |
|
579 |
l.user_options = options |
|
580 |
# 2.1alpha3 -> 2.1alpha4. The DisableDelivery flag is now moved into
|
|
581 |
# get/setDeilveryStatus(). This must be done after the addresses are
|
|
582 |
# canonicalized.
|
|
583 |
for k, v in l.user_options.items(): |
|
584 |
if not l.isMember(k): |
|
585 |
# There's a key in user_options that isn't associated with a real
|
|
586 |
# member address. This is likely caused by an earlier bug.
|
|
587 |
del l.user_options[k] |
|
588 |
continue
|
|
589 |
if l.getMemberOption(k, mm_cfg.DisableDelivery): |
|
590 |
# Convert this flag into a legacy disable
|
|
591 |
l.setDeliveryStatus(k, UNKNOWN) |
|
592 |
l.setMemberOption(k, mm_cfg.DisableDelivery, 0) |
|
593 |
l.useropts_version = 1 |
|
594 |
||
595 |
||
596 |
||
597 |
def NewRequestsDatabase(l): |
|
598 |
"""With version 1.2, we use a new pending request database schema."""
|
|
599 |
r = getattr(l, 'requests', {}) |
|
600 |
if not r: |
|
601 |
# no old-style requests
|
|
602 |
return
|
|
603 |
for k, v in r.items(): |
|
604 |
if k == 'post': |
|
605 |
# This is a list of tuples with the following format
|
|
606 |
#
|
|
607 |
# a sequential request id integer
|
|
608 |
# a timestamp float
|
|
609 |
# a message tuple: (author-email-str, message-text-str)
|
|
610 |
# a reason string
|
|
611 |
# the subject string
|
|
612 |
#
|
|
613 |
# We'll re-submit this as a new HoldMessage request, but we'll
|
|
614 |
# blow away the original timestamp and request id. This means the
|
|
615 |
# request will live a little longer than it possibly should have,
|
|
616 |
# but that's no big deal.
|
|
617 |
for p in v: |
|
618 |
author, text = p[2] |
|
619 |
reason = p[3] |
|
992
by Mark Sapiro
/cygdrive/c/MM_bzr/log.txt |
620 |
msg = email.message_from_string(text, Message.Message) |
1
by
This commit was manufactured by cvs2svn to create branch |
621 |
l.HoldMessage(msg, reason) |
622 |
del r[k] |
|
623 |
elif k == 'add_member': |
|
624 |
# This is a list of tuples with the following format
|
|
625 |
#
|
|
626 |
# a sequential request id integer
|
|
627 |
# a timestamp float
|
|
628 |
# a digest flag (0 == nodigest, 1 == digest)
|
|
629 |
# author-email-str
|
|
630 |
# password
|
|
631 |
#
|
|
632 |
# See the note above; the same holds true.
|
|
633 |
for ign, ign, digest, addr, password in v: |
|
167
by bwarsaw
NewRequestsDatabase(): HoldSubscription() now takes 5 arguments; the |
634 |
l.HoldSubscription(addr, '', password, digest, |
635 |
mm_cfg.DEFAULT_SERVER_LANGUAGE) |
|
1
by
This commit was manufactured by cvs2svn to create branch |
636 |
del r[k] |
637 |
else: |
|
638 |
syslog('error', """\ |
|
639 |
VERY BAD NEWS. Unknown pending request type `%s' found for list: %s""", |
|
640 |
k, l.internal_name()) |