1 ==========================
2 Mailing list configuration
3 ==========================
5 Mailing lists can be configured via the REST API.
7 >>> from mailman.app.lifecycle import create_list
8 >>> mlist = create_list('ant@example.com')
9 >>> from mailman.config import config
10 >>> transaction = config.db
11 >>> transaction.commit()
14 Reading a configuration
15 =======================
17 All readable attributes for a list are available on a sub-resource.
19 >>> from mailman.testing.documentation import dump_json
20 >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/config')
21 accept_these_nonmembers: []
22 acceptable_aliases: []
23 admin_immed_notify: True
24 admin_notify_mchanges: False
27 allow_list_posts: True
29 archive_policy: public
30 archive_rendering_mode: text
31 autorespond_owner: none
32 autorespond_postings: none
33 autorespond_requests: none
34 autoresponse_grace_period: 90d
35 autoresponse_owner_text:
36 autoresponse_postings_text:
37 autoresponse_request_text:
38 bounce_info_stale_after: 7d
39 bounce_notify_owner_on_bounce_increment: False
40 bounce_notify_owner_on_disable: True
41 bounce_notify_owner_on_removal: True
42 bounce_score_threshold: 5
43 bounce_you_are_disabled_warnings: 3
44 bounce_you_are_disabled_warnings_interval: 7d
45 bounces_address: ant-bounces@example.com
46 collapse_alternatives: True
47 convert_html_to_plaintext: False
49 default_member_action: defer
50 default_nonmember_action: hold
54 digest_last_sent_at: None
55 digest_send_periodic: True
56 digest_size_threshold: 30.0
57 digest_volume_frequency: monthly
59 discard_these_nonmembers: []
62 dmarc_mitigate_action: no_mitigation
63 dmarc_mitigate_unconditionally: False
64 dmarc_moderation_notice:
65 dmarc_wrapped_message_text:
67 filter_action: discard
71 first_strip_reply_to: False
73 forward_unrecognized_bounces_to: administrators
74 fqdn_listname: ant@example.com
75 gateway_to_mail: False
76 gateway_to_news: False
79 hold_these_nonmembers: []
81 include_rfc2369_headers: True
83 join_address: ant-join@example.com
85 leave_address: ant-leave@example.com
88 mail_host: example.com
91 max_num_recipients: 10
92 member_roster_visibility: moderators
93 moderator_password: None
94 newsgroup_moderation: none
96 nntp_prefix_subject_too: True
97 no_reply_address: noreply@example.com
98 owner_address: ant-owner@example.com
103 posting_address: ant@example.com
104 posting_pipeline: default-posting-pipeline
105 preferred_language: en
106 process_bounces: True
107 reject_these_nonmembers: []
108 reply_goes_to_list: no_munging
110 request_address: ant-request@example.com
111 require_explicit_destination: True
112 respond_to_post_requests: True
113 send_goodbye_message: True
114 send_welcome_message: True
115 subject_prefix: [Ant]
116 subscription_policy: confirm
117 unsubscription_policy: confirm
118 usenet_watermark: None
123 Changing the full configuration
124 ===============================
126 Not all of the readable attributes can be set through the web interface. The
127 ones that can, can either be set via ``PUT`` or ``PATCH``. ``PUT`` changes
128 all the writable attributes in one request.
130 When using ``PUT``, all writable attributes must be included.
132 >>> dump_json('http://localhost:9001/3.0/lists/'
133 ... 'ant@example.com/config',
135 ... acceptable_aliases=['one@example.com', 'two@example.com'],
136 ... accept_these_nonmembers=['aperson@example.com'],
137 ... admin_immed_notify=False,
138 ... admin_notify_mchanges=True,
139 ... administrivia=False,
140 ... advertised=False,
141 ... anonymous_list=True,
142 ... archive_policy='never',
143 ... archive_rendering_mode='text',
144 ... autorespond_owner='respond_and_discard',
145 ... autorespond_postings='respond_and_continue',
146 ... autorespond_requests='respond_and_discard',
147 ... autoresponse_grace_period='45d',
148 ... autoresponse_owner_text='the owner',
149 ... autoresponse_postings_text='the mailing list',
150 ... autoresponse_request_text='the robot',
151 ... bounce_info_stale_after='5d',
152 ... bounce_notify_owner_on_bounce_increment=False,
153 ... bounce_notify_owner_on_disable=True,
154 ... bounce_notify_owner_on_removal=True,
155 ... bounce_score_threshold=5,
156 ... bounce_you_are_disabled_warnings=3,
157 ... bounce_you_are_disabled_warnings_interval='1d',
158 ... forward_unrecognized_bounces_to='administrators',
159 ... filter_extensions=['.mkv'],
160 ... filter_types=['application/zip'],
161 ... process_bounces=True,
162 ... discard_these_nonmembers=[r'^name_*bperson*@example.com'],
163 ... display_name='Fnords',
164 ... description='This is my mailing list',
165 ... include_rfc2369_headers=False,
166 ... info='This is the mailing list information',
167 ... allow_list_posts=False,
168 ... digest_send_periodic=False,
169 ... digest_size_threshold=10.5,
170 ... digest_volume_frequency='yearly',
171 ... digests_enabled=False,
172 ... dmarc_addresses=['^.*@example.com'],
173 ... dmarc_mitigate_action='munge_from',
174 ... dmarc_mitigate_unconditionally=False,
175 ... dmarc_moderation_notice='Some moderation notice',
176 ... dmarc_wrapped_message_text='some message text',
177 ... personalize='none',
178 ... preferred_language='ja',
179 ... posting_pipeline='virgin',
180 ... filter_content=True,
181 ... first_strip_reply_to=True,
182 ... gateway_to_mail=True,
183 ... gateway_to_news=True,
184 ... linked_newsgroup='my.group',
185 ... newsgroup_moderation='moderated',
186 ... nntp_prefix_subject_too=False,
187 ... convert_html_to_plaintext=True,
188 ... collapse_alternatives=False,
189 ... reject_these_nonmembers=[r'^b[hello]*@example.com'],
190 ... hold_these_nonmembers=[r'^re[gG]ex@example.com'],
191 ... reply_goes_to_list='point_to_list',
192 ... reply_to_address='bee@example.com',
193 ... require_explicit_destination=False,
194 ... member_roster_visibility='members',
195 ... send_goodbye_message=False,
196 ... send_welcome_message=False,
197 ... subject_prefix='[ant]',
198 ... subscription_policy='moderate',
199 ... unsubscription_policy='confirm',
200 ... default_member_action='hold',
201 ... default_nonmember_action='discard',
202 ... moderator_password='password',
203 ... max_message_size='500',
204 ... respond_to_post_requests=True,
205 ... max_days_to_hold='20',
206 ... max_num_recipients='20',
207 ... pass_extensions=['.pdf'],
208 ... pass_types=['image/jpeg'],
209 ... filter_action='preserve',
217 These values are changed permanently.
219 >>> dump_json('http://localhost:9001/3.0/lists/'
220 ... 'ant@example.com/config')
221 accept_these_nonmembers: ['aperson@example.com']
222 acceptable_aliases: ['one@example.com', 'two@example.com']
223 admin_immed_notify: False
224 admin_notify_mchanges: True
227 allow_list_posts: False
229 archive_policy: never
230 archive_rendering_mode: text
231 autorespond_owner: respond_and_discard
232 autorespond_postings: respond_and_continue
233 autorespond_requests: respond_and_discard
234 autoresponse_grace_period: 45d
235 autoresponse_owner_text: the owner
236 autoresponse_postings_text: the mailing list
237 autoresponse_request_text: the robot
238 bounce_info_stale_after: 5d
239 bounce_notify_owner_on_bounce_increment: False
240 bounce_notify_owner_on_disable: True
241 bounce_notify_owner_on_removal: True
242 bounce_score_threshold: 5
243 bounce_you_are_disabled_warnings: 3
244 bounce_you_are_disabled_warnings_interval: 1d
246 collapse_alternatives: False
247 convert_html_to_plaintext: True
249 default_member_action: hold
250 default_nonmember_action: discard
251 description: This is my mailing list
253 digest_send_periodic: False
254 digest_size_threshold: 10.5
255 digest_volume_frequency: yearly
256 digests_enabled: False
257 discard_these_nonmembers: ['^name_*bperson*@example.com']
259 dmarc_addresses: ['^.*@example.com']
260 dmarc_mitigate_action: munge_from
261 dmarc_mitigate_unconditionally: False
262 dmarc_moderation_notice: Some moderation notice
263 dmarc_wrapped_message_text: some message text
265 filter_action: preserve
267 filter_extensions: ['.mkv']
268 filter_types: ['application/zip']
269 first_strip_reply_to: True
271 forward_unrecognized_bounces_to: administrators
272 fqdn_listname: ant@example.com
273 gateway_to_mail: True
274 gateway_to_news: True
276 hold_these_nonmembers: ['^re[gG]ex@example.com']
278 include_rfc2369_headers: False
280 member_roster_visibility: members
281 moderator_password: {plaintext}password
282 newsgroup_moderation: moderated
284 nntp_prefix_subject_too: False
286 pass_extensions: ['.pdf']
287 pass_types: ['image/jpeg']
289 posting_pipeline: virgin
290 preferred_language: ja
291 process_bounces: True
292 reject_these_nonmembers: ['^b[hello]*@example.com']
293 reply_goes_to_list: point_to_list
294 reply_to_address: bee@example.com
296 require_explicit_destination: False
297 respond_to_post_requests: True
298 send_goodbye_message: False
299 send_welcome_message: False
300 subject_prefix: [ant]
301 subscription_policy: moderate
302 unsubscription_policy: confirm
306 Changing a partial configuration
307 ================================
309 Using ``PATCH``, you can change just one attribute.
311 >>> dump_json('http://localhost:9001/3.0/lists/'
312 ... 'ant@example.com/config',
313 ... dict(display_name='My List'),
319 These values are changed permanently.
321 >>> print(mlist.display_name)
328 Mailing list configuration variables are actually available as sub-resources
329 on the mailing list. Their values can be retrieved and set through the
336 You can view the current value of the sub-resource.
338 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
339 ... '/config/display_name')
340 display_name: My List
343 The resource can be changed by PUTting to it. Note that the value still
344 requires a dictionary, and that dictionary must have a single key matching the
345 name of the resource.
348 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
349 ... '/config/display_name',
350 ... dict(display_name='Your List'),
356 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
357 ... '/config/display_name')
358 display_name: Your List
361 PATCH works the same way, with the same effect, so you can choose to use
364 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
365 ... '/config/display_name',
366 ... dict(display_name='Their List'),
372 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
373 ... '/config/display_name')
374 display_name: Their List
381 These are recipient aliases that can be used in the ``To:`` and ``CC:``
382 headers instead of the posting address. They can also be strings beginning
383 with ``^`` which are interpreted as regular expressions matched against
384 addresses in ``To:`` and ``CC:`` headers. They are often used in forwarded
385 emails. By default, a mailing list has no acceptable aliases.
387 >>> from mailman.interfaces.mailinglist import IAcceptableAliasSet
388 >>> IAcceptableAliasSet(mlist).clear()
389 >>> transaction.commit()
390 >>> dump_json('http://localhost:9001/3.0/lists/'
391 ... 'ant@example.com/config/acceptable_aliases')
392 acceptable_aliases: []
395 We can add a few by ``PUT``-ing them on the sub-resource. The keys in the
396 dictionary are ignored.
398 >>> dump_json('http://localhost:9001/3.0/lists/'
399 ... 'ant@example.com/config/acceptable_aliases',
400 ... dict(acceptable_aliases=['foo@example.com',
401 ... 'bar@example.net']),
407 You can get all the mailing list's acceptable aliases through the REST API.
409 >>> from mailman.testing.documentation import call_http
410 >>> response = call_http(
411 ... 'http://localhost:9001/3.0/lists/'
412 ... 'ant@example.com/config/acceptable_aliases')
413 >>> for alias in response['acceptable_aliases']:
418 The mailing list has its aliases set.
420 >>> from mailman.interfaces.mailinglist import IAcceptableAliasSet
421 >>> aliases = IAcceptableAliasSet(mlist)
422 >>> for alias in sorted(aliases.aliases):
427 The aliases can be removed by using ``DELETE``.
429 >>> response = call_http(
430 ... 'http://localhost:9001/3.0/lists/'
431 ... 'ant@example.com/config/acceptable_aliases',
437 Now the mailing list has no aliases.
439 >>> aliases = IAcceptableAliasSet(mlist)
440 >>> print(len(list(aliases.aliases)))
447 Mailman can do pattern based header matching during its normal rule
448 processing. Each mailing list can also be configured with a set of header
449 matching regular expression rules. These can be used to impose list-specific
450 header filtering with the same semantics as the global ``[antispam]`` section,
451 or to have a different action.
453 The list of header matches for a mailing list are returned on the
454 ``header-matches`` child of this list.
456 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
457 ... '/header-matches')
462 New header matches can be created by POSTing to the resource.
465 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
466 ... '/header-matches', {
467 ... 'header': 'X-Spam-Flag',
468 ... 'pattern': '^Yes',
472 location: .../3.0/lists/ant.example.com/header-matches/0
476 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
477 ... '/header-matches/0')
482 self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/0
484 To follow the global antispam action, the header match rule must not specify
485 an ``action`` key, which names the chain to jump to if the rule matches. If
486 the default antispam action is changed in the configuration file and Mailman
487 is restarted, those rules will get the new jump action. If a specific action
488 is desired, the ``action`` key must name a valid chain to jump to.
491 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
492 ... '/header-matches', {
493 ... 'header': 'X-Spam-Status',
494 ... 'pattern': '^Yes',
495 ... 'action': 'discard',
499 location: .../3.0/lists/ant.example.com/header-matches/1
503 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
504 ... '/header-matches/1')
506 header: x-spam-status
510 self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/1
512 The resource can be changed by PATCHing it. The ``position`` key can be used
513 to change the priority of the header match in the list. If it is not supplied,
514 the priority is not changed.
517 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
518 ... '/header-matches/1',
519 ... dict(pattern='^No', action='accept'),
524 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
525 ... '/header-matches/1')
527 header: x-spam-status
531 self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/1
533 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
534 ... '/header-matches/1',
535 ... dict(position=0),
540 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
541 ... '/header-matches')
544 header: x-spam-status
548 self_link: .../lists/ant.example.com/header-matches/0
554 self_link: .../lists/ant.example.com/header-matches/1
559 The PUT method can replace an entire header match. The ``position`` key is
560 optional; if it is omitted, the order will not be changed.
563 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
564 ... '/header-matches/1',
565 ... dict(header='X-Spam-Status',
573 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
574 ... '/header-matches/1')
576 header: x-spam-status
580 self_link: http://localhost:9001/3.0/lists/ant.example.com/header-matches/1
582 A header match can be removed using the DELETE method.
585 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
586 ... '/header-matches/1',
592 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
593 ... '/header-matches')
596 header: x-spam-status
600 self_link: .../lists/ant.example.com/header-matches/0
605 The mailing list's header matches can be cleared by issuing a DELETE request on
609 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
610 ... '/header-matches',
616 >>> dump_json('http://localhost:9001/3.0/lists/ant.example.com'
617 ... '/header-matches')