Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 161 lines (128 sloc) 4.25 KB
#!/usr/bin/env python
"""Planet PostgreSQL - list synchronizer
This file contains the functions to synchronize the list of subscribers
to planet with those of a majordomo mailinglist.
Copyright (C) 2008 PostgreSQL Global Development Group
import ConfigParser
import re
import psycopg2
import httplib
from urllib import urlopen, urlencode
class MajordomoInterface:
Simple interface wrapping some majordomo commands through screenscraping
the mj_wwwadm interface.
def __init__(self, confp):
self.mjhost = confp.get('list', 'server')
self.listname = confp.get('list', 'listname')
self.listpwd = confp.get('list', 'password')
def fetch_current_subscribers(self):
Fetch the current list of subscribers by calling out to the majordomo server
and screenscrape the result of the 'who-short' command.
f = urlopen("https://%s/mj/mj_wwwadm?passw=%s&list=%s&func=who-short" %
(self.mjhost, self.listpwd, self.listname))
s =
# Ugly screen-scraping regexp hack
resub = re.compile('list administration<br>\s+</p>\s+<pre>([^<]+)</pre>')
m = resub.findall(s)
if len(m) != 1:
if s.find("<!-- Majordomo who_none format file -->") > 0:
# Nobody on the list yet
return set()
raise Exception("Could not find list of subscribers")
return set([a for a in re.split('[\s\n]+',m[0]) if a])
def RemoveSubscribers(self, remove_subscribers):
Remove the specified subscribers from the list.
victims = "\r\n".join(remove_subscribers)
'func': 'unsubscribe-farewell',
'victims': victims
def AddSubscribers(self, add_subscribers):
Add the specified subscribers to the list.
victims = "\r\n".join(add_subscribers)
'func': 'subscribe-set-welcome',
'victims': victims
def __PostMajordomoForm(self, varset):
Post a fake form to the majordomo mj_wwwadm interface with whatever
variables are specified. Add the listname and password on top of what's
already in the set of variables.
var = varset
'list': self.listname,
'passw': self.listpwd
body = urlencode(var)
h = httplib.HTTPS(self.mjhost)
h.putrequest('POST', '/mj/mj_wwwadm')
h.putheader('host', self.mjhost)
h.putheader('content-length', str(len(body)))
errcode, errmsg, headers = h.getreply()
if errcode != 200:
print "ERROR: Form returned code %i, message %s" % (errcode, errmsg)
raise Exception("Aborting")
class Synchronizer:
Perform the synchronization between the planet database and the
majordomo list.
def __init__(self, c, db):
self.db = db
self.mj = MajordomoInterface(c)
def sync(self):
self.subscribers = self.mj.fetch_current_subscribers()
def diff_subscribers(self):
Generate a list of differences between the current and expected subscribers,
so we know what to modify.
self.remove_subscribers = self.subscribers.difference(self.expected)
self.add_subscribers = self.expected.difference(self.subscribers)
def apply_subscriber_diff(self):
If there are any changes to subscribers to be made (subscribe or unsubscribe),
send these commands to the majordomo admin interface using a http POST
operation with a faked form.
if len(self.remove_subscribers):
print "Removed %i subscribers" % len(self.remove_subscribers)
if len(self.add_subscribers):
print "Added %i subscribers" % len(self.add_subscribers)
def fetch_expected_subscribers(self):
Fetch the list of addresses that *should* be subscribed to the list by
looking in the database.
curs = self.db.cursor()
SELECT DISTINCT email FROM auth_user
WHERE feeds.approved AND NOT feeds.archived
self.expected = set([r[0] for r in curs.fetchall()])
if __name__=="__main__":
c = ConfigParser.ConfigParser()'planet.ini')
Synchronizer(c, psycopg2.connect(c.get('planet','db'))).sync()
Something went wrong with that request. Please try again.