first reimport from platal
[platal.git] / bin / lists.rpc.py
CommitLineData
0337d704 1#!/usr/bin/env python
2#***************************************************************************
3#* Copyright (C) 2004 polytechnique.org *
4#* http://opensource.polytechnique.org/ *
5#* *
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. *
10#* *
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. *
15#* *
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#***************************************************************************
21
22import base64, MySQLdb, os, getopt, sys, sha, signal, re, shutil, ConfigParser
23import MySQLdb.converters
24import SocketServer
25
26sys.path.append('/usr/lib/mailman/bin')
27
28from pwd import getpwnam
29from grp import getgrnam
30
31from SimpleXMLRPCServer import SimpleXMLRPCServer
32from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
33
34import paths
35from Mailman import MailList
36from Mailman import Utils
37from Mailman import Message
38from Mailman import Errors
39from Mailman import mm_cfg
40from Mailman import i18n
41from Mailman.UserDesc import UserDesc
42from Mailman.ListAdmin import readMessage
43from email.Iterators import typed_subpart_iterator
44from threading import Lock
45
46class AuthFailed(Exception): pass
47
48################################################################################
49#
50# CONFIG
51#
52#------------------------------------------------
53
54config = ConfigParser.ConfigParser()
55config.read(os.path.dirname(__file__)+'/../configs/platal.conf')
56
57def get_config(sec,val,default=None):
58 try:
59 return config.get(sec, val)[1:-1]
60 except ConfigParser.NoOptionError, e:
61 if default is None:
62 print e
63 sys.exit(1)
64 else:
65 return default
66
67BASEURL = get_config('Core', 'baseurl')
68MYSQL_USER = get_config('Core', 'dbuser')
69MYSQL_PASS = get_config('Core', 'dbpwd')
70
71PLATAL_DOMAIN = get_config('Mail', 'domain')
72PLATAL_DOMAIN2 = get_config('Mail', 'domain2', '')
73
74ML_OWNER = get_config('Lists', 'admin_owner')
75VHOST_SEP = get_config('Lists', 'vhost_sep', '_')
76ON_CREATE_CMD = get_config('Lists', 'on_create', '')
77
78################################################################################
79#
80# CLASSES
81#
82#------------------------------------------------
83# Manage Basic authentication
84#
85
86class BasicAuthXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
87
88 """XMLRPC Request Handler
89 This request handler is used to provide BASIC HTTP user authentication.
90 It first overloads the do_POST() function, authenticates the user, then
91 calls the super.do_POST().
92
93 Moreover, we override _dispatch, so that we call functions with as first
94 argument a UserDesc taken from the database, containing name, email and perms
95 """
96
97 def _dispatch(self,method,params):
98 # TODO: subclass in SimpleXMLRPCDispatcher and not here.
99 new_params = list(params)
100 new_params.insert(0,self.data[2])
101 new_params.insert(0,self.data[1])
102 new_params.insert(0,self.data[0])
103 return self.server._dispatch(method,new_params)
104
105 def do_POST(self):
106 try:
107 _, auth = self.headers["authorization"].split()
108 uid, md5 = base64.decodestring(auth).strip().split(':')
109 vhost = self.path.split('/')[1].lower()
110 self.data = self.getUser(uid,md5,vhost)
111 if self.data is None:
112 raise AuthFailed
113 # Call super.do_POST() to do the actual work
114 SimpleXMLRPCRequestHandler.do_POST(self)
115 except:
116 self.send_response(401)
117 self.end_headers()
118
119 def getUser(self, uid, md5, vhost):
120 res = mysql_fetchone ("""SELECT CONCAT(u.prenom, ' ',u.nom),a.alias,u.perms
121 FROM auth_user_md5 AS u
122 INNER JOIN aliases AS a ON ( a.id=u.user_id AND a.type='a_vie' )
123 WHERE u.user_id = '%s' AND u.password = '%s' AND u.perms IN ('admin','user')
124 LIMIT 1""" %( uid, md5 ) )
125 if res:
126 name,forlife,perms = res
127 if vhost != PLATAL_DOMAIN:
128 res = mysql_fetchone ("""SELECT uid
129 FROM groupex.membres AS m
130 INNER JOIN groupex.asso AS a ON (m.asso_id = a.id)
131 WHERE perms='admin' AND uid='%s' AND mail_domain='%s'""" %( uid , vhost ) )
132 if res: perms= 'admin'
133 userdesc = UserDesc(forlife+'@'+PLATAL_DOMAIN, name, None, 0)
134 return (userdesc,perms,vhost)
135 else:
136 return None
137
138################################################################################
139#
140# XML RPC STUFF
141#
142#-------------------------------------------------------------------------------
143# helpers
144#
145
146def connectDB():
147 db = MySQLdb.connect(
148 db='x4dat',
149 user=MYSQL_USER,
150 passwd=MYSQL_PASS,
151 unix_socket='/var/run/mysqld/mysqld.sock')
152 db.ping()
153 return db.cursor()
154
155def mysql_fetchone(query):
156 ret = None
157 try:
158 lock.acquire()
159 mysql.execute(query)
160 if int(mysql.rowcount) > 0:
161 ret = mysql.fetchone()
162 finally:
163 lock.release()
164 return ret
165
166def is_owner(userdesc,perms,mlist):
167 return ( perms == 'admin' and ML_OWNER in mlist.owner ) or ( userdesc.address in mlist.owner )
168
169def is_admin_on(userdesc,perms,mlist):
170 return ( perms == 'admin' ) or ( userdesc.address in mlist.owner )
171
172
173def quote(s,is_header=False):
174 if is_header:
175 h = Utils.oneline(s,'iso-8859-1')
176 else:
177 h = s
178 h = str('').join(re.split('[\x00-\x1f]+', s))
179 return Utils.uquote(h.replace('&','&amp;').replace('>','&gt;').replace('<','&lt;'))
180
181def to_forlife(email):
182 try:
183 mbox,fqdn = email.split('@')
184 except:
185 mbox = email
186 fqdn = PLATAL_DOMAIN
187 if ( fqdn == PLATAL_DOMAIN ) or ( fqdn == PLATAL_DOMAIN2 ):
188 res = mysql_fetchone("""SELECT CONCAT(f.alias,'@%s'), CONCAT(u.prenom,' ',u.nom)
189 FROM auth_user_md5 AS u
190 INNER JOIN aliases AS f ON (f.id=u.user_id AND f.type='a_vie')
191 INNER JOIN aliases AS a ON (a.id=u.user_id AND a.alias='%s' AND a.type!='homonyme')
192 WHERE u.perms IN ('admin','user')
193 LIMIT 1""" %( PLATAL_DOMAIN, mbox ) )
194 if res:
195 return res
196 else:
197 return (None,None)
198 return (email,mbox)
199
200##
201# see /usr/lib/mailman/bin/rmlist
202##
203def remove_it(listname, filename):
204 if os.path.islink(filename) or os.path.isfile(filename):
205 os.unlink(filename)
206 elif os.path.isdir(filename):
207 shutil.rmtree(filename)
208
209#-------------------------------------------------------------------------------
210# helpers on lists
211#
212
213def get_list_info(userdesc,perms,mlist,front_page=0):
214 members = mlist.getRegularMemberKeys()
215 is_member = userdesc.address in members
216 is_admin = ML_OWNER in mlist.owner
217 is_owner = ( perms == 'admin' and is_admin ) or ( userdesc.address in mlist.owner )
218 if mlist.advertised or is_member or is_owner or (not front_page and perms == 'admin'):
219 is_pending = False
220 if not is_member and (mlist.subscribe_policy > 1):
221 try:
222 mlist.Lock()
223 for id in mlist.GetSubscriptionIds():
224 if userdesc.address == mlist.GetRecord(id)[1]:
225 is_pending = 1
226 break
227 mlist.Unlock()
228 except:
229 mlist.Unlock()
230 return 0
231
232 host = mlist.internal_name().split(VHOST_SEP)[0].lower()
233 details = {
234 'list' : mlist.real_name,
235 'addr' : mlist.real_name.lower() + '@' + host,
236 'host' : host,
237 'desc' : quote(mlist.description),
238 'info' : quote(mlist.info),
239 'diff' : (mlist.default_member_moderation>0) + (mlist.generic_nonmember_action>0),
240 'ins' : mlist.subscribe_policy > 1,
241 'priv' : (1-mlist.advertised)+2*is_admin,
242 'sub' : 2*is_member + is_pending,
243 'own' : is_owner,
244 'nbsub': len(members)
245 }
246 return (details,members)
247 return 0
248
249def get_options(userdesc,perms,vhost,listname,opts):
250 try:
251 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
252 except:
253 return 0
254 try:
255 if not is_admin_on(userdesc, perms, mlist):
256 return 0
257 options = { }
258 for (k,v) in mlist.__dict__.iteritems():
259 if k in opts:
260 if type(v) is str:
261 options[k] = quote(v)
262 else: options[k] = v
263 details = get_list_info(userdesc,perms,mlist)[0]
264 return (details,options)
265 except:
266 return 0
267
268def set_options(userdesc,perms,vhost,listname,opts,vals):
269 try:
270 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
271 except:
272 return 0
273 try:
274 if not is_admin_on(userdesc, perms, mlist):
275 return 0
276 mlist.Lock()
277 for (k,v) in vals.iteritems():
278 if k not in opts:
279 continue
280 if k == 'default_member_moderation':
281 for member in mlist.getMembers():
282 mlist.setMemberOption(member, mm_cfg.Moderate, int(v))
283 t = type(mlist.__dict__[k])
284 if t is bool: mlist.__dict__[k] = bool(v)
285 elif t is int: mlist.__dict__[k] = int(v)
286 elif t is str: mlist.__dict__[k] = Utils.uncanonstr(v,'fr')
287 else: mlist.__dict__[k] = v
288 mlist.Save()
289 mlist.Unlock()
290 return 1
291 except:
292 mlist.Unlock()
293 return 0
294
295#-------------------------------------------------------------------------------
296# users procedures for [ index.php ]
297#
298
299def get_lists(userdesc,perms,vhost,email=None):
300 if email is None:
301 udesc = userdesc
302 else:
303 udesc = UserDesc(email, email, None, 0)
304 prefix = vhost.lower()+VHOST_SEP
305 names = Utils.list_names()
306 names.sort()
307 result = []
308 for name in names:
309 if not name.startswith(prefix):
310 continue
311 try:
312 mlist = MailList.MailList(name,lock=0)
313 except:
314 continue
315 try:
316 details = get_list_info(udesc,perms,mlist,(email is None and vhost == PLATAL_DOMAIN))[0]
317 result.append(details)
318 except:
319 continue
320 return result
321
322def subscribe(userdesc,perms,vhost,listname):
323 try:
324 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
325 except:
326 return 0
327 try:
328 mlist.Lock()
329 if ( mlist.subscribe_policy in (0,1) ) or is_owner(userdesc,perms,mlist):
330 mlist.ApprovedAddMember(userdesc)
331 result = 2
332 else:
333 result = 1
334 try:
335 mlist.AddMember(userdesc)
336 except Errors.MMNeedApproval:
337 pass
338 mlist.Save()
339 except:
340 result = 0
341 mlist.Unlock()
342 return result
343
344def unsubscribe(userdesc,perms,vhost,listname):
345 try:
346 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
347 except:
348 return 0
349 try:
350 mlist.Lock()
351 mlist.ApprovedDeleteMember(userdesc.address)
352 mlist.Save()
353 mlist.Unlock()
354 return 1
355 except:
356 mlist.Unlock()
357 return 0
358
359#-------------------------------------------------------------------------------
360# users procedures for [ index.php ]
361#
362
363def get_members(userdesc,perms,vhost,listname):
364 try:
365 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
366 except:
367 return 0
368 try:
369 details,members = get_list_info(userdesc,perms,mlist)
370 members.sort()
371 members = map(lambda member: (quote(mlist.getMemberName(member)) or '', member), members)
372 return (details,members,mlist.owner)
373 except:
374 return 0
375
376#-------------------------------------------------------------------------------
377# users procedures for [ trombi.php ]
378#
379
380def get_members_limit(userdesc,perms,vhost,listname,page,nb_per_page):
381 try:
382 members = get_members(userdesc,perms,vhost,listname.lower())[1]
383 except:
384 return 0
385 i = int(page) * int(nb_per_page)
386 return (len(members), members[i:i+int(nb_per_page)])
387
388def get_owners(userdesc,perms,vhost,listname):
389 try:
390 details,members,owners = get_members(userdesc,perms,vhost,listname.lower())
391 except:
392 return 0
393 return (details,owners)
394
395#-------------------------------------------------------------------------------
396# owners procedures [ admin.php ]
397#
398
399def mass_subscribe(userdesc,perms,vhost,listname,users):
400 try:
401 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
402 except:
403 return 0
404 try:
405 if not is_admin_on(userdesc, perms, mlist):
406 return 0
407
408 members = mlist.getRegularMemberKeys()
409 added = []
410 mlist.Lock()
411 for user in users:
412 email, name = to_forlife(user)
413 if ( email is None ) or ( email in members ):
414 continue
415 userd = UserDesc(email, name, None, 0)
416 mlist.ApprovedAddMember(userd)
417 added.append( (quote(userd.fullname), userd.address) )
418 mlist.Save()
419 except:
420 pass
421 mlist.Unlock()
422 return added
423
424def mass_unsubscribe(userdesc,perms,vhost,listname,users):
425 try:
426 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
427 except:
428 return 0
429 try:
430 if not is_admin_on(userdesc, perms, mlist):
431 return 0
432
433 mlist.Lock()
434 map(lambda user: mlist.ApprovedDeleteMember(user), users)
435 mlist.Save()
436 except:
437 pass
438 mlist.Unlock()
439 return users
440
441def add_owner(userdesc,perms,vhost,listname,user):
442 try:
443 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
444 except:
445 return 0
446 try:
447 if not is_admin_on(userdesc, perms, mlist):
448 return 0
449 email = to_forlife(user)[0]
450 if email is None:
451 return 0
452 if email not in mlist.owner:
453 mlist.Lock()
454 mlist.owner.append(email)
455 mlist.Save()
456 except:
457 pass
458 mlist.Unlock()
459 return True
460
461def del_owner(userdesc,perms,vhost,listname,user):
462 try:
463 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
464 except:
465 return 0
466 try:
467 if not is_admin_on(userdesc, perms, mlist):
468 return 0
469 if len(mlist.owner) < 2:
470 return 0
471 mlist.Lock()
472 mlist.owner.remove(user)
473 mlist.Save()
474 except:
475 pass
476 mlist.Unlock()
477 return True
478
479#-------------------------------------------------------------------------------
480# owners procedures [ admin.php ]
481#
482
483def get_pending_ops(userdesc,perms,vhost,listname):
484 try:
485 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
486 except:
487 return 0
488 try:
489 if not is_admin_on(userdesc, perms, mlist):
490 return 0
491
492 mlist.Lock()
493
494 subs = []
495 seen = []
496 dosave = False
497 for id in mlist.GetSubscriptionIds():
498 time, addr, fullname, passwd, digest, lang = mlist.GetRecord(id)
499 if addr in seen:
500 mlist.HandleRequest(id, mm_cfg.DISCARD)
501 dosave = True
502 continue
503 seen.append(addr)
504 try:
505 login = re.match("^[^.]*\.[^.]*\.\d\d\d\d$", addr.split('@')[0]).group()
506 subs.append({'id': id, 'name': quote(fullname), 'addr': addr, 'login': login })
507 except:
508 subs.append({'id': id, 'name': quote(fullname), 'addr': addr })
509
510 helds = []
511 for id in mlist.GetHeldMessageIds():
512 ptime, sender, subject, reason, filename, msgdata = mlist.GetRecord(id)
513 try:
514 size = os.path.getsize(os.path.join(mm_cfg.DATA_DIR, filename))
515 except OSError, e:
516 if e.errno <> errno.ENOENT: raise
517 continue
518 helds.append({
519 'id' : id,
520 'sender': quote(sender, True),
521 'size' : size,
522 'subj' : quote(subject, True),
523 'stamp' : ptime
524 })
525 if dosave: mlist.Save()
526 mlist.Unlock()
527 except:
528 mlist.Unlock()
529 return 0
530 return (subs,helds)
531
532
533def handle_request(userdesc,perms,vhost,listname,id,value,comment):
534 try:
535 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
536 except:
537 return 0
538 try:
539 if not is_admin_on(userdesc, perms, mlist):
540 return 0
541 mlist.Lock()
542 mlist.HandleRequest(int(id),int(value),comment)
543 mlist.Save()
544 mlist.Unlock()
545 return 1
546 except:
547 mlist.Unlock()
548 return 0
549
550
551def get_pending_mail(userdesc,perms,vhost,listname,id,raw=0):
552 try:
553 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
554 except:
555 return 0
556 try:
557 if not is_admin_on(userdesc, perms, mlist):
558 return 0
559 mlist.Lock()
560 ptime, sender, subject, reason, filename, msgdata = mlist.GetRecord(int(id))
561 fpath = os.path.join(mm_cfg.DATA_DIR, filename)
562 size = os.path.getsize(fpath)
563 msg = readMessage(fpath)
564 mlist.Unlock()
565
566 if raw:
567 return str(msg)
568 results = []
569 for part in typed_subpart_iterator(msg,'text','plain'):
570 c = part.get_payload()
571 if c is not None: results.append (c)
572 results = map(lambda x: quote(x), results)
573 return {'id' : id,
574 'sender': quote(sender, True),
575 'size' : size,
576 'subj' : quote(subject, True),
577 'stamp' : ptime,
578 'parts' : results }
579 except:
580 mlist.Unlock()
581 return 0
582
583#-------------------------------------------------------------------------------
584# owner options [ options.php ]
585#
586
587owner_opts = ['accept_these_nonmembers', 'admin_notify_mchanges', 'description', \
588 'default_member_moderation', 'generic_nonmember_action', 'info', \
589 'subject_prefix', 'goodbye_msg', 'send_goodbye_msg', 'subscribe_policy', \
590 'welcome_msg']
591
592def get_owner_options(userdesc,perms,vhost,listname):
593 return get_options(userdesc,perms,vhost,listname.lower(),owner_opts)
594
595def set_owner_options(userdesc,perms,vhost,listname,values):
596 return set_options(userdesc,perms,vhost,listname.lower(),owner_opts,values)
597
598def add_to_wl(userdesc,perms,vhost,listname,addr):
599 try:
600 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
601 except:
602 return 0
603 try:
604 if not is_admin_on(userdesc, perms, mlist):
605 return 0
606 mlist.Lock()
607 mlist.accept_these_nonmembers.append(addr)
608 mlist.Save()
609 mlist.Unlock()
610 return 1
611 except:
612 mlist.Unlock()
613 return 0
614
615def del_from_wl(userdesc,perms,vhost,listname,addr):
616 try:
617 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
618 except:
619 return 0
620 try:
621 if not is_admin_on(userdesc, perms, mlist):
622 return 0
623 mlist.Lock()
624 mlist.accept_these_nonmembers.remove(addr)
625 mlist.Save()
626 mlist.Unlock()
627 return 1
628 except:
629 mlist.Unlock()
630 return 0
631
632def get_bogo_level(userdesc,perms,vhost,listname):
633 try:
634 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
635 except:
636 return 0
637 try:
638 if not is_admin_on(userdesc, perms, mlist):
639 return 0
640 if mlist.header_filter_rules == []:
641 return 0
642 action = mlist.header_filter_rules[0][1]
643 if action == mm_cfg.HOLD:
644 return 1
645 if action == mm_cfg.DISCARD:
646 return 2
647 except:
648 return 0
649
650def set_bogo_level(userdesc,perms,vhost,listname,level):
651 try:
652 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
653 except:
654 return 0
655 try:
656 if not is_admin_on(userdesc, perms, mlist):
657 return 0
658 hfr = []
659 if int(level) is 1:
660 hfr.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg.HOLD, False))
661 elif int(level) is 2:
662 hfr.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg.DISCARD, False))
663 if mlist.header_filter_rules != hfr:
664 mlist.Lock()
665 mlist.header_filter_rules = hfr
666 mlist.Save()
667 mlist.Unlock()
668 return 1
669 except:
670 mlist.Unlock()
671 return 0
672
673#-------------------------------------------------------------------------------
674# admin procedures [ soptions.php ]
675#
676
677admin_opts = [ 'advertised', 'archive', \
678 'max_message_size', 'msg_footer', 'msg_header']
679
680def get_admin_options(userdesc,perms,vhost,listname):
681 if perms != 'admin':
682 return 0
683 return get_options(userdesc,perms,vhost,listname.lower(),admin_opts)
684
685def set_admin_options(userdesc,perms,vhost,listname,values):
686 if perms != 'admin':
687 return 0
688 return set_options(userdesc,perms,vhost,listname.lower(),admin_opts,values)
689
690#-------------------------------------------------------------------------------
691# admin procedures [ check.php ]
692#
693
694check_opts = {
695 'acceptable_aliases' : '',
696 'admin_immed_notify' : True,
697 'administrivia' : True,
698 'anonymous_list' : False,
699 'autorespond_admin' : False,
700 'autorespond_postings' : False,
701 'autorespond_requests' : False,
702 'available_languages' : ['fr'],
703 'ban_list' : [],
704 'bounce_matching_headers' : '',
705 'bounce_processing' : False,
706 'convert_html_to_plaintext' : False,
707 'digestable' : False,
708 'digest_is_default' : False,
709 'discard_these_nonmembers' : [],
710 'emergency' : False,
711 'encode_ascii_prefixes' : 2,
712 'filter_content' : False,
713 'first_strip_reply_to' : False,
714 'forward_auto_discards' : True,
715 'hold_these_nonmembers' : [],
716 'host_name' : 'listes.polytechnique.org',
717 'include_list_post_header' : False,
718 'include_rfc2369_headers' : False,
719 'max_num_recipients' : 0,
720 'new_member_options' : 256,
721 'nondigestable' : True,
722 'obscure_addresses' : True,
723 'preferred_language' : 'fr',
724 'reject_these_nonmembers' : [],
725 'reply_goes_to_list' : 0,
726 'reply_to_address' : '',
727 'require_explicit_destination' : False,
728 'send_reminders' : 0,
729 'send_welcome_msg' : True,
730 'topics_enabled' : False,
731 'umbrella_list' : False,
732 'unsubscribe_policy' : 0,
733}
734
735def check_options(userdesc,perms,vhost,listname,correct=False):
736 try:
737 mlist = MailList.MailList(vhost+VHOST_SEP+listname.lower(),lock=0)
738 except:
739 return 0
740 try:
741 if perms != 'admin': return 0
742 if correct:
743 mlist.Lock()
744 options = { }
745 for (k,v) in check_opts.iteritems():
746 if mlist.__dict__[k] != v:
747 options[k] = v,mlist.__dict__[k]
748 if correct: mlist.__dict__[k] = v
749 if mlist.real_name.lower() != listname:
750 options['real_name'] = listname, mlist.real_name
751 if correct: mlist.real_name = listname
752 if correct:
753 mlist.Save()
754 mlist.Unlock()
755 details = get_list_info(userdesc,perms,mlist)[0]
756 return (details,options)
757 except:
758 if correct: mlist.Unlock()
759 return 0
760
761#-------------------------------------------------------------------------------
762# super-admin procedures
763#
764
765def get_all_lists(userdesc,perms,vhost):
766 prefix = vhost.lower()+VHOST_SEP
767 names = Utils.list_names()
768 names.sort()
769 result = []
770 for name in names:
771 if not name.startswith(prefix):
772 continue
773 result.append(name.replace(prefix,''))
774 return result
775
776def create_list(userdesc,perms,vhost,listname,desc,advertise,modlevel,inslevel,owners,members):
777 if perms != 'admin':
778 return 0
779 name = vhost.lower()+VHOST_SEP+listname.lower();
780 if Utils.list_exists(name):
781 return 0
782
783 owner = []
784 for o in owners:
785 email = to_forlife(o)[0]
786 if email is not None:
787 owner.append(email)
788 if len(owner) is 0:
789 return 0
790
791 mlist = MailList.MailList()
792 try:
793 oldmask = os.umask(002)
794 pw = sha.new('foobar').hexdigest()
795
796 try:
797 mlist.Create(name, owner[0], pw)
798 finally:
799 os.umask(oldmask)
800
801 mlist.real_name = listname
802 mlist.host_name = 'listes.polytechnique.org'
803 mlist.description = desc
804
805 mlist.advertised = int(advertise) is 0
806 mlist.default_member_moderation = int(modlevel) is 2
807 mlist.generic_nonmember_action = int(modlevel) > 0
808 mlist.subscribe_policy = 2 * (int(inslevel) is 1)
809 mlist.admin_notify_mchanges = (mlist.subscribe_policy or mlist.generic_nonmember_action or mlist.default_member_moderation or not mlist.advertised)
810
811 mlist.owner = owner
812
813 mlist.subject_prefix = '['+listname+'] '
814 mlist.max_message_size = 0
815
816 mlist.msg_footer = "_______________________________________________\n" \
817 + "Liste de diffusion %(real_name)s\n"
818
819 mlist.header_filter_rules = []
820 mlist.header_filter_rules.append(('X-Spam-Flag: Yes, tests=bogofilter', mm_cfg.HOLD, False))
821
822 mlist.Save()
823
824 mlist.Unlock()
825
826 check_options(userdesc,perms,vhost,listname.lower(),True)
827 mass_subscribe(userdesc,perms,vhost,listname.lower(),members)
828
829 # avoid the "-1 mail to moderate" bug
830 mlist = MailList.MailList(name)
831 mlist._UpdateRecords()
832 mlist.Save()
833 mlist.Unlock()
834 if ON_CREATE_CMD != '':
835 try: os.system(ON_CREATE_CMD + ' ' + name)
836 except: pass
837 except:
838 try:
839 mlist.Unlock()
840 except:
841 pass
842 return 0
843 return 1
844
845def delete_list(userdesc,perms,vhost,listname,del_archives=0):
846 lname = vhost+VHOST_SEP+listname.lower()
847 try:
848 mlist = MailList.MailList(lname,lock=0)
849 except:
850 return 0
851 try:
852 if not is_admin_on(userdesc, perms, mlist):
853 return 0
854 # remove the list
855 REMOVABLES = [ os.path.join('lists', lname), ]
856 # remove stalled locks
857 for filename in os.listdir(mm_cfg.LOCK_DIR):
858 fn_lname = filename.split('.')[0]
859 if fn_lname == lname:
860 REMOVABLES.append(os.path.join(mm_cfg.LOCK_DIR, filename))
861 # remove archives ?
862 if del_archives:
863 REMOVABLES.extend([
864 os.path.join('archives', 'private', lname),
865 os.path.join('archives', 'private', lname+'.mbox'),
866 os.path.join('archives', 'public', lname),
867 os.path.join('archives', 'public', lname+'.mbox')
868 ])
869 map(lambda dir: remove_it(lname, os.path.join(mm_cfg.VAR_PREFIX, dir)), REMOVABLES)
870 return 1
871 except:
872 return 0
873
874def kill(userdesc,perms,vhost,alias,del_from_promo):
875 exclude = []
876 if not del_from_promo:
877 exclude.append(PLATAL_DOMAIN+VHOST_SEP+'promo'+alias[-4:])
878 for list in Utils.list_names():
879 if list in exclude: continue
880 try:
881 mlist = MailList.MailList(list,lock=0)
882 except:
883 continue
884 try:
885 mlist.Lock()
886 mlist.ApprovedDeleteMember(alias+'@'+PLATAL_DOMAIN,None,0,0)
887 mlist.Save()
888 mlist.Unlock()
889 except:
890 mlist.Unlock()
891 return 1
892
893
894#-------------------------------------------------------------------------------
895# server
896#
897class FastXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
898 allow_reuse_address = True
899
900################################################################################
901#
902# INIT
903#
904#-------------------------------------------------------------------------------
905# use Mailman user and group (not root)
906# fork in background if asked to
907#
908
909uid = getpwnam(mm_cfg.MAILMAN_USER)[2]
910gid = getgrnam(mm_cfg.MAILMAN_GROUP)[2]
911
912if not os.getuid():
913 os.setregid(gid,gid)
914 os.setreuid(uid,uid)
915
916signal.signal(signal.SIGHUP, signal.SIG_IGN)
917
918if ( os.getuid() is not uid ) or ( os.getgid() is not gid):
919 sys.exit(0)
920
921opts, args = getopt.getopt(sys.argv[1:], 'f')
922for o, a in opts:
923 if o == '-f' and os.fork():
924 sys.exit(0)
925
926i18n.set_language('fr')
927mysql = connectDB()
928lock = Lock()
929
930#-------------------------------------------------------------------------------
931# server
932#
933server = FastXMLRPCServer(("localhost", 4949), BasicAuthXMLRPCRequestHandler)
934
935# index.php
936server.register_function(get_lists)
937server.register_function(subscribe)
938server.register_function(unsubscribe)
939# members.php
940server.register_function(get_members)
941# trombi.php
942server.register_function(get_members_limit)
943server.register_function(get_owners)
944# admin.php
945server.register_function(mass_subscribe)
946server.register_function(mass_unsubscribe)
947server.register_function(add_owner)
948server.register_function(del_owner)
949# moderate.php
950server.register_function(get_pending_ops)
951server.register_function(handle_request)
952server.register_function(get_pending_mail)
953# options.php
954server.register_function(get_owner_options)
955server.register_function(set_owner_options)
956server.register_function(add_to_wl)
957server.register_function(del_from_wl)
958server.register_function(get_bogo_level)
959server.register_function(set_bogo_level)
960# soptions.php
961server.register_function(get_admin_options)
962server.register_function(set_admin_options)
963# check.php
964server.register_function(check_options)
965# create + del
966server.register_function(get_all_lists)
967server.register_function(create_list)
968server.register_function(delete_list)
969# utilisateurs.php
970server.register_function(kill)
971
972server.serve_forever()
973
974# vim:set et: