#!/usr/local/bin/python2.3

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

Example usage:
    >>> configure("mailer name")
Where "mailer name" is any of the names below.

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

To do:
 o Establish which mail client(s) are installed in a more clever way.
 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 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.  Richie's suggestion is for the script to create a clone of an
   existing account with the new settings.  Then people could test the
   cloned account, and if they're happy with it they can either delete
   their old account or delete the new one and run the script again in
   "modify" rather than "clone" mode.  This sounds like a good idea,
   although a lot of work...
 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@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
##  o Outlook Express 6 on Windows XP

import re
import os
import sys
import types
import socket
import shutil
import StringIO
import ConfigParser

try:
    import win32gui
    import win32api
    import win32con
    import pywintypes
    from win32com.shell import shell, shellcon
except ImportError:
    # The ImportError is delayed until these are needed - if we
    # did it here, the functions that don't need these would still
    # fail.  (And having "import win32api" in lots of functions
    # didn't seem to make much sense).
    win32api = win32con = shell = shellcon = win32gui = pywintypes = None

from spambayes import oe_mailbox
from spambayes import OptionsClass
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:
            portStr = str(port)
            if portStr in options["pop3proxy", "listen_ports"] or \
               portStr in options["smtpproxy", "listen_ports"]:
                continue
            else:
                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

    results = []
    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 cares.  We do the popaccount one
                # separately, because it also has the username.
                p = c.get(sect, "popaccount")
                c.set(sect, "popaccount", "%s@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,)
                    results.append("[%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"
    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()
    return results

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
    results = []
    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)
        results.append("[%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)
        results.append("[%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.
    return results

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

    results = []
    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,)
                    results.append("[%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.
    return results

def configure_outlook_express(unused):
    """Configure OE to use the SpamBayes POP3 and SMTP proxies, and
    configure SpamBayes to proxy the servers that OE was connecting to."""
    # Requires win32all to be available (or for someone to write a
    # Mac version <wink>)
    if win32api is None:
        raise ImportError("win32 extensions required")

    accounts = oe_mailbox.OEAccountKeys()

    translate = {("POP3 Server", "POP3 Port") : "pop3proxy",
                 ("SMTP Server", "SMTP Port") : "smtpproxy",
                 }

    pop_proxy = pop_proxy_port
    smtp_proxy = smtp_proxy_port

    results = []
    for proto, subkey, account in accounts:
        if proto == "POP3":
            for (server_key, port_key), sect in translate.items():
                server = "%s:%s" % (account[server_key][0],
                                    account[port_key][0])
                if sect[:4] == "pop3":
                    pop_proxy = move_to_next_free_port(pop_proxy)
                    proxy = pop_proxy
                else:
                    smtp_proxy = move_to_next_free_port(smtp_proxy)
                    proxy = smtp_proxy
                options[sect, "remote_servers"] += (server,)
                options[sect, "listen_ports"] += (proxy,)
                win32api.RegSetValueEx(subkey, server_key, 0,
                                       win32con.REG_SZ, "127.0.0.1")
                win32api.RegSetValueEx(subkey, port_key, 0,
                                       win32con.REG_SZ, str(proxy))
                results.append("[%s] Proxy %s on localhost:%s" % \
                               (account["Account Name"][0], server, proxy))
        elif proto == "IMAP4":
            # Setup imapfilter instead.
            pass

    options.update_file(optionsPathname)

    # Outlook Express rules are done in much the same way.  Should one
    # be set up to work with notate_to or notate_subject?  (and set that
    # option, obviously)
    return results

def configure_pegasus_mail(config_location):
    """Configure Pegasus Mail to use the SpamBayes POP3 and SMTP proxies,
    and configure SpamBayes to proxy the servers that Pegasus Mail was
    connecting to."""

    # We can't use ConfigParser here, as we want 'surgical' editing,
    # so we want to use out OptionsClass.  There is the additional trouble
    # that the Pegasus Mail config file doesn't have a section header.

    pop_proxy = pop_proxy_port
    smtp_proxy = smtp_proxy_port

    results = []
    for filename in os.listdir(config_location):
        if filename.lower().startswith("pop") or filename.lower().startswith("smt"):
            full_filename = os.path.join(config_location, filename)
            working_filename = "%s.tmp" % (filename, )
            shutil.copyfile(filename, working_filename)
            c = OptionsClass.OptionsClass()
            c.merge_file(working_filename)
            server = "%s:%s" % (c.get("all", "host"), c.get("all", "port"))
            if filename[:3] == "pop":
                pop_proxy = move_to_next_free_port(pop_proxy)
                proxy = pop_proxy
                sect = "pop3proxy"
            else:
                smtp_proxy = move_to_next_free_port(smtp_proxy)
                proxy = smtp_proxy
                sect = "smtpproxy"
            options[sect, "remote_servers"] += (server,)
            options[sect, "listen_ports"] += (proxy,)
            # Write in the new options!!
            c.set("all", "host", "127.0.0.1")
            c.set("all", "port", proxy)
            c.update_file(working_filename)
            results.append("[%s] Proxy %s on localhost:%s" % \
                           (c.get("all", "title"), server, proxy))
        elif filename.lower() == "IMAP.PM":
            # Setup imapfilter instead.
            pass

    # Pegasus Mail has a 'weight' system for determining junk mail.
    # The best plan would probably be to just add to this.  Something like:
    rules_filename = os.path.join(config_location, "spambust.dat")
    header_name = options["Headers", "classification_header_name"]
    spam_tag = options["Headers", "header_spam_string"]
    unsure_tag = options["Headers", "header_unsure_string"]
    ham_tag = options["Headers", "header_ham_string"]
    spam_weight = 500
    ham_weight = -500
    unsure_weight = -50 # leave judgement up to the rest of the rules
    rule = '# SpamBayes adjustments\n' \
           'if header "%s" contains "%s" weight %s\n' \
           'if header "%s" contains "%s" weight %s\n' \
           'if header "%s" contains "%s" wieght %s\n\n' % \
           (header_name, spam_tag, spam_weight,
            header_name, unsure_tag, unsure_weight,
            header_name, ham_tag, ham_weight)
    rules_file = file(rules_filename, "a")
    rules_file.write(rule)
    rules_file.close()
    return results

def pocomail_accounts_filename():
    if win32api is None:
        # If we don't have win32, then we don't know.
        return ""
    key = "Software\\Poco Systems Inc"

    pop_proxy  = pop_proxy_port
    smtp_proxy = smtp_proxy_port

    try:
        reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, key)
    except pywintypes.error:
        # It seems that we don't have PocoMail
        return ""
    else:
        subkey_name   = "%s\\%s" % (key, win32api.RegEnumKey(reg, 0))
        reg           = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER,
                                              subkey_name)
        pocomail_path = win32api.RegQueryValueEx(reg, "Path")[0]

    return os.path.join(pocomail_path, "accounts.ini")

def configure_pocomail(pocomail_accounts_file):
    if os.path.exists(pocomail_accounts_file):
        f = open(pocomail_accounts_file, "r")

        accountName       = ""
        pocomail_accounts = { }

        # Builds the dictionary with all the existing accounts.
        for line in f.readlines():
            line = line.rstrip('\n\r')
            if line == '':
                continue

            if line[0] == '[' and line[-1] == ']':
                accountName = line[1:-1]
                pocomail_accounts[accountName] = { }
            else:
                separator   = line.find('=')
                optionName  = line[:separator]
                optionValue = line[separator + 1:]

                if optionName == "POPServer":
                    pop3 = optionValue.split(':')
                    if len(pop3) == 1:
                        pop3.append(110)
                    server = "%s:%s" % tuple(pop3)

                    proxy     = pop_proxy
                    pop_proxy = move_to_next_free_port(pop_proxy)

                    if not server in options["pop3proxy", "remote_servers"]:
                        options["pop3proxy", "remote_servers"] += (server,)
                        options["pop3proxy", "listen_ports"]   += (proxy,)
                    else:
                        serverIndex = 0
                        for remoteServer in options["pop3proxy",
                                                    "remote_servers"]:
                            if remoteServer == server:
                                break
                            serverIndex += 1
                        proxy = options["pop3proxy", "listen_ports"][serverIndex]

                    optionValue = "%s:%s" % ('localhost', proxy)

                pocomail_accounts[accountName][optionName] = optionValue

        f.close()
        f = open(pocomail_accounts_file, "w")
        for accountName in pocomail_accounts.keys():
            f.write('[' + accountName + ']\n')
            for optionName, optionValue in pocomail_accounts[accountName].items():
                f.write("%s=%s\n" % (optionName, optionValue))
            f.write('\n')
        f.close()

        options.update_file(optionsPathname)

        # Add a filter to pocomail
        pocomail_filters_file = os.path.join(pocomail_path, "filters.ini")

        if os.path.exists(pocomail_filters_file):
            f = open(pocomail_filters_file, "r")

            pocomail_filters = { }
            filterName       = ""

            for line in f.readlines():
                line = line.rstrip('\n\r')
                if line == '': continue

                if line[0] == '[' and line[-1] == ']':
                    filterName = line[1:-1]
                    pocomail_filters[filterName] = []
                elif line[0] != '{':
                    pocomail_filters[filterName].append(line)
            f.close()

            spamBayesFilter = 'spam,X-Spambayes-Classification,move,' \
                              '"Junk Mail",0,0,,,0,,,move,In,0,0,,0,,,' \
                              'move,In,0,0,,0,,,move,In,0,0,,0,,,move,' \
                              'In,0,0,,0,,,move,In,0,0,1,0'
            if pocomail_filters.has_key("Incoming") and \
               spamBayesFilter not in pocomail_filters["Incoming"]:
                pocomail_filters["Incoming"].append(spamBayesFilter)

            f = open(pocomail_filters_file, "w")
            f.write('{ Filter list generated by PocoMail 3.01 (1661)' \
                    '- Licensed Version}\n')
            for filterName in pocomail_filters.keys():
                f.write('\n[' + filterName + ']\n')
                for filter in pocomail_filters[filterName]:
                    f.write(filter + '\n')
            f.close()
    return []


def find_config_location(mailer):
    """Attempt to find the location of the config file for
    the given mailer, to pass to the configure_* scripts
    above."""
    # Requires win32all to be available, until someone
    # fixes the function to look in the right places for *nix/Mac.
    if win32api is None:
        raise ImportError("win32 extensions required")
    if mailer in ["Outlook Express", ]:
        # Outlook Express can be configured without a
        # config location, because it's all in the registry.
        return ""
    windowsUserDirectory = shell.SHGetFolderPath(0,shellcon.CSIDL_APPDATA,0,0)
    potential_locations = \
                        {"Eudora" : ("%(wud)s%(sep)sQualcomm%(sep)sEudora",),
                         "Mozilla" : \
                         ("%(wud)s%(sep)sMozilla%(sep)sProfiles%(sep)s%(user)s",
                          "%(wud)s%(sep)sMozilla%(sep)sProfiles%(sep)sdefault",),
                         "M2" : ("%(wud)s%(sep)sOpera%(sep)sOpera7",),
                         "PocoMail" : (pocomail_accounts_filename(),),
                         }
    # We try with the username that the user uses
    # for Windows, even though that might not be the same as their profile
    # names for mailers.  We can get smarter later.
    username = win32api.GetUserName()
    loc_dict = {"sep" : os.sep,
                "wud" : windowsUserDirectory,
                "user" : username}
    for loc in potential_locations[mailer]:
        loc = loc % loc_dict
        if os.path.exists(loc):
            return loc
    return None

def configure(mailer):
    """Automatically configure the specified mailer and SpamBayes."""
    loc = find_config_location(mailer)
    if loc is None:
        # Can't set it up, so do nothing.
        return
    funcs = {"Eudora" : configure_eudora,
             "Mozilla" : configure_mozilla,
             "M2" : configure_m2,
             "Outlook Express" : configure_outlook_express,
             "PocoMail" : configure_pocomail,
             }
    return funcs[mailer](loc)

def is_installed(mailer):
    """Return True if we believe that the mailer is installed."""
    # For the simpler mailers, we believe it is installed if the
    # configuration path can be found and exists.
    config_location = find_config_location(mailer)
    if config_location:
        if os.path.exists(config_location):
            return True
        return False
    # For the ones based in the registry, we have different
    # techniques.
    if mailer == "Outlook Express":
        if oe_mailbox.OEIsInstalled():
            return True
        return False

    # If we don't know, guess that it isn't.
    return False

def offer_to_configure(mailer):
    """If the mailer appears to be installed, offer to set it up for
    SpamBayes (and SpamBayes for it)."""
    # At the moment, the test we use to check if the mailer is installed
    # is whether a valid path to the configuration file can be found.
    # This is ok, except for those that are setup in the registry - there
    # will always be a valid path, whether they are installed or not.
    if find_config_location(mailer) is not None:
        confirm_text = "Would you like %s setup for SpamBayes, and " \
                       "SpamBayes setup with your %s settings?\n" \
                       "(This is alpha software! We recommend that you " \
                       "only do this if you know how to re-setup %s " \
                       "if necessary.)" % (mailer, mailer, mailer)
        ans = MessageBox(confirm_text, "Configure?",
                         win32con.MB_YESNO | win32con.MB_ICONQUESTION)
        if ans == win32con.IDYES:
            results = configure(mailer)
            if results is None:
                MessageBox("Configuration unsuccessful.", "Error",
                           win32con.MB_OK | win32con.MB_ICONERROR)
            else:
                text = "Configuration complete.\n\n" + "\n".join(results)
                MessageBox(text, "Complete", win32con.MB_OK)

def GetConsoleHwnd():
    """Returns the window handle of the console window in which this script is
    running, or 0 if not running in a console window.  This function is taken
    directly from Pythonwin\dllmain.cpp in the win32all source, ported to
    Python."""

    # fetch current window title
    try:
        oldWindowTitle = win32api.GetConsoleTitle()
    except:
        return 0

    # format a "unique" NewWindowTitle
    newWindowTitle = "%d/%d" % (win32api.GetTickCount(),
                                win32api.GetCurrentProcessId())

    # change current window title
    win32api.SetConsoleTitle(newWindowTitle)

    # ensure window title has been updated
    import time
    time.sleep(0.040)

    # look for NewWindowTitle
    hwndFound = win32gui.FindWindow(0, newWindowTitle)

    # restore original window title
    win32api.SetConsoleTitle(oldWindowTitle)

    return hwndFound

hwndOwner = GetConsoleHwnd()
def MessageBox(message, title=None, style=win32con.MB_OK):
    return win32gui.MessageBox(hwndOwner, message, title, style)


if __name__ == "__main__":
    pmail_ini_dir = "C:\\Program Files\\PMAIL\\MAIL\\ADMIN"
    for mailer in ["Eudora", "Mozilla", "M2", "Outlook Express", "PocoMail"]:
        #print find_config_location(mailer)
        #configure(mailer)
        offer_to_configure(mailer)


syntax highlighted by Code2HTML, v. 0.9.1