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-\x09\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_members(userdesc
, perms
, vhost
, listname
):
360 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
364 details
, members
= get_list_info(userdesc
, perms
, mlist
)
366 members
= map(lambda member
: (quote(mlist
.getMemberName(member
)) or '', member
), members
)
367 return (details
, members
, mlist
.owner
)
371 #-------------------------------------------------------------------------------
372 # users procedures for [ trombi.php ]
375 def get_members_limit(userdesc
, perms
, vhost
, listname
, page
, nb_per_page
):
377 members
= get_members(userdesc
, perms
, vhost
, listname
.lower())[1]
380 i
= int(page
) * int(nb_per_page
)
381 return (len(members
), members
[i
:i
+int(nb_per_page
)])
383 def get_owners(userdesc
, perms
, vhost
, listname
):
385 details
, members
, owners
= get_members(userdesc
, perms
, vhost
, listname
.lower())
388 return (details
, owners
)
390 #-------------------------------------------------------------------------------
391 # owners procedures [ admin.php ]
394 def replace_email(userdesc
, perms
, vhost
, listname
, from_email
, to_email
):
396 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
400 if not is_admin_on(userdesc
, perms
, mlist
):
404 mlist
.ApprovedChangeMemberAddress(from_email
.lower(), to_email
.lower(), 0)
411 def mass_subscribe(userdesc
, perms
, vhost
, listname
, users
):
413 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
417 if not is_admin_on(userdesc
, perms
, mlist
):
420 members
= mlist
.getRegularMemberKeys()
424 email
, name
= to_forlife(user
)
425 if ( email
is None ) or ( email
in members
):
427 userd
= UserDesc(email
, name
, None, 0)
428 mlist
.ApprovedAddMember(userd
)
429 added
.append( (quote(userd
.fullname
), userd
.address
) )
436 def mass_unsubscribe(userdesc
, perms
, vhost
, listname
, users
):
438 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
442 if not is_admin_on(userdesc
, perms
, mlist
):
446 map(lambda user
: mlist
.ApprovedDeleteMember(user
), users
)
453 def add_owner(userdesc
, perms
, vhost
, listname
, user
):
455 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
459 if not is_admin_on(userdesc
, perms
, mlist
):
461 email
= to_forlife(user
)[0]
464 if email
not in mlist
.owner
:
466 mlist
.owner
.append(email
)
473 def del_owner(userdesc
, perms
, vhost
, listname
, user
):
475 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
479 if not is_admin_on(userdesc
, perms
, mlist
):
481 if len(mlist
.owner
) < 2:
484 mlist
.owner
.remove(user
)
491 #-------------------------------------------------------------------------------
492 # owners procedures [ admin.php ]
495 def get_pending_ops(userdesc
, perms
, vhost
, listname
):
497 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
501 if not is_admin_on(userdesc
, perms
, mlist
):
509 for id in mlist
.GetSubscriptionIds():
510 time
, addr
, fullname
, passwd
, digest
, lang
= mlist
.GetRecord(id)
512 mlist
.HandleRequest(id, mm_cfg
.DISCARD
)
517 login
= re
.match("^[^.]*\.[^.]*\.\d\d\d\d$", addr
.split('@')[0]).group()
518 subs
.append({'id': id, 'name': quote(fullname
), 'addr': addr
, 'login': login
})
520 subs
.append({'id': id, 'name': quote(fullname
), 'addr': addr
})
523 for id in mlist
.GetHeldMessageIds():
524 ptime
, sender
, subject
, reason
, filename
, msgdata
= mlist
.GetRecord(id)
526 size
= os
.path
.getsize(os
.path
.join(mm_cfg
.DATA_DIR
, filename
))
528 if e
.errno
<> errno
.ENOENT
: raise
532 'sender': quote(sender
, True),
534 'subj' : quote(subject
, True),
537 if dosave
: mlist
.Save()
545 def handle_request(userdesc
, perms
, vhost
, listname
, id, value
, comment
):
547 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
551 if not is_admin_on(userdesc
, perms
, mlist
):
554 mlist
.HandleRequest(int(id), int(value
), comment
)
563 def get_pending_mail(userdesc
, perms
, vhost
, listname
, id, raw
=0):
565 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
569 if not is_admin_on(userdesc
, perms
, mlist
):
572 ptime
, sender
, subject
, reason
, filename
, msgdata
= mlist
.GetRecord(int(id))
573 fpath
= os
.path
.join(mm_cfg
.DATA_DIR
, filename
)
574 size
= os
.path
.getsize(fpath
)
575 msg
= readMessage(fpath
)
582 for part
in typed_subpart_iterator(msg
, 'text', 'plain'):
583 c
= part
.get_payload()
584 if c
is not None: results_plain
.append (c
)
585 results_plain
= map(lambda x
: quote(x
), results_plain
)
586 for part
in typed_subpart_iterator(msg
, 'text', 'html'):
587 c
= part
.get_payload()
588 if c
is not None: results_html
.append (c
)
589 results_html
= map(lambda x
: quote(x
), results_html
)
591 'sender': quote(sender
, True),
593 'subj' : quote(subject
, True),
595 'parts_plain' : results_plain
,
596 'parts_html': results_html
}
601 #-------------------------------------------------------------------------------
602 # owner options [ options.php ]
605 owner_opts
= ['accept_these_nonmembers', 'admin_notify_mchanges', 'description', \
606 'default_member_moderation', 'generic_nonmember_action', 'info', \
607 'subject_prefix', 'goodbye_msg', 'send_goodbye_msg', 'subscribe_policy', \
610 def get_owner_options(userdesc
, perms
, vhost
, listname
):
611 return get_options(userdesc
, perms
, vhost
, listname
.lower(), owner_opts
)
613 def set_owner_options(userdesc
, perms
, vhost
, listname
, values
):
614 return set_options(userdesc
, perms
, vhost
, listname
.lower(), owner_opts
, values
)
616 def add_to_wl(userdesc
, perms
, vhost
, listname
, addr
):
618 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
622 if not is_admin_on(userdesc
, perms
, mlist
):
625 mlist
.accept_these_nonmembers
.append(addr
)
633 def del_from_wl(userdesc
, perms
, vhost
, listname
, addr
):
635 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
639 if not is_admin_on(userdesc
, perms
, mlist
):
642 mlist
.accept_these_nonmembers
.remove(addr
)
650 def get_bogo_level(userdesc
, perms
, vhost
, listname
):
652 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
656 if not is_admin_on(userdesc
, perms
, mlist
):
658 if mlist
.header_filter_rules
== []:
661 action
= mlist
.header_filter_rules
[1][1]
664 action
= mlist
.header_filter_rules
[0][1]
665 if action
== mm_cfg
.HOLD
:
667 if action
== mm_cfg
.DISCARD
:
672 def set_bogo_level(userdesc
, perms
, vhost
, listname
, level
):
674 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
.lower(), lock
=0)
678 if not is_admin_on(userdesc
, perms
, mlist
):
682 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.HOLD
, False))
683 elif int(level
) is 2:
684 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter, spamicity=(0\.999999|1\.000000)', mm_cfg
.DISCARD
, False))
685 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.HOLD
, False))
686 elif int(level
) is 3:
687 hfr
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.DISCARD
, False))
688 if mlist
.header_filter_rules
!= hfr
:
690 mlist
.header_filter_rules
= hfr
698 #-------------------------------------------------------------------------------
699 # admin procedures [ soptions.php ]
702 admin_opts
= [ 'advertised', 'archive', \
703 'max_message_size', 'msg_footer', 'msg_header']
705 def get_admin_options(userdesc
, perms
, vhost
, listname
):
708 return get_options(userdesc
, perms
, vhost
, listname
.lower(), admin_opts
)
710 def set_admin_options(userdesc
, perms
, vhost
, listname
, values
):
713 return set_options(userdesc
, perms
, vhost
, listname
.lower(), admin_opts
, values
)
715 #-------------------------------------------------------------------------------
716 # admin procedures [ check.php ]
720 'acceptable_aliases' : '',
721 'admin_immed_notify' : True,
722 'administrivia' : True,
723 'anonymous_list' : False,
724 'autorespond_admin' : False,
725 'autorespond_postings' : False,
726 'autorespond_requests' : False,
727 'available_languages' : ['fr'],
729 'bounce_matching_headers' : '',
730 'bounce_processing' : False,
731 'convert_html_to_plaintext' : False,
732 'digestable' : False,
733 'digest_is_default' : False,
734 'discard_these_nonmembers' : [],
736 'encode_ascii_prefixes' : 2,
737 'filter_content' : False,
738 'first_strip_reply_to' : False,
739 'forward_auto_discards' : True,
740 'hold_these_nonmembers' : [],
741 'host_name' : 'listes.polytechnique.org',
742 'include_list_post_header' : False,
743 'include_rfc2369_headers' : False,
744 'max_num_recipients' : 0,
745 'new_member_options' : 256,
746 'nondigestable' : True,
747 'obscure_addresses' : True,
748 'preferred_language' : 'fr',
749 'reject_these_nonmembers' : [],
750 'reply_goes_to_list' : 0,
751 'reply_to_address' : '',
752 'require_explicit_destination' : False,
753 'send_reminders' : 0,
754 'send_welcome_msg' : True,
755 'topics_enabled' : False,
756 'umbrella_list' : False,
757 'unsubscribe_policy' : 0,
760 def check_options(userdesc
, perms
, vhost
, listname
, correct
=False):
761 listname
= listname
.lower()
763 mlist
= MailList
.MailList(vhost
+VHOST_SEP
+listname
, lock
=0)
767 if perms
!= 'admin': return 0
771 for (k
, v
) in check_opts
.iteritems():
772 if mlist
.__dict__
[k
] != v
:
773 options
[k
] = v
, mlist
.__dict__
[k
]
774 if correct
: mlist
.__dict__
[k
] = v
775 if mlist
.real_name
.lower() != listname
:
776 options
['real_name'] = listname
, mlist
.real_name
777 if correct
: mlist
.real_name
= listname
781 details
= get_list_info(userdesc
, perms
, mlist
)[0]
782 return (details
, options
)
784 if correct
: mlist
.Unlock()
787 #-------------------------------------------------------------------------------
788 # super-admin procedures
791 def get_all_lists(userdesc
, perms
, vhost
):
792 prefix
= vhost
.lower()+VHOST_SEP
793 names
= Utils
.list_names()
797 if not name
.startswith(prefix
):
799 result
.append(name
.replace(prefix
, ''))
802 def create_list(userdesc
, perms
, vhost
, listname
, desc
, advertise
, modlevel
, inslevel
, owners
, members
):
805 name
= vhost
.lower()+VHOST_SEP
+listname
.lower();
806 if Utils
.list_exists(name
):
811 email
= to_forlife(o
)[0]
812 if email
is not None:
817 mlist
= MailList
.MailList()
819 oldmask
= os
.umask(002)
820 pw
= sha
.new('foobar').hexdigest()
823 mlist
.Create(name
, owner
[0], pw
)
827 mlist
.real_name
= listname
828 mlist
.host_name
= 'listes.polytechnique.org'
829 mlist
.description
= desc
831 mlist
.advertised
= int(advertise
) is 0
832 mlist
.default_member_moderation
= int(modlevel
) is 2
833 mlist
.generic_nonmember_action
= int(modlevel
) > 0
834 mlist
.subscribe_policy
= 2 * (int(inslevel
) is 1)
835 mlist
.admin_notify_mchanges
= (mlist
.subscribe_policy
or mlist
.generic_nonmember_action
or mlist
.default_member_moderation
or not mlist
.advertised
)
839 mlist
.subject_prefix
= '['+listname
+'] '
840 mlist
.max_message_size
= 0
842 inverted_listname
= '_'.join(listname
.split('_', 1)[-1::-1])
843 mlist
.msg_footer
= "_______________________________________________\n" \
844 + "Liste de diffusion %(real_name)s\n" \
845 + "http://listes.polytechnique.org/members/" + inverted_listname
847 mlist
.header_filter_rules
= []
848 mlist
.header_filter_rules
.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg
.HOLD
, False))
854 if ON_CREATE_CMD
!= '':
855 try: os
.system(ON_CREATE_CMD
+ ' ' + name
)
858 check_options(userdesc
, perms
, vhost
, listname
.lower(), True)
859 mass_subscribe(userdesc
, perms
, vhost
, listname
.lower(), members
)
861 # avoid the "-1 mail to moderate" bug
862 mlist
= MailList
.MailList(name
)
863 mlist
._UpdateRecords()
874 def delete_list(userdesc
, perms
, vhost
, listname
, del_archives
=0):
875 lname
= vhost
+VHOST_SEP
+listname
.lower()
877 mlist
= MailList
.MailList(lname
, lock
=0)
881 if not is_admin_on(userdesc
, perms
, mlist
):
884 REMOVABLES
= [ os
.path
.join('lists', lname
), ]
885 # remove stalled locks
886 for filename
in os
.listdir(mm_cfg
.LOCK_DIR
):
887 fn_lname
= filename
.split('.')[0]
888 if fn_lname
== lname
:
889 REMOVABLES
.append(os
.path
.join(mm_cfg
.LOCK_DIR
, filename
))
893 os
.path
.join('archives', 'private', lname
),
894 os
.path
.join('archives', 'private', lname
+'.mbox'),
895 os
.path
.join('archives', 'public', lname
),
896 os
.path
.join('archives', 'public', lname
+'.mbox')
898 map(lambda dir: remove_it(lname
, os
.path
.join(mm_cfg
.VAR_PREFIX
, dir)), REMOVABLES
)
903 def kill(userdesc
, perms
, vhost
, alias
, del_from_promo
):
905 if not del_from_promo
:
906 exclude
.append(PLATAL_DOMAIN
+VHOST_SEP
+'promo'+alias
[-4:])
907 for list in Utils
.list_names():
908 if list in exclude
: continue
910 mlist
= MailList
.MailList(list, lock
=0)
915 mlist
.ApprovedDeleteMember(alias
+'@'+PLATAL_DOMAIN
, None, 0, 0)
923 #-------------------------------------------------------------------------------
926 class FastXMLRPCServer(SocketServer
.ThreadingMixIn
, SimpleXMLRPCServer
):
927 allow_reuse_address
= True
929 ################################################################################
933 #-------------------------------------------------------------------------------
934 # use Mailman user and group (not root)
935 # fork in background if asked to
938 uid
= getpwnam(mm_cfg
.MAILMAN_USER
)[2]
939 gid
= getgrnam(mm_cfg
.MAILMAN_GROUP
)[2]
942 os
.setregid(gid
, gid
)
943 os
.setreuid(uid
, uid
)
945 signal
.signal(signal
.SIGHUP
, signal
.SIG_IGN
)
947 if ( os
.getuid() is not uid
) or ( os
.getgid() is not gid
):
950 opts
, args
= getopt
.getopt(sys
.argv
[1:], 'f')
952 if o
== '-f' and os
.fork():
955 i18n
.set_language('fr')
959 #-------------------------------------------------------------------------------
962 server
= FastXMLRPCServer(("localhost", 4949), BasicAuthXMLRPCRequestHandler
)
965 server
.register_function(get_lists
)
966 server
.register_function(subscribe
)
967 server
.register_function(unsubscribe
)
969 server
.register_function(get_members
)
971 server
.register_function(get_members_limit
)
972 server
.register_function(get_owners
)
974 server
.register_function(replace_email
)
975 server
.register_function(mass_subscribe
)
976 server
.register_function(mass_unsubscribe
)
977 server
.register_function(add_owner
)
978 server
.register_function(del_owner
)
980 server
.register_function(get_pending_ops
)
981 server
.register_function(handle_request
)
982 server
.register_function(get_pending_mail
)
984 server
.register_function(get_owner_options
)
985 server
.register_function(set_owner_options
)
986 server
.register_function(add_to_wl
)
987 server
.register_function(del_from_wl
)
988 server
.register_function(get_bogo_level
)
989 server
.register_function(set_bogo_level
)
991 server
.register_function(get_admin_options
)
992 server
.register_function(set_admin_options
)
994 server
.register_function(check_options
)
996 server
.register_function(get_all_lists
)
997 server
.register_function(create_list
)
998 server
.register_function(delete_list
)
1000 server
.register_function(kill
)
1002 server
.serve_forever()