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