diff options
author | Magnus Hagander | 2008-11-17 13:26:49 +0000 |
---|---|---|
committer | Magnus Hagander | 2008-11-17 13:26:49 +0000 |
commit | 99883c19d1032e18789763fa704951621364b785 (patch) | |
tree | 097454a3011d1078f530ec30e934031585e9fd48 | |
parent | 848b2f5617f0c217dcef82f0161007a9590a9807 (diff) |
Add code to be able to synchronize a majordomo mailinglist
with the owners of the blogs.
git-svn-id: file:///Users/dpage/pgweb/svn-repo/trunk@2291 8f5c7a92-453e-0410-a47f-ad33c8a6b003
-rwxr-xr-x | planet/listsync.py | 160 | ||||
-rw-r--r-- | planet/planet.ini.sample | 6 |
2 files changed, 166 insertions, 0 deletions
diff --git a/planet/listsync.py b/planet/listsync.py new file mode 100755 index 00000000..7e7952e9 --- /dev/null +++ b/planet/listsync.py @@ -0,0 +1,160 @@ +#!/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("http://%s/mj/mj_wwwadm?passw=%s&list=%s&func=who-short" % + (self.mjhost, self.listpwd, self.listname)) + s = f.read() + f.close() + + # 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) + self.__PostMajordomoForm({ + 'func': 'unsubscribe-farewell', + 'victims': victims + }) + + def AddSubscribers(self, add_subscribers): + """ + Add the specified subscribers to the list. + """ + + victims = "\r\n".join(add_subscribers) + self.__PostMajordomoForm({ + '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 + var.update({ + 'list': self.listname, + 'passw': self.listpwd + }) + body = urlencode(var) + + h = httplib.HTTP(self.mjhost) + h.putrequest('POST', '/mj/mj_wwwadm') + h.putheader('host', self.mjhost) + h.putheader('content-type','application/x-www-form-urlencoded') + h.putheader('content-length', str(len(body))) + h.endheaders() + h.send(body) + errcode, errmsg, headers = h.getreply() + if errcode != 200: + print "ERROR: Form returned code %i, message %s" % (errcode, errmsg) + print h.file.read() + 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() + self.fetch_expected_subscribers() + self.diff_subscribers() + self.apply_subscriber_diff() + + 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): + self.mj.RemoveSubscribers(self.remove_subscribers) + print "Removed %i subscribers" % len(self.remove_subscribers) + if len(self.add_subscribers): + self.mj.AddSubscribers(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() + curs.execute(""" +SELECT email FROM planetadmin.auth_user +INNER JOIN planet.feeds ON planetadmin.auth_user.username=planet.feeds.userid +WHERE planet.feeds.approved +""") + self.expected = set([r[0] for r in curs.fetchall()]) + + + +if __name__=="__main__": + c = ConfigParser.ConfigParser() + c.read('planet.ini') + + Synchronizer(c, psycopg2.connect(c.get('planet','db'))).sync() + diff --git a/planet/planet.ini.sample b/planet/planet.ini.sample index 5d9c91d3..60cabc4f 100644 --- a/planet/planet.ini.sample +++ b/planet/planet.ini.sample @@ -2,3 +2,9 @@ [planet] db=dbname=planetbeta host=/tmp/ user=planetbeta + +[list] +server=localhost +listname=planet-subscribers +password=yeahthatssecret + |