Logo Search packages:      
Sourcecode: jppy version File versions  Download package

mailinglist_sync.py

#
#  mailinglist_sync.py
#
#  Copyright 2002 Nick Piper <nick@nickpiper.co.uk>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU Library General Public License, version 2,
#  as published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  Library General Public License for more details.
#
#  You should have received a copy of the GNU Library General Public License
#  along with this program; if not, write the Free Software Foundation,
#  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
#

# this could do with refactoring, know it does mailman as well as majordomo.
# plus, it sometimes seems to not get a reply from mailman, so it just subs everyone?

import os
import smtplib
import re
import time
import difflib
import sys
import struct
import string
import operator
import jppy
import urllib
import types

    # add this to your .procmailrc !
    ##########################e
    # :0Bi
    # * ^Subject: .*Majortrap - 145532ts3df
    # /home/nicholas/uni/Nightline/current_members.txt
    ##########################

import urllib

class mailman_controller:
    def __init__(self, listname, adminpw, base_url):
        self.listname = listname
        self.adminpw = adminpw
        self.base_url = base_url

    def unsubscribe(self, unsubscribees):
        if type(unsubscribees) != types.ListType:
            unsubscribees = [unsubscribees]

        settings = {"send_unsub_ack_to_this_batch": 0,
                    "send_unsub_notifications_to_list_owner": 1,
                    "setmemberopts_btn": "Submit Your Changes"}

        settings.update({"unsubscribees": "\n".join(unsubscribees),
                         "adminpw": self.adminpw})

        form_url = "%s/admin/%s/members/remove" % (self.base_url, self.listname)

        self.submit_form(form_url, settings)

    def subscribe(self, subscribees, invitation=None):
        if type(subscribees) != types.ListType:
            subscribees = [subscribees]

        settings = {"subscribe_or_invite": 0,
                    "send_welcome_msg_to_this_batch": 0,
                    "send_notifications_to_list_owner": 1,
                    "setmemberopts_btn": "Submit Your Changes"}

        settings.update({"subscribees": "\n".join(subscribees),
                         "adminpw": self.adminpw})

        if invitation:
            settings.update({"invitation": invitation,
                             "send_welcome_msg_to_this_batch": 1})

        form_url = "%s/admin/%s/members/add" % (self.base_url, self.listname)

        self.submit_form(form_url, settings)

    def submit_form(self, url, form):
        data = urllib.urlencode(form)
        socket  = urllib.urlopen(url, data)
        results = socket.read()

        if results.find("admlogin") != -1:
            raise RuntimeError("Wrong password for %s at %s" % (self.listname, self.base_url))
        else:
            return 1

class PostMan:
    def __init__(self, subject, toaddr, return_addr, smtp_server):
        self.subject = subject
        self.smtp = smtplib.SMTP(smtp_server)
        self.toaddr = toaddr
        self.msg = ""
        self.return_addr = return_addr
        
    def send(self, message):
        self.m  = "Subject: %s\nFrom: %s\n\n%s" % (self.subject, self.return_addr, message)
        self.smtp.sendmail(self.return_addr,self.toaddr, self.m)

    def append(self,str):
        self.msg = "%s%s" % (self.msg,str)

    def post(self):
        self.send(self.msg)

    def close(self):
        self.smtp.quit()
            
class MailingListSync(jppy.conduit):
    def __init__(self,mailinglists,mailinglist_email,smtp_server,
                 subject_key,return_addr,current_mem_file,user_email,
                 type="majordomo",base_url=None):
        self.mailinglist_email= mailinglist_email 
        self.mailinglists     = mailinglists
        self.smtp_server      = smtp_server     
        self.subject_key      = subject_key     
        self.return_addr      = return_addr     
        self.current_mem_file = current_mem_file
        self.user_email       = user_email      
        self.type             = type
        self.base_url         = base_url

        if self.type not in ("majordomo","mailman"):
            self.log("Mailinglist sync does not support %s." % self.type)
            self.mailinglists = {}

        if self.type == "mailman" and self.base_url is None:
            raise RuntimeError("Mailman lists require a base_url")

    def __del__(self):
        pass

    def fetch_current_subscriber_lists(self,which_lists,server_address):
        # fetch details from mailinglist
        if os.path.exists(self.current_mem_file):
            os.unlink(self.current_mem_file)
        m = PostMan(self.subject_key, server_address,
                         self.return_addr, self.smtp_server)
        for mailinglist in which_lists:
            if self.type == "majordomo":
                m.append("approve %s who %s\n" % (self.mailinglists[mailinglist]["password"],
                                                  mailinglist))
            elif self.type == "mailman":
                m.append("who %s\n" % (self.mailinglists[mailinglist]["password"]))
        m.post()
        m.close()
        self.log_nocr("Posted request ")
        time.sleep(5)
        n = 0
        while 1:
            self.log_nocr(".")
            if os.path.exists(self.current_mem_file): break
            time.sleep(5)
            n += 1
            if n > 30:
                self.log("Giving up!")
                m = PostMan("Mailinglist Sync Failed", self.user_email,
                                 self.return_addr, self.smtp_server)
                m.send("I didn't get a response from mailing list server.")
                m.close()
                sys.exit(0)
        self.log_nocr("Got answer")
        cmf = open(self.current_mem_file,"r")
        cma = cmf.readlines()
        cmf.close()

        mailinglist = None
        if self.type == "majordomo":
            for line in cma:
                m = re.match("^Members of list '([^']+)':$",line)
                if m and m.group(1) in self.mailinglists.keys():
                    mailinglist = m.group(1)
                elif re.match("^[0-9]+ subscribers$",line):
                    mailinglist = None
                if mailinglist and re.match(".+@.+\..+",line):
                    self.mailinglists[mailinglist]["oldsubscribers"].append(line.strip())
            for mailinglist in self.mailinglists.keys():
                self.mailinglists[mailinglist]["oldsubscribers"].sort()        
        elif self.type == "mailman":
            for line in cma:
                m = re.match("^- Results:$",line)
                if m:
                    mailinglist = which_lists[0]
                elif re.match("^- Done.$",line):
                    mailinglist = None
                if mailinglist and re.match(".+@.+\..+",line):
                    address = line.strip().lower()
                    self.mailinglists[mailinglist]["oldsubscribers"].append(address)
            self.mailinglists[which_lists[0]]["oldsubscribers"].sort()  

    def submit_subscriber_changes(self,which_lists,server_address):
        requests = []
        for mailinglist in which_lists:
            if self.type == "mailman":
                mailman = mailman_controller(mailinglist,
                                             self.mailinglists[mailinglist]["password"],
                                             self.base_url)
            s = difflib.SequenceMatcher(None,
                                self.mailinglists[mailinglist]["oldsubscribers"],
                                self.mailinglists[mailinglist]["subscribers"])
            for opcode in s.get_opcodes():
                if opcode[0] in ("delete","replace"):
                    for name in self.mailinglists[mailinglist]["oldsubscribers"][opcode[1]:opcode[2]]:
                        self.log("Unsub %s" % name)
                        if self.type == "majordomo":
                            requests.append('approve %s unsubscribe %s %s' % (
                                self.mailinglists[mailinglist]["password"],
                                mailinglist, name))
                        elif self.type == "mailman":
                            mailman.unsubscribe(name)
                if opcode[0] in ("insert","replace"):
                    for name in self.mailinglists[mailinglist]["subscribers"][opcode[3]:opcode[4]]:
                        self.log("Sub %s" % name)                        
                        if self.type == "majordomo":
                            requests.append('approve %s subscribe %s %s' % (
                                self.mailinglists[mailinglist]["password"],
                                mailinglist, name))
                        elif self.type == "mailman": 
                            mailman.subscribe(name)

        if self.type == "majordomo":
            m = PostMan("Mailing List Server Changes",server_address,
                             self.return_addr, self.smtp_server)
            m.append("\n".join(requests))
            if len(m.msg) > 0:
                m.post()
                self.log_nocr(", Mailed changes")
            else:
                self.log_nocr(", No changes")
            m.close()                
        

    def post_sync(self):
        #### find out who should be subscribed
        self.log("Mailinglist sync started")
        for mailinglist in self.mailinglists.keys():
            self.mailinglists[mailinglist]["subscribers"]    = []
            self.mailinglists[mailinglist]["oldsubscribers"] = []
            for contact in jppy.addressBook().records():
                for line in [contact.custom1,
                             contact.custom2,
                             contact.custom3,
                             contact.custom4]:
                    if mailinglist in line.split(" ") and contact.email:
                        if self.type == "mailman":
                            address = contact.email.lower()
                        else:
                            address = contact.email
                        self.mailinglists[mailinglist]["subscribers"].append(address)
            self.mailinglists[mailinglist]["subscribers"].sort()


        ### find out who is currently subscribed
        if self.type == "majordomo":
            # majordomo can do many lists in one email
            self.fetch_current_subscriber_lists(self.mailinglists.keys(),
                                                self.mailinglist_email)
            self.submit_subscriber_changes(self.mailinglists.keys(),
                                                self.mailinglist_email)            
        else:
            # other mailing list servers can't
            for list_name in self.mailinglists.keys():
                self.log("(%s)" % list_name)
                self.fetch_current_subscriber_lists([list_name],
                                                    "%s-request%s" % (
                                                        list_name,
                                                        self.mailinglist_email))
                self.submit_subscriber_changes([list_name],
                                               "%s-request%s" % (
                                                   list_name,
                                                   self.mailinglist_email))

        self.log(", sync with mailinglist complete.")


    def log(self,str,sd=None):
        jppy.log("mailinglist: %s" % str,sd=sd)

    def log_nocr(self,str,sd=None):
        jppy.log_nocr(str,sd=sd)        

Generated by  Doxygen 1.6.0   Back to index