summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMagnus Hagander2008-11-17 13:26:49 +0000
committerMagnus Hagander2008-11-17 13:26:49 +0000
commit99883c19d1032e18789763fa704951621364b785 (patch)
tree097454a3011d1078f530ec30e934031585e9fd48
parent848b2f5617f0c217dcef82f0161007a9590a9807 (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-xplanet/listsync.py160
-rw-r--r--planet/planet.ini.sample6
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
+