[Spambayes-checkins] spambayes/windows autoconfigure.py,NONE,1.1

Tony Meyer anadelonbrin at users.sourceforge.net
Tue Sep 2 00:48:58 EDT 2003


Update of /cvsroot/spambayes/spambayes/windows
In directory sc8-pr-cvs1:/tmp/cvs-serv24581/windows

Added Files:
	autoconfigure.py 
Log Message:
This script will read the configuration file for a mail client and changes it to use
the spambayes pop3/smtp proxies (or imapfilter at some point), and sets up
the bayescustomize.ini file to the correct servers/ports.  Where possible, it
also creates filtering rules as well.

Currently works for Eudora 5.2 (including the filtering rule), Mozilla 1.3 Mail,
and Opera 7.11 Mail (M2).  Outlook Express is the next (much more difficult)
target.

--- NEW FILE: autoconfigure.py ---
#!/usr/bin/env python

"""Automatically set up the user's mail client and SpamBayes.

Currently works with:
 o Eudora (POP3/SMTP only)
 o Mozilla Mail (POP3/SMTP only)
 o Opera Mail (M2) (POP3/SMTP only)

To do:
 o Establish which mail client(s) are to be setup.
 o Locate the appropriate configuration directory
   (e.g. for Eudora this is probably either the application directory,
   or c:\documents and settings\username\application data\qualcomm\eudora,
   i.e. sh_appdata\qualcomm\eudora)
 o This will create some unnecessary proxies in some cases.  For example,
   if I have my client set up to get mail from pop.example.com for the
   user 'tmeyer' and the user 'tonym', two proxies will be created, but
   only one is necessary.  We should check the existing proxies before
   adding a new one.
 o Figure out Outlook Express's pop3uidl.dbx file and how to hook into it
   (use the oe_mailbox.py module)
 o Other mail clients?  Other platforms?
 o This won't work all that well if multiple mail clients are used (they
   will end up trying to use the same ports).  In such a case, we really
   need to keep track of if the server is being proxied already, and
   reuse ports, but this is complicated.
 o We currently don't make any moves to protect the original file, so if
   something does wrong, it's corrupted.  We also write into the file,
   rather than a temporary one and then copy across.  This should all be
   fixed.
 o Suggestions?
"""

# This module is part of the spambayes project, which is Copyright 2002-3
# The Python Software Foundation and is covered by the Python Software
# Foundation license.

__author__ = "Tony Meyer <ta-meyer at ihug.co.nz>"
__credits__ = "All the Spambayes folk."

try:
    True, False
except NameError:
    # Maintain compatibility with Python 2.2
    True, False = 1, 0

## Tested with:
## o Eudora 5.2 on Windows XP
## o Mozilla 1.3 on Windows XP
## o Opera 7.11 on Windows XP

import re
import os
import types
import socket
import StringIO
import ConfigParser

from spambayes.Options import options, optionsPathname

def move_to_next_free_port(port):
    # Increment port until we get to one that isn't taken
    # I doubt this will work if there is a firewall that prevents
    # localhost connecting to particular ports, but I'm not sure
    # how else we can do this - Richie says that bind() doesn't
    # necessarily fail if the port is already bound.
    while True:
        try:
            port += 1
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.connect(("127.0.0.1", port))
            s.close()
        except socket.error:
            return port

# Let's be safe and use high ports, starting at 1110 and 1025, and going up
# as required.
pop_proxy_port = move_to_next_free_port(1109)
smtp_proxy_port = move_to_next_free_port(1024)

def configure_eudora(config_location):
    """Configure Eudora to use the SpamBayes POP3 and SMTP proxies, and
    configure SpamBayes to proxy the servers that Eudora was connecting to.
    """
    ini_filename = "%s%seudora.ini" % (config_location, os.sep)
    c = ConfigParser.ConfigParser()
    c.read(ini_filename)

    translate = {("PopServer", "POPPort") : "pop3proxy",
                 ("SMTPServer", "SMTPPort") : "smtpproxy",
                 }

    pop_proxy = pop_proxy_port
    smtp_proxy = smtp_proxy_port

    for sect in c.sections():
        if sect.startswith("Persona-") or sect == "Settings":
            if c.get(sect, "UsesIMAP") == "0":
                # Eudora stores the POP3 server name in two places.
                # Why?  Who knows?  We do the popaccount one
                # separately, because it also has the username.
                p = c.get(sect, "popaccount")
                c.set(sect, "popaccount", "%s at localhost" % \
                      (p[:p.index('@')],))
                for ((eud_name, eud_port), us_name in translate.items():
                    try:
                        port = c.get(sect, eud_port)
                    except ConfigParser.NoOptionError:
                        port = None
                        
                    if us_name.lower()[:4] == "pop3":
                        if port is None:
                            port = 110
                        pop_proxy = move_to_next_free_port(pop_proxy)
                        proxy_port = pop_proxy
                    else:
                        if port is None:
                            port = 25
                        smtp_proxy = move_to_next_free_port(smtp_proxy)
                        proxy_port = smtp_proxy
                    server = "%s:%s" % (c.get(sect, eud_name), port)
                    options[us_name, "remote_servers"] += (server,)
                    options[us_name, "listen_ports"] += (proxy_port,)
                    if options["globals", "verbose"]:
                        print "[%s] Proxy %s on localhost:%s" % \
                              (sect, server, proxy_port)
                    c.set(sect, eud_name, "localhost")
                    c.set(sect, eud_port, proxy_port)
            else:
                # Setup imapfilter instead
                pass

    out = file(ini_filename, "w")
    c.write(out)
    out.close()
    options.update_file(optionsPathname)

    # Setup filtering rule
    # This assumes that the spam and unsure folders already exist!
    # (Creating them shouldn't be that difficult - it's just a mbox file,
    # and I think the .toc file is automatically created).  Left for
    # another day, however.
    filter_filename = "%s%sFilters.pce" % (config_location, os.sep)
    spam_folder_name = "Junk Mail"
    unsure_folder_name = "Possible Junk"
    header_name = options["Headers", "classification_header_name"]
    spam_tag = options["Headers", "header_spam_string"]
    unsure_tag = options["Headers", "header_unsure_string"]
    # We are assuming that a rules file already exists, otherwise there
    # is a bit more to go at the top.
    filter_rules = "rule SpamBayes-Spam\n" \
                   "transfer %s.mbx\n" \
                   "incoming\n" \
                   "header %s\n" \
                   "verb contains\n" \
                   "value %s\n" \
                   "conjunction ignore\n" \
                   "header \n" \
                   "verb contains\n" \
                   "value \n" \
                   "rule SpamBayes-Unsure\n" \
                   "transfer %s.mbx\n" \
                   "incoming\n" \
                   "header %s\n" \
                   "verb contains\n" \
                   "value %s\n" \
                   "conjunction ignore\n" \
                   "header \n" \
                   "verb contains\n" \
                   "value \n" % (spam_folder_name, header_name, spam_tag,
                                 unsure_folder_name, header_name, unsure_tag)
    filter_file = file(filter_filename, "a")
    filter_file.write(filter_rules)
    filter_file.close()

def configure_mozilla(config_location):
    """Configure Mozilla to use the SpamBayes POP3 and SMTP proxies, and
    configure SpamBayes to proxy the servers that Mozilla was connecting
    to."""
    prefs_file = file("%s%sprefs.js" % (config_location, os.sep), "r")
    prefs = prefs_file.read()
    prefs_file.close()
    save_prefs = prefs
    pop_accounts = {}
    smtp_accounts = {}
    
    r = re.compile(r"user_pref\(\"mail.server.server(\d+).(real)?hostname\", \"([^\"]*)\"\);")
    current_pos = 0
    while True:
        m = r.search(prefs[current_pos:])
        if not m:
            break
        server_num = m.group(1)
        real = m.group(2) or ''
        server = m.group(3)
        current_pos += m.end()
        old_pref = 'user_pref("mail.server.server%s.%shostname", "%s");' % \
                   (server_num, real, server)

        # Find the port, if there is one
        port_string = 'user_pref("mail.server.server%s.port", ' % \
                      (server_num,)
        port_loc = prefs.find(port_string)
        if port_loc == -1:
            port = "110"
            old_port = None
        else:
            loc_plus_len = port_loc + len(port_string)
            end_of_number = loc_plus_len + prefs[loc_plus_len:].index(')')
            port = prefs[loc_plus_len : end_of_number]
            old_port = "%s%s);" % (port_string, port)

        # Find the type of connection
        type_string = 'user_pref("mail.server.server%s.type", "' % \
                      (server_num,)
        type_loc = prefs.find(type_string)
        if type_loc == -1:
            # no type, so ignore this one
            continue
        type_loc += len(type_string)
        account_type = prefs[type_loc : \
                             type_loc + prefs[type_loc:].index('"')]
        
        if account_type == "pop3":
            new_pref = 'user_pref("mail.server.server%s.%shostname", ' \
                       '"127.0.0.1");' % (server_num, real)
            if not pop_accounts.has_key(server_num) or real:
                pop_accounts[server_num] = (new_pref, old_pref,
                                            old_port, server, port)
        elif account_type == "imap":
            # Setup imapfilter instead
            pass

    proxy_port = pop_proxy_port
    for num, (pref, old_pref, old_port, server, port) in pop_accounts.items():
        server = "%s:%s" % (server, port)
        proxy_port = move_to_next_free_port(proxy_port)
        port_pref = 'user_pref("mail.server.server%s.port", %s);' % \
                    (num, proxy_port)
        options["pop3proxy", "remote_servers"] += (server,)
        options["pop3proxy", "listen_ports"] += (proxy_port,)
        if old_port is None:
            pref = "%s\n%s" % (pref, port_pref)
        else:
            save_prefs = save_prefs.replace(old_port, port_pref)
        save_prefs = save_prefs.replace(old_pref, pref)
        if options["globals", "verbose"]:
            print "[%s] Proxy %s on localhost:%s" % \
                  (num, server, proxy_port)

    # Do the SMTP server.
    # Mozilla recommends that only advanced users setup more than one,
    # so we'll just set that one up.  Advanced users can setup SpamBayes
    # themselves <wink>.
    prefs = save_prefs
    r = re.compile(r"user_pref\(\"mail.smtpserver.smtp(\d+).hostname\", \"([^\"]*)\"\);")
    current_pos = 0
    while True:
        m = r.search(prefs[current_pos:])
        if not m:
            break
        current_pos = m.end()
        server_num = m.group(1)
        server = m.group(2)
        old_pref = 'user_pref("mail.smtpserver.smtp%s.hostname", ' \
                   '"%s");' % (server_num, server)
        new_pref = 'user_pref("mail.smtpserver.smtp%s.hostname", ' \
                   '"127.0.0.1");' % (server_num,)

        # Find the port        
        port_string = 'user_pref("mail.smtpserver.smtp1.port", '
        port_loc = prefs.find(port_string)
        if port_loc == -1:
            port = "25"
            old_port = None
        else:
            loc_plus_len = port_loc + len(port_string)
            end_of_number = loc_plus_len + prefs[loc_plus_len:].index(')')
            port = prefs[loc_plus_len : end_of_number]
            old_port = 'user_pref("mail.smtpserver.smtp%s.port", %s);' % \
                       (server_num, port)
        smtp_accounts[server_num] = (new_pref, old_pref, old_port,
                                     server, port)

    proxy_port = smtp_proxy_port
    for num, (pref, old_pref, old_port, server, port) in smtp_accounts.items():
        server = "%s:%s" % (server, port)
        proxy_port = move_to_next_free_port(proxy_port)
        port_pref = 'user_pref("mail.smtpserver.smtp%s.port", %s);' % \
                    (num, proxy_port)
        options["smtpproxy", "remote_servers"] += (server,)
        options["smtpproxy", "listen_ports"] += (proxy_port,)
        if old_port is None:
            pref = "%s\n%s" % (pref, port_pref)
        else:
            save_prefs = save_prefs.replace(old_port, port_pref)
        save_prefs = save_prefs.replace(old_pref, pref)
        if options["globals", "verbose"]:
            print "[%s] Proxy %s on localhost:%s" % \
                  (num, server, proxy_port)

    prefs_file = file("%s%sprefs.js" % (config_location, os.sep), "w")
    prefs_file.write(save_prefs)
    prefs_file.close()
    options.update_file(optionsPathname)

    # Setup filtering rules.
    # Assumes that the folders already exist!  I don't know how difficult
    # it would be to create new Mozilla mail folders.
    filter_filename = "%s%smsgFilterRules.dat" % (config_location, os.sep)
    store_name = "" # how do we get this?
    spam_folder_url = "mailbox:////%s//Junk%20Mail" % (store_name,)
    unsure_folder_url = "mailbox:////%s//Possible%20Junk" % (store_name,)
    header_name = options["Headers", "classification_header_name"]
    spam_tag = options["Headers", "header_spam_string"]
    unsure_tag = options["Headers", "header_unsure_string"]
    rule = 'name="SpamBayes-Spam"\n' \
           'enabled="yes"\n' \
           'type="1"\n' \
           'action="Move to folder"\n' \
           'actionValue="%s"\n' \
           'condition="OR (\"%s\",contains,%s)"\n' \
           'name="SpamBayes-Unsure"\n' \
           'enabled="yes"\n' \
           'type="1"\n' \
           'action="Move to folder"\n' \
           'actionValue="%s"\n' \
           'condition="OR (\"%s\",contains,%s)"\n' % \
           (spam_folder_url, header_name, spam_tag,
            unsure_folder_url, header_name, unsure_tag)
    # This should now be written to the file, but I'm not sure how we
    # determine which subdirectory it goes into - does it have to go
    # into them all?
    # We are assuming that a rules file already exists, otherwise there
    # is a bit more to go at the top.

def configure_m2(config_location):
    """Configure M2 (Opera's mailer) to use the SpamBayes POP3 and SMTP
    proxies, and configure SpamBayes to proxy the servers that M2 was
    connecting to."""
    ini_filename = os.path.join(config_location, "Mail", "accounts.ini")
    ini_file = file(ini_filename, "r")
    faked_up = StringIO.StringIO()
    faked_up.write(";") # Missing at the start
    faked_up.write(ini_file.read())
    faked_up.seek(0)
    ini_file.close()
    c = ConfigParser.ConfigParser()
    c.readfp(faked_up)

    translate = {("Incoming Servername", "Incoming Port") : "pop3proxy",
                 ("Outgoing Servername", "Outgoing Port") : "smtpproxy",
                 }

    pop_proxy = pop_proxy_port
    smtp_proxy = smtp_proxy_port

    for sect in c.sections():
        if sect.startswith("Account") and sect != "Accounts":
            if c.get(sect, "Incoming Protocol") == "POP":
                for (m2_name, m2_port), us_name in translate.items():
                    try:
                        port = c.get(sect, m2_port)
                    except ConfigParser.NoOptionError:
                        port = None
                        
                    if us_name.lower()[:4] == "pop3":
                        if port is None:
                            port = 110
                        pop_proxy = move_to_next_free_port(pop_proxy)
                        proxy_port = pop_proxy
                    else:
                        if port is None:
                            port = 25
                        smtp_proxy = move_to_next_free_port(smtp_proxy)
                        proxy_port = smtp_proxy
                    server = "%s:%s" % (c.get(sect, m2_name), port)
                    options[us_name, "remote_servers"] += (server,)
                    options[us_name, "listen_ports"] += (proxy_port,)
                    if options["globals", "verbose"]:
                        print "[%s] Proxy %s on localhost:%s" % \
                              (sect, server, proxy_port)
                    c.set(sect, m2_name, "localhost")
                    c.set(sect, m2_port, proxy_port)
            elif c.get(sect, "Incoming Protocol") == "IMAP":
                # Setup imapfilter instead
                pass

    out = file(ini_filename, "w")
    c.write(out)
    out.close()
    options.update_file(optionsPathname)

    # Setting up a filter in M2 is very simple, but I'm not sure what the
    # right rule is - M2 doesn't move mail, it just displays a subset.
    # If someone can describe the best all-purpose rule, I'll pop it in
    # here.


if __name__ == "__main__":
    #configure_eudora(eudora_ini_dir)
    #configure_mozilla(mozilla_ini_dir)
    #configure_m2(m2_ini_dir)
    pass





More information about the Spambayes-checkins mailing list