2 #***************************************************************************
3 #* Copyright (C) 2004 polytechnique.org *
4 #* http://opensource.polytechnique.org/ *
6 #* This program is free software; you can redistribute it and/or modify *
7 #* it under the terms of the GNU General Public License as published by *
8 #* the Free Software Foundation; either version 2 of the License, or *
9 #* (at your option) any later version. *
11 #* This program is distributed in the hope that it will be useful, *
12 #* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 #* GNU General Public License for more details. *
16 #* You should have received a copy of the GNU General Public License *
17 #* along with this program; if not, write to the Free Software *
18 #* Foundation, Inc., *
19 #* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 #***************************************************************************
22 import base64
, MySQLdb
, os
, getopt
, sys
, sha
, signal
, re
, shutil
, ConfigParser
23 import MySQLdb
.converters
26 sys
.path
.append('/usr/lib/mailman/bin')
28 from pwd
import getpwnam
29 from grp
import getgrnam
31 from SimpleXMLRPCServer
import SimpleXMLRPCServer
32 from SimpleXMLRPCServer
import SimpleXMLRPCRequestHandler
35 from Mailman
import MailList
36 from Mailman
import Utils
37 from Mailman
import Message
38 from Mailman
import Errors
39 from Mailman
import mm_cfg
40 from Mailman
import i18n
41 from Mailman
.UserDesc
import UserDesc
42 from Mailman
.ListAdmin
import readMessage
43 from email
.Iterators
import typed_subpart_iterator
44 from threading
import Lock
46 class AuthFailed(Exception): pass
48 ################################################################################
52 #------------------------------------------------
54 config
= ConfigParser
.ConfigParser()
55 config
.read(os
.path
.dirname(__file__
)+'/../configs/platal.ini')
56 config
.read(os
.path
.dirname(__file__
)+'/../configs/platal.conf')
58 def get_config(sec
, val
, default
=None):
60 return config
.get(sec
, val
)[1:-1]
61 except ConfigParser
.NoOptionError
, e
:
68 MYSQL_USER
= get_config('Core', 'dbuser')
69 MYSQL_PASS
= get_config('Core', 'dbpwd')
71 PLATAL_DOMAIN
= get_config('Mail', 'domain')
72 PLATAL_DOMAIN2
= get_config('Mail', 'domain2', '')
74 VHOST_SEP
= get_config('Lists', 'vhost_sep', '_')
75 ON_CREATE_CMD
= get_config('Lists', 'on_create', '')
77 ################################################################################
81 #------------------------------------------------
82 # Manage Basic authentication
85 class BasicAuthXMLRPCRequestHandler(SimpleXMLRPCRequestHandler
):
87 """XMLRPC Request Handler
88 This request handler is used to provide BASIC HTTP user authentication.
89 It first overloads the do_POST() function, authenticates the user, then
90 calls the super.do_POST().
92 Moreover, we override _dispatch, so that we call functions with as first
93 argument a UserDesc taken from the database, containing name, email and perms
96 def _dispatch(self
, method
, params
):
97 # TODO: subclass in SimpleXMLRPCDispatcher and not here.
98 new_params
= list(params
)
99 new_params
.insert(0, self
.data
[2])
100 new_params
.insert(0, self
.data
[1])
101 new_params
.insert(0, self
.data
[0])
102 return self
.server
._dispatch(method
, new_params
)
106 _
, auth
= self
.headers
["authorization"].split()
107 uid
, md5
= base64
.decodestring(auth
).strip().split(':')
108 vhost
= self
.path
.split('/')[1].lower()
109 self
.data
= self
.getUser(uid
, md5
, vhost
)
110 if self
.data
is None:
112 # Call super.do_POST() to do the actual work
113 SimpleXMLRPCRequestHandler
.do_POST(self
)
115 self
.send_response(401)
118 def getUser(self
, uid
, md5
, vhost
):
119 res
= mysql_fetchone ("""SELECT CONCAT(u.prenom, ' ', u.nom), a.alias, u.perms
120 FROM auth_user_md5 AS u
121 INNER JOIN aliases AS a ON ( a.id=u.user_id AND a.type='a_vie' )
122 WHERE u.user_id = '%s' AND u.password = '%s' AND u.perms IN ('admin', 'user')
123 LIMIT 1""" %( uid
, md5
) )
125 name
, forlife
, perms
= res
126 if vhost
!= PLATAL_DOMAIN
:
127 res
= mysql_fetchone ("""SELECT uid
128 FROM groupex.membres AS m
129 INNER JOIN groupex.asso AS a ON (m.asso_id = a.id)
130 WHERE perms='admin' AND uid='%s' AND mail_domain='%s'""" %( uid
, vhost
) )
131 if res
: perms
= 'admin'
132 userdesc
= UserDesc(forlife
+'@'+PLATAL_DOMAIN
, name
, None, 0)
133 return (userdesc
, perms
, vhost
)
137 ################################################################################
141 #-------------------------------------------------------------------------------
146 db
= MySQLdb
.connect(
150 unix_socket
='/var/run/mysqld/mysqld.sock')
154 def mysql_fetchone(query
):
159 if int(mysql
.rowcount
) > 0:
160 ret
= mysql
.fetchone()
165 def is_admin_on(userdesc
, perms
, mlist
):
166 return ( perms
== 'admin' ) or ( userdesc
.address
in mlist
.owner
)
169 def quote(s
, is_header
=False):
171 h
= Utils
.oneline(s
, 'iso-8859-1')
174 h
= str('').join(re
.split('[\x00-\x08\x0B-\x1f]+', h
))
175 return Utils
.uquote(h
.replace('&', '&').replace('>', '>').replace('<', '<'))
177 def to_forlife(email
):
179 mbox
, fqdn
= email
.split('@')
183 if ( fqdn
== PLATAL_DOMAIN
) or ( fqdn
== PLATAL_DOMAIN2
):
184 res
= mysql_fetchone("""SELECT CONCAT(f.alias, '@%s'), CONCAT(u.prenom, ' ', u.nom)
185 FROM auth_user_md5 AS u
186 INNER JOIN aliases AS f ON (f.id=u.user_id AND f.type='a_vie')
187 INNER JOIN aliases AS a ON (a.id=u.user_id AND a.alias='%s' AND a.type!='homonyme')
188 WHERE u.perms IN ('admin', 'user')
189 LIMIT 1""" %( PLATAL_DOMAIN
, mbox
) )
194 return (email
.lower(), mbox
)
197 # see /usr/lib/mailman/bin/rmlist
199 def remove_it(listname
, filename
):
200 if os
.path
.islink(filename
) or os
.path
.isfile(filename
):
202 elif os
.path
.isdir(filename
):
203 shutil
.rmtree(filename
)
205 #-------------------------------------------------------------------------------
209 def get_list_info(userdesc
, perms
, mlist
, front_page
=0):
210 members
= mlist
.getRegularMemberKeys()
211 is_member
= userdesc
.address
in members
212 is_owner
= userdesc
.address
in mlist
.owner
213 if mlist
.advertised
or is_member
or is_owner
or (not front_page
and perms
== 'admin'):
215 if not is_member
and (mlist
.subscribe_policy
> 1):
218 for id in mlist
.GetSubscriptionIds():
219 if userdesc
.address
== mlist
.GetRecord(id)[1]:
227 host
= mlist
.internal_name().split(VHOST_SEP
)[0].lower()
229 'list' : mlist
.real_name
,
230 'addr' : mlist
.real_name
.lower() + '@' + host
,
232 'desc' : quote(mlist
.description
),
233 'info' : quote(mlist
.info
),
234 'diff' : (mlist
.default_member_moderation
>0) + (mlist
.generic_nonmember_action
>0),
235 'ins' : mlist
.subscribe_policy
> 1,
236 'priv' : 1-mlist
.advertised
,
237 'sub' : 2*is_member
+ is_pending
,
239 'nbsub': len(members
)
241 return (details
, members
)
244 def get_options(userdesc
, perms
, vhost
, listname
, opts
):
246 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
250 if not is_admin_on(userdesc
, perms
, mlist
):
253 for (k
, v
) in mlist
.__dict__
.iteritems():
256 options
[k
] = quote(v
)
258 details
= get_list_info(userdesc
, perms
, mlist
)[0]
259 return (details
, options
)
263 def set_options(userdesc
, perms
, vhost
, listname
, opts
, vals
):
265 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
269 if not is_admin_on(userdesc
, perms
, mlist
):
272 for (k
, v
) in vals
.iteritems():
275 if k
== 'default_member_moderation':
276 for member
in mlist
.getMembers():
277 mlist
.setMemberOption(member
, mm_cfg
.Moderate
, int(v
))
278 t
= type(mlist
.__dict__
[k
])
279 if t
is bool: mlist
.__dict__
[k
] = bool(v
)
280 elif t
is int: mlist
.__dict__
[k
] = int(v
)
281 elif t
is str: mlist
.__dict__
[k
] = Utils
.uncanonstr(v
, 'fr')
282 else: mlist
.__dict__
[k
] = v
290 #-------------------------------------------------------------------------------
291 # users procedures for [ index.php ]
294 def get_lists(userdesc
, perms
, vhost
, email
=None):
298 udesc
= UserDesc(email
.lower(), email
.lower(), None, 0)
299 prefix
= vhost
.lower()+VHOST_SEP
300 names
= Utils
.list_names()
304 if not name
.startswith(prefix
):
307 mlist
= MailList
.MailList(name
, lock
=0)
311 details
= get_list_info(udesc
, perms
, mlist
, (email
is None and vhost
== PLATAL_DOMAIN
))[0]
312 result
.append(details
)
317 def subscribe(userdesc
, perms
, vhost
, listname
):
319 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
324 if ( mlist
.subscribe_policy
in (0, 1) ) or userdesc
.address
in mlist
.owner
:
325 mlist
.ApprovedAddMember(userdesc
)
330 mlist
.AddMember(userdesc
)
331 except Errors
.MMNeedApproval
:
339 def unsubscribe(userdesc
, perms
, vhost
, listname
):
341 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
346 mlist
.ApprovedDeleteMember(userdesc
.address
)
354 #-------------------------------------------------------------------------------
355 # users procedures for [ index.php ]
358 def get_name(member
):
360 return quote(mlist
.getMemberName(member
))
364 def get_members(userdesc
, perms
, vhost
, listname
):
366 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
370 details
, members
= get_list_info(userdesc
, perms
, mlist
)
372 members
= map(lambda member
: (get_name(member
), member
), members
)
373 return (details
, members
, mlist
.owner
)
377 #-------------------------------------------------------------------------------
378 # users procedures for [ trombi.php ]
381 def get_members_limit(userdesc
, perms
, vhost
, listname
, page
, nb_per_page
):
383 members
= get_members(userdesc
, perms
, vhost
, listname
.lower())[1]
386 i
= int(page
) * int(nb_per_page
)
387 return (len(members
), members
[i
:i
+int(nb_per_page
)])
389 def get_owners(userdesc
, perms
, vhost
, listname
):
391 details
, members
, owners
= get_members(userdesc
, perms
, vhost
, listname
.lower())
394 return (details
, owners
)
396 #-------------------------------------------------------------------------------
397 # owners procedures [ admin.php ]
400 def replace_email(userdesc
, perms
, vhost
, listname
, from_email
, to_email
):
402 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
406 if not is_admin_on(userdesc
, perms
, mlist
):
410 mlist
.ApprovedChangeMemberAddress(from_email
.lower(), to_email
.lower(), 0)
417 def mass_subscribe(userdesc
, perms
, vhost
, listname
, users
):
419 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
423 if not is_admin_on(userdesc
, perms
, mlist
):
426 members
= mlist
.getRegularMemberKeys()
430 email
, name
= to_forlife(user
)
431 if ( email
is None ) or ( email
in members
):
433 userd
= UserDesc(email
, name
, None, 0)
434 mlist
.ApprovedAddMember(userd
)
435 added
.append( (quote(userd
.fullname
), userd
.address
) )
442 def mass_unsubscribe(userdesc
, perms
, vhost
, listname
, users
):
444 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
448 if not is_admin_on(userdesc
, perms
, mlist
):
452 map(lambda user
: mlist
.ApprovedDeleteMember(user
), users
)
459 def add_owner(userdesc
, perms
, vhost
, listname
, user
):
461 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
465 if not is_admin_on(userdesc
, perms
, mlist
):
467 email
= to_forlife(user
)[0]
470 if email
not in mlist
.owner
:
472 mlist
.owner
.append(email
)
479 def del_owner(userdesc
, perms
, vhost
, listname
, user
):
481 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
485 if not is_admin_on(userdesc
, perms
, mlist
):
487 if len(mlist
.owner
) < 2:
490 mlist
.owner
.remove(user
)
497 #-------------------------------------------------------------------------------
498 # owners procedures [ admin.php ]
501 def get_pending_ops(userdesc
, perms
, vhost
, listname
):
503 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
507 if not is_admin_on(userdesc
, perms
, mlist
):
515 for id in mlist
.GetSubscriptionIds():
516 time
, addr
, fullname
, passwd
, digest
, lang
= mlist
.GetRecord(id)
518 mlist
.HandleRequest(id, mm_cfg
.DISCARD
)
523 login
= re
.match("^[^.]*\.[^.]*\.\d\d\d\d$", addr
.split('@')[0]).group()
524 subs
.append({'id': id, 'name': quote(fullname
), 'addr': addr
, 'login': login
})
526 subs
.append({'id': id, 'name': quote(fullname
), 'addr': addr
})
529 for id in mlist
.GetHeldMessageIds():
530 ptime
, sender
, subject
, reason
, filename
, msgdata
= mlist
.GetRecord(id)
531 fpath
= os
.path
.join(mm_cfg
.DATA_DIR
, filename
)
533 size
= os
.path
.getsize(fpath
)
535 if e
.errno
<> errno
.ENOENT
: raise
538 msg
= readMessage(fpath
)
539 fromX
= msg
.has_key("X-Org-Mail")
544 'sender': quote(sender
, True),
546 'subj' : quote(subject
, True),
550 if dosave
: mlist
.Save()
557 def handle_request(userdesc
, perms
, vhost
, listname
, id, value
, comment
):
559 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
563 if not is_admin_on(userdesc
, perms
, mlist
):
566 mlist
.HandleRequest(int(id), int(value
), comment
)
574 def get_pending_sub(userdesc
, perms
, vhost
, listname
, id):
576 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
580 if not is_admin_on(userdesc
, perms
, mlist
):
586 if id in mlist
.GetSubscriptionIds():
587 time
, addr
, fullname
, passwd
, digest
, lang
= mlist
.GetRecord(id)
589 login
= re
.match("^[^.]*\.[^.]*\.\d\d\d\d$", addr
.split('@')[0]).group()
590 sub
= {'id': id, 'name': quote(fullname
), 'addr': addr
, 'login': login
}
592 sub
= {'id': id, 'name': quote(fullname
), 'addr': addr
}
599 def get_pending_mail(userdesc
, perms
, vhost
, listname
, id, raw
=0):
601 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
605 if not is_admin_on(userdesc
, perms
, mlist
):
608 ptime
, sender
, subject
, reason
, filename
, msgdata
= mlist
.GetRecord(int(id))
609 fpath
= os
.path
.join(mm_cfg
.DATA_DIR
, filename
)
610 size
= os
.path
.getsize(fpath
)
611 msg
= readMessage(fpath
)
615 return quote(str(msg
))
618 for part
in typed_subpart_iterator(msg
, 'text', 'plain'):
619 c
= part
.get_payload()
620 if c
is not None: results_plain
.append (c
)
621 results_plain
= map(lambda x
: quote(x
), results_plain
)
622 for part
in typed_subpart_iterator(msg
, 'text', 'html'):
623 c
= part
.get_payload()
624 if c
is not None: results_html
.append (c
)
625 results_html
= map(lambda x
: quote(x
), results_html
)
627 'sender': quote(sender
, True),
629 'subj' : quote(subject
, True),
631 'parts_plain' : results_plain
,
632 'parts_html': results_html
}
637 #-------------------------------------------------------------------------------
638 # owner options [ options.php ]
641 owner_opts
= ['accept_these_nonmembers', 'admin_notify_mchanges', 'description', \
642 'default_member_moderation', 'generic_nonmember_action', 'info', \
643 'subject_prefix', 'goodbye_msg', 'send_goodbye_msg', 'subscribe_policy', \
646 def get_owner_options(userdesc
, perms
, vhost
, listname
):
647 return get_options(userdesc
, perms
, vhost
, listname
.lower(), owner_opts
)
649 def set_owner_options(userdesc
, perms
, vhost
, listname
, values
):
650 return set_options(userdesc
, perms
, vhost
, listname
.lower(), owner_opts
, values
)
652 def add_to_wl(userdesc
, perms
, vhost
, listname
, addr
):
654 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
658 if not is_admin_on(userdesc
, perms
, mlist
):
661 mlist
.accept_these_nonmembers
.append(addr
)
669 def del_from_wl(userdesc
, perms
, vhost
, listname
, addr
):
671 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
675 if not is_admin_on(userdesc
, perms
, mlist
):
678 mlist
.accept_these_nonmembers
.remove(addr
)
686 def get_bogo_level(userdesc
, perms
, vhost
, listname
):
687 """ Compute bogo level from the filtering rules set up on the list. """
689 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
693 if not is_admin_on(userdesc
, perms
, mlist
):
695 if len(mlist
.header_filter_rules
) == 0:
702 # The first rule filters Unsure mails
703 if mlist
.header_filter_rules
[0][0] == 'X-Spam-Flag: Unsure, tests=bogofilter':
707 # Check the other rules:
708 # - we have 2 rules: this is level 2 (drop > 0.999999, moderate Yes)
709 # - we have only one rule with HOLD directive : this is level 1 (moderate spams)
710 # - we have only one rule with DISCARD directive : this is level 3 (drop spams)
712 action
= mlist
.header_filter_rules
[filterbase
+ 1][1]
715 action
= mlist
.header_filter_rules
[filterbase
][1]
716 if action
== mm_cfg
.HOLD
:
718 elif action
== mm_cfg
.DISCARD
:
720 return (filterlevel
<< 1) + unsurelevel
724 def set_bogo_level(userdesc
, perms
, vhost
, listname
, level
):
725 """ set filter to the specified level. """
727 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
731 if not is_admin_on(userdesc
, perms
, mlist
):
735 # The level is a combination of a spam filtering level and unsure filtering level
736 # - the unsure filtering level is only 1 bit (1 = HOLD unsures, 0 = Accept unsures)
737 # - the spam filtering level is a number growing with filtering strength
738 # (0 = no filtering, 1 = moderate spam, 2 = drop 0.999999 and moderate others, 3 = drop spams)
739 bogolevel
= int(level
)
740 filterlevel
= bogolevel
>> 1
741 unsurelevel
= bogolevel
& 1
743 # Set up unusre filtering
745 hfr
.append(('X-Spam-Flag: Unsure, tests=bogofilter', mm_cfg
.HOLD
, False))
747 # Set up spam filtering
749 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.HOLD
, False))
750 elif filterlevel
is 2:
751 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter, spamicity=(0\.999999|1\.000000)', mm_cfg
.DISCARD
, False))
752 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.HOLD
, False))
753 elif filterlevel
is 3:
754 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.DISCARD
, False))
757 if mlist
.header_filter_rules
!= hfr
:
759 mlist
.header_filter_rules
= hfr
767 #-------------------------------------------------------------------------------
768 # admin procedures [ soptions.php ]
771 admin_opts
= [ 'advertised', 'archive', \
772 'max_message_size', 'msg_footer', 'msg_header']
774 def get_admin_options(userdesc
, perms
, vhost
, listname
):
777 return get_options(userdesc
, perms
, vhost
, listname
.lower(), admin_opts
)
779 def set_admin_options(userdesc
, perms
, vhost
, listname
, values
):
782 return set_options(userdesc
, perms
, vhost
, listname
.lower(), admin_opts
, values
)
784 #-------------------------------------------------------------------------------
785 # admin procedures [ check.php ]
789 'acceptable_aliases' : '',
790 'admin_immed_notify' : True,
791 'administrivia' : True,
792 'anonymous_list' : False,
793 'autorespond_admin' : False,
794 'autorespond_postings' : False,
795 'autorespond_requests' : False,
796 'available_languages' : ['fr'],
798 'bounce_matching_headers' : '',
799 'bounce_processing' : False,
800 'convert_html_to_plaintext' : False,
801 'digestable' : False,
802 'digest_is_default' : False,
803 'discard_these_nonmembers' : [],
805 'encode_ascii_prefixes' : 2,
806 'filter_content' : False,
807 'first_strip_reply_to' : False,
808 'forward_auto_discards' : True,
809 'hold_these_nonmembers' : [],
810 'host_name' : 'listes.polytechnique.org',
811 'include_list_post_header' : False,
812 'include_rfc2369_headers' : False,
813 'max_num_recipients' : 0,
814 'new_member_options' : 256,
815 'nondigestable' : True,
816 'obscure_addresses' : True,
817 'preferred_language' : 'fr',
818 'reject_these_nonmembers' : [],
819 'reply_goes_to_list' : 0,
820 'reply_to_address' : '',
821 'require_explicit_destination' : False,
822 'send_reminders' : 0,
823 'send_welcome_msg' : True,
824 'topics_enabled' : False,
825 'umbrella_list' : False,
826 'unsubscribe_policy' : 0,
829 def check_options(userdesc
, perms
, vhost
, listname
, correct
=False):
830 listname
= listname
.lower()
832 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
, lock
=0)
836 if perms
!= 'admin': return 0
840 for (k
, v
) in check_opts
.iteritems():
841 if mlist
.__dict__
[k
] != v
:
842 options
[k
] = v
, mlist
.__dict__
[k
]
843 if correct
: mlist
.__dict__
[k
] = v
844 if mlist
.real_name
.lower() != listname
:
845 options
['real_name'] = listname
, mlist
.real_name
846 if correct
: mlist
.real_name
= listname
850 details
= get_list_info(userdesc
, perms
, mlist
)[0]
851 return (details
, options
)
853 if correct
: mlist
.Unlock()
856 #-------------------------------------------------------------------------------
857 # super-admin procedures
860 def get_all_lists(userdesc
, perms
, vhost
):
861 prefix
= vhost
.lower()+VHOST_SEP
862 names
= Utils
.list_names()
866 if not name
.startswith(prefix
):
868 result
.append(name
.replace(prefix
, ''))
871 def create_list(userdesc
, perms
, vhost
, listname
, desc
, advertise
, modlevel
, inslevel
, owners
, members
):
874 name
= vhost
.lower()+VHOST_SEP
+listname
.lower();
875 if Utils
.list_exists(name
):
880 email
= to_forlife(o
)[0]
881 if email
is not None:
886 mlist
= MailList
.MailList()
888 oldmask
= os
.umask(002)
889 pw
= sha
.new('foobar').hexdigest()
892 mlist
.Create(name
, owner
[0], pw
)
896 mlist
.real_name
= listname
897 mlist
.host_name
= 'listes.polytechnique.org'
898 mlist
.description
= desc
900 mlist
.advertised
= int(advertise
) is 0
901 mlist
.default_member_moderation
= int(modlevel
) is 2
902 mlist
.generic_nonmember_action
= int(modlevel
) > 0
903 mlist
.subscribe_policy
= 2 * (int(inslevel
) is 1)
904 mlist
.admin_notify_mchanges
= (mlist
.subscribe_policy
or mlist
.generic_nonmember_action
or mlist
.default_member_moderation
or not mlist
.advertised
)
908 mlist
.subject_prefix
= '['+listname
+'] '
909 mlist
.max_message_size
= 0
911 inverted_listname
= listname
.lower() + '_' + vhost
.lower()
912 mlist
.msg_footer
= "_______________________________________________\n" \
913 + "Liste de diffusion %(real_name)s\n" \
914 + "http://listes.polytechnique.org/members/" + inverted_listname
916 mlist
.header_filter_rules
= []
917 mlist
.header_filter_rules
.append(('X-Spam-Flag: Unsure, tests=bogofilter', mm_cfg
.HOLD
, False))
918 mlist
.header_filter_rules
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.HOLD
, False))
924 if ON_CREATE_CMD
!= '':
925 try: os
.system(ON_CREATE_CMD
+ ' ' + name
)
928 check_options(userdesc
, perms
, vhost
, listname
.lower(), True)
929 mass_subscribe(userdesc
, perms
, vhost
, listname
.lower(), members
)
931 # avoid the "-1 mail to moderate" bug
932 mlist
= MailList
.MailList(name
)
933 mlist
._UpdateRecords()
944 def delete_list(userdesc
, perms
, vhost
, listname
, del_archives
=0):
945 lname
= vhost
+VHOST_SEP
+listname
.lower()
947 mlist
= MailList
.MailList(lname
, lock
=0)
951 if not is_admin_on(userdesc
, perms
, mlist
):
954 REMOVABLES
= [ os
.path
.join('lists', lname
), ]
955 # remove stalled locks
956 for filename
in os
.listdir(mm_cfg
.LOCK_DIR
):
957 fn_lname
= filename
.split('.')[0]
958 if fn_lname
== lname
:
959 REMOVABLES
.append(os
.path
.join(mm_cfg
.LOCK_DIR
, filename
))
963 os
.path
.join('archives', 'private', lname
),
964 os
.path
.join('archives', 'private', lname
+'.mbox'),
965 os
.path
.join('archives', 'public', lname
),
966 os
.path
.join('archives', 'public', lname
+'.mbox')
968 map(lambda dir: remove_it(lname
, os
.path
.join(mm_cfg
.VAR_PREFIX
, dir)), REMOVABLES
)
973 def kill(userdesc
, perms
, vhost
, alias
, del_from_promo
):
975 if not del_from_promo
:
976 exclude
.append(PLATAL_DOMAIN
+VHOST_SEP
+'promo'+alias
[-4:])
977 for list in Utils
.list_names():
978 if list in exclude
: continue
980 mlist
= MailList
.MailList(list, lock
=0)
985 mlist
.ApprovedDeleteMember(alias
+'@'+PLATAL_DOMAIN
, None, 0, 0)
993 #-------------------------------------------------------------------------------
996 class FastXMLRPCServer(SocketServer
.ThreadingMixIn
, SimpleXMLRPCServer
):
997 allow_reuse_address
= True
999 ################################################################################
1003 #-------------------------------------------------------------------------------
1004 # use Mailman user and group (not root)
1005 # fork in background if asked to
1008 uid
= getpwnam(mm_cfg
.MAILMAN_USER
)[2]
1009 gid
= getgrnam(mm_cfg
.MAILMAN_GROUP
)[2]
1012 os
.setregid(gid
, gid
)
1013 os
.setreuid(uid
, uid
)
1015 signal
.signal(signal
.SIGHUP
, signal
.SIG_IGN
)
1017 if ( os
.getuid() is not uid
) or ( os
.getgid() is not gid
):
1020 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'f')
1022 if o
== '-f' and os
.fork():
1025 i18n
.set_language('fr')
1029 #-------------------------------------------------------------------------------
1032 server
= FastXMLRPCServer(("localhost", 4949), BasicAuthXMLRPCRequestHandler
)
1035 server
.register_function(get_lists
)
1036 server
.register_function(subscribe
)
1037 server
.register_function(unsubscribe
)
1039 server
.register_function(get_members
)
1041 server
.register_function(get_members_limit
)
1042 server
.register_function(get_owners
)
1044 server
.register_function(replace_email
)
1045 server
.register_function(mass_subscribe
)
1046 server
.register_function(mass_unsubscribe
)
1047 server
.register_function(add_owner
)
1048 server
.register_function(del_owner
)
1050 server
.register_function(get_pending_ops
)
1051 server
.register_function(handle_request
)
1052 server
.register_function(get_pending_sub
)
1053 server
.register_function(get_pending_mail
)
1055 server
.register_function(get_owner_options
)
1056 server
.register_function(set_owner_options
)
1057 server
.register_function(add_to_wl
)
1058 server
.register_function(del_from_wl
)
1059 server
.register_function(get_bogo_level
)
1060 server
.register_function(set_bogo_level
)
1062 server
.register_function(get_admin_options
)
1063 server
.register_function(set_admin_options
)
1065 server
.register_function(check_options
)
1067 server
.register_function(get_all_lists
)
1068 server
.register_function(create_list
)
1069 server
.register_function(delete_list
)
1071 server
.register_function(kill
)
1073 server
.serve_forever()
1075 # vim:set et sw=4 sts=4 sws=4: