Dammit -- wrong version. :-) Sorry. Try this one. -jag
-- Joshua Ginsberg <[EMAIL PROTECTED]> Free Software Foundation - Senior Systems Administrator
diff -rNu mailman-2.1.5.virgin/Mailman/Cgi/RPC2.py mailman-2.1.5/Mailman/Cgi/RPC2.py
--- mailman-2.1.5.virgin/Mailman/Cgi/RPC2.py 1969-12-31 19:00:00.000000000 -0500
+++ mailman-2.1.5/Mailman/Cgi/RPC2.py 2005-10-14 16:25:08.000000000 -0400
@@ -0,0 +1,49 @@
+'''RPC2.py
+
+An XML-RPC listener for interfacing with GNU/Mailman
+
+Joshua Ginsberg <[EMAIL PROTECTED]>
+'''
+
+import os, re, xmlrpclib
+from Mailman import XMLRPC, mm_cfg, MailList, Errors, i18n
+
+# Set up i18n
+_ = i18n._
+i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
+
+def main():
+ #0: Make sure we look like a CGI-like environment
+ try:
+ cgi_version = os.environ['GATEWAY_INTERFACE']
+ except KeyError:
+ raise xmlrpclib.Fault(-32300,
+ _('Listener must be run within a CGI environment.'))
+ if re.match('CGI/[0-9].[0-9]', cgi_version) is None:
+ raise xmlrpclib.Fault(-32300,
+ _('Listener must be run within a CGI environment.'))
+
+ #1: What is the HTTP method?
+ try:
+ cgi_method = os.environ['REQUEST_METHOD']
+ except KeyError:
+ raise xmlrpclib.Fault(-32300,
+ _('No HTTP method was found.'))
+
+ if cgi_method == 'POST':
+
+ #2: Content-type = text/xml
+ try:
+ content_type = os.environ['CONTENT_TYPE']
+ except KeyError:
+ raise xmlrpclib.Fault(-32300,
+ _('Webserver did not provide a content type.'))
+ if content_type != 'text/xml':
+ raise xmlrpclib.Fault(-32300, _('Content type must be text/xml'))
+
+ XMLRPC.handler.handle_request()
+
+ elif cgi_method == 'GET':
+ XMLRPC.handler.handle_get()
+ else:
+ raise xmlrpclib.Fault(-32300, _('Invalid HTTP Method.'))
diff -rNu mailman-2.1.5.virgin/Mailman/XMLRPC.py mailman-2.1.5/Mailman/XMLRPC.py
--- mailman-2.1.5.virgin/Mailman/XMLRPC.py 1969-12-31 19:00:00.000000000 -0500
+++ mailman-2.1.5/Mailman/XMLRPC.py 2005-10-14 16:25:06.000000000 -0400
@@ -0,0 +1,325 @@
+'''XMLRPC
+
+This library implements an XML-RPC interface to the GNU/Mailman list manager
+software (http://list.org).
+
+Joshua Ginsberg <[EMAIL PROTECTED]>
+'''
+
+from Mailman import MailList, mm_cfg, i18n, Errors, Utils
+from Mailman.Logging.Syslog import syslog
+import os, signal, sys, os.path, shutil, xmlrpclib, sha, traceback, cgi
+from DocXMLRPCServer import DocCGIXMLRPCRequestHandler
+
+# Set up i18n
+_ = i18n._
+i18n.set_language(mm_cfg.DEFAULT_SERVER_LANGUAGE)
+
+def __authenhandler__(username, password, siteadmin):
+ '''__authenhandler__(username, password, siteadmin)
+
+ A helper script designed to verify the basic authentication data
+
+ username -- the username provided
+ password -- the password provided
+ siteadmin -- If true, the username must be 'mailman' and the password must
+ be the site administrator password. If false, the username must be the
+ list to work with, and the password must be either the list admin
+ password or the site admin password.
+
+ Raises MMAuthenticationError if the authentication fails. Returns True
+ otherwise.
+ '''
+ if siteadmin and username != 'mailman':
+ raise Errors.MMBadUserError, _('This method requires site admin access.')
+ if not siteadmin and username == 'mailman':
+ raise Errors.MMBadUserError, \
+ _('This method requires a list as a username.')
+ if username == 'mailman':
+ # if username is mailman, the only authentication that can happen is
+ # as site administrator
+ if Utils.check_global_password(password):
+ return True
+ else:
+ raise Errors.MMAuthenticationError, _('Invalid login.')
+ else:
+ try:
+ mlist = MailList.MailList(username, lock=0)
+ except Errors.MMListError, e:
+ # a missing list means a bad username
+ # plus, we don't want to confirm what lists do or don't exist by
+ # doing something different than if the u/p combination is bad
+ syslog('error', _('''Mailing list %s not found''') % (username,))
+ raise Errors.MMAuthenticationError, _('Invalid login.')
+ if mlist.WebAuthenticate((mm_cfg.AuthListAdmin, mm_cfg.AuthSiteAdmin),
+ password):
+ return True
+ else:
+ raise Errors.MMAuthenticationError, _('Invalid login.')
+
+class BoboTheClown(object):
+ '''Bobo the Clown is dumb. Just like this class.'''
+ pass
+
+def add_member(listname, authpassword, address, fullname, password, digest,
+ now):
+ '''add_member(listname, authpassword, address, fullname, password, digest,
+ now):
+
+ Subscribes the email address provided to the mailing list.
+
+ listname -- the name of the list to add a member to
+ authpassword -- the site admin or list admin password for this list
+ address -- the email address to add
+ fullname -- the user's full name
+ password -- the password the user would like to have
+ digest -- True or False implying if they wish to receive batched digest
+ delivery
+ now -- True or False implying whether to bypass normal mailing list rules
+ about confirmation/approval
+
+ Returns True if everything succeeded
+ '''
+
+ __authenhandler__(listname, authpassword, False)
+ ip = os.environ['REMOTE_ADDR']
+
+ mlist = MailList.MailList(listname, lock=0)
+ # switch to mailing list preferred language
+ i18n.set_language(mlist.preferred_language)
+ # taking my cues from other Cgi scripts, notably admin.py
+
+ def freak_out(signum, frame, mlist=mlist):
+ mlist.Unlock()
+ sys.exit(0)
+
+ signal.signal(signal.SIGTERM, freak_out)
+ mlist.Lock()
+ try:
+ bobo = BoboTheClown()
+ for attr in ['address', 'fullname', 'password', 'digest']:
+ if eval(attr) is not None: setattr(bobo, attr, eval(attr))
+ if now:
+ mlist.ApprovedAddMember(bobo, whence='XMLRPC from %s' % (ip,))
+ else:
+ mlist.AddMember(bobo, remote='XMLRPC from %s' % (ip,))
+ mlist.Save()
+ finally:
+ mlist.Unlock()
+ return True
+
+def delete_member(listname, password, email, now):
+ '''delete_member(listname, password, email, now):
+
+ Unsubscribes a member from the list.
+
+ listname -- the name of the list to delete a member from
+ password -- the site admin or list admin password for this list
+ email -- The email address to unsubscribe
+ now -- True or False implying whether to bypass normal mailing list rules
+ about approval
+
+ Returns True if everything succeeded
+ '''
+
+ __authenhandler__(listname, password, False)
+ ip = os.environ['REMOTE_ADDR']
+
+ mlist = MailList.MailList(listname, lock=0)
+ # switch to mailing list preferred language
+ i18n.set_language(mlist.preferred_language)
+ # taking my cues from other Cgi scripts, notably admin.py
+
+ def freak_out(signum, frame, mlist=mlist):
+ mlist.Unlock()
+ sys.exit(0)
+
+ signal.signal(signal.SIGTERM, freak_out)
+ mlist.Lock()
+ try:
+ if now:
+ mlist.ApprovedDeleteMember(email, whence='XMLRPC from %s' % (ip,))
+ else:
+ mlist.DeleteMember(email, whence='XMLRPC from %s' % (ip,))
+ mlist.Save()
+ finally:
+ mlist.Unlock()
+ return True
+
+def change_address(listname, password, old, new, keepold):
+ '''change_address(listname, password, old, new, keepold):
+
+ Changes an existing member's email address.
+
+ listname -- the name of the list to update
+ password -- the site admin or list admin password for this list
+ old -- the old address
+ new -- the new address
+ keepold -- True or False whether to keep the old address subscribed as well
+
+ Returns True if everything succeeded
+ '''
+
+ __authenhandler__(listname, password, False)
+ ip = os.environ['REMOTE_ADDR']
+
+ mlist = MailList.MailList(listname, lock=0)
+ # switch to mailing list preferred language
+ i18n.set_language(mlist.preferred_language)
+ # taking my cues from other Cgi scripts, notably admin.py
+
+ def freak_out(signum, frame, mlist=mlist):
+ mlist.Unlock()
+ sys.exit(0)
+
+ signal.signal(signal.SIGTERM, freak_out)
+ mlist.Lock()
+
+ try:
+ mlist.ApprovedChangeMemberAddress(old, new, keepold)
+ finally:
+ mlist.Unlock()
+ return True
+
+def create_list(site_pw, listname, emaildomain, webhost, moderated, listadmin,
+ listadmin_pw, languages):
+ '''create_list(site_pw, listname, emaildomain, webhost, moderated, listadmin,
+ listadmin_pw, languages):
+
+ Creates a new mailing list.
+
+ site_pw -- the site administrator password
+ listname -- the name of the list you wish to create
+ emaildomain -- the email domain this list will appear to come from
+ webhost -- the hostname used to access the web interface for this list
+ moderated -- True or False implying whether new members of the list are
+ to be moderated by default
+ listadmin -- The list administrator's email address
+ listadmin_pw -- The list administrator's password
+ languages -- a list of two-letter language codes for this list to support
+
+ You must be logged in as the site administrator (no username, site admin
+ password) for this method to be invoked.
+
+ Returns True if everything succeded
+ '''
+
+ __authenhandler__('mailman', site_pw, True)
+ ip = os.environ['REMOTE_ADDR']
+
+ mlist = MailList.MailList()
+ try:
+ def freak_out(signum, frame, mlist=mlist):
+ mlist.Unlock()
+ sys.exit(0)
+
+ signal.signal(signal.SIGTERM, freak_out)
+ listadmin_pw = sha.new(listadmin_pw).hexdigest()
+ # okay, I'm just plagiarizing from Cgi/create.py here
+ oldmask = os.umask(002)
+ try:
+ mlist.Create(listname, listadmin, listadmin_pw, languages)
+ finally:
+ os.umask(oldmask)
+ mlist.default_member_moderation = moderated
+ mlist.web_page_url = mm_cfg.DEFAULT_URL_PATTERN % webhost
+ mlist.host_name = emaildomain
+ mlist.Save()
+ finally:
+ mlist.Unlock()
+ return True
+
+def delete_list(site_pw, archives_too):
+ '''delete_list(archives_too):
+
+ Delete a list and possibly its archives too.
+
+ site_pw -- The site administrator password
+ archives_too -- True or False implying whether to remove the lists archives
+
+ Returns True if everything succeded.
+ '''
+
+
+ __authenhandler__('mailman', site_pw, True)
+ ip = os.environ['REMOTE_ADDR']
+
+ mlist = MailList.MailList(listname, lock=0)
+
+ # pretty much straight ripping off bin/rmlist
+ if mm_cfg.MTA:
+ modname = 'Mailman.MTA.' + mm_cfg.MTA
+ __import__(modname)
+ sys.modules[modname].remove(mlist)
+
+ REMOVABLES = [os.path.join('lists', listname)]
+
+ # Remove any stale locks associated with the list
+ for filename in os.listdir(mm_cfg.LOCK_DIR):
+ fn_listname = filename.split('.')[0]
+ if fn_listname == listname:
+ REMOVABLES.append(os.path.join(mm_cfg.LOCK_DIR, filename))
+ if archives_too:
+ REMOVABLES.extend([
+ os.path.join('archives', 'private', listname),
+ os.path.join('archives', 'private', listname + '.mbox'),
+ os.path.join('archives', 'public', listname),
+ os.path.join('archives', 'public', listname + '.mbox')
+ ])
+
+ for dirtmpl in REMOVABLES:
+ filename = os.path.join(mm_cfg.VAR_PREFIX, dirtmpl)
+ if os.path.islink(filename):
+ os.unlink(filename)
+ elif os.path.isdir(filename):
+ shutil.rmtree(filename)
+ elif os.path.isfile(filename):
+ os.unlink(filename)
+ return True
+
+def __method_wrapper__(method_func):
+ '''__method_wrapper__(method_func):
+
+ Python's XMLRPC library will catch any exceptions that are not of the class
+ xmlrpclib.Fault and wrap then as xmlrpclib.Fault exceptions with an error
+ code of 1. However, for XMLRPC Fault Interoperability, the code needs to be
+ -32500. This function wraps method execution, catches Mailman errors, and
+ raises them as xmlrpclib.Fault errors with the appropriate Fault code.
+ '''
+
+ def foo_lambda(*args):
+ try:
+ return method_func(*args)
+ except Exception, e:
+ tb_type, tb_value, tb = sys.exc_info()
+ # default application error code is -32500
+ code = -32500
+ if tb_type == Errors.MMSubscribeNeedsConfirmation:
+ code = -32501
+ elif tb_type == Errors.MMNeedApproval:
+ code = -32502
+ elif tb_type == Errors.MMAuthenticationError:
+ code = -32503
+ error_message = str(e) + str(traceback.format_tb(tb))
+ error_message = cgi.escape(error_message)
+ raise xmlrpclib.Fault(code, error_message)
+ foo_lambda.__doc__ = method_func.__doc__
+ return foo_lambda
+
+# Set up the XMLRPC handler
+handler = DocCGIXMLRPCRequestHandler()
+handler.set_server_title(_('GNU/Mailman List Administration Interface'))
+handler.set_server_name(_('GNU/Mailman List Administration Interface'))
+handler.set_server_documentation = _(__doc__)
+handler.register_introspection_functions()
+
+__methodMap__ = {\
+ 'Mailman.addMember': add_member,
+ 'Mailman.deleteMember': delete_member,
+ 'Mailman.changeMemberAddress': change_address,
+ 'Mailman.createList': create_list,
+ 'Mailman.deleteList': delete_list}
+
+for method in __methodMap__.keys():
+ func = __methodMap__[method]
+ handler.register_function(__method_wrapper__(func), method)
diff -rNu mailman-2.1.5.virgin/src/Makefile.in mailman-2.1.5/src/Makefile.in
--- mailman-2.1.5.virgin/src/Makefile.in 2003-03-31 14:27:14.000000000 -0500
+++ mailman-2.1.5/src/Makefile.in 2005-10-14 16:25:10.000000000 -0400
@@ -71,7 +71,7 @@
# Fixed definitions
CGI_PROGS= admindb admin confirm create edithtml listinfo options \
- private rmlist roster subscribe
+ private rmlist roster subscribe RPC2
COMMONOBJS= common.o vsnprintf.o
signature.asc
Description: This is a digitally signed message part
_______________________________________________ Mailman-Developers mailing list [email protected] http://mail.python.org/mailman/listinfo/mailman-developers Mailman FAQ: http://www.python.org/cgi-bin/faqw-mm.py Searchable Archives: http://www.mail-archive.com/mailman-users%40python.org/ Unsubscribe: http://mail.python.org/mailman/options/mailman-developers/archive%40jab.org Security Policy: http://www.python.org/cgi-bin/faqw-mm.py?req=show&file=faq01.027.htp
