"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "spambayes-1.0.4/windows/autoconfigure.py" of archive spambayes-1.0.4.zip:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 #!/usr/bin/env python
    2 
    3 """Automatically set up the user's mail client and SpamBayes.
    4 
    5 Example usage:
    6     >>> configure("mailer name")
    7 Where "mailer name" is any of the names below.
    8 
    9 Currently works with:
   10  o Eudora (POP3/SMTP only)
   11  o Mozilla Mail (POP3/SMTP only)
   12  o M2 (Opera Mail) (POP3/SMTP only)
   13  o Outlook Express (POP3/SMTP only)
   14  o PocoMail (POP3/SMTP only)
   15 
   16 To do:
   17  o Establish which mail client(s) are installed in a more clever way.
   18  o This will create some unnecessary proxies in some cases.  For example,
   19    if I have my client set up to get mail from pop.example.com for the
   20    user 'tmeyer' and the user 'tonym', two proxies will be created, but
   21    only one is necessary.  We should check the existing proxies before
   22    adding a new one.
   23  o Other mail clients?  Other platforms?
   24  o This won't work all that well if multiple mail clients are used (they
   25    will end up trying to use the same ports).  In such a case, we really
   26    need to keep track of if the server is being proxied already, and
   27    reuse ports, but this is complicated.
   28  o We currently don't make any moves to protect the original file, so if
   29    something does wrong, it's corrupted.  We also write into the file,
   30    rather than a temporary one and then copy across.  This should all be
   31    fixed.  Richie's suggestion is for the script to create a clone of an
   32    existing account with the new settings.  Then people could test the
   33    cloned account, and if they're happy with it they can either delete
   34    their old account or delete the new one and run the script again in
   35    "modify" rather than "clone" mode.  This sounds like a good idea,
   36    although a lot of work...
   37  o Suggestions?
   38 """
   39 
   40 # This module is part of the spambayes project, which is Copyright 2002-3
   41 # The Python Software Foundation and is covered by the Python Software
   42 # Foundation license.
   43 
   44 __author__ = "Tony Meyer <ta-meyer@ihug.co.nz>"
   45 __credits__ = "All the Spambayes folk."
   46 
   47 try:
   48     True, False
   49 except NameError:
   50     # Maintain compatibility with Python 2.2
   51     True, False = 1, 0
   52 
   53 ## Tested with:
   54 ##  o Eudora 5.2 on Windows XP
   55 ##  o Mozilla 1.3 on Windows XP
   56 ##  o Opera 7.11 on Windows XP
   57 ##  o Outlook Express 6 on Windows XP
   58 
   59 import re
   60 import os
   61 import sys
   62 import types
   63 import socket
   64 import shutil
   65 import StringIO
   66 import ConfigParser
   67 
   68 try:
   69     import win32gui
   70     import win32api
   71     import win32con
   72     import pywintypes
   73     from win32com.shell import shell, shellcon
   74 except ImportError:
   75     # The ImportError is delayed until these are needed - if we
   76     # did it here, the functions that don't need these would still
   77     # fail.  (And having "import win32api" in lots of functions
   78     # didn't seem to make much sense).
   79     win32api = win32con = shell = shellcon = win32gui = pywintypes = None
   80 
   81 from spambayes import oe_mailbox
   82 from spambayes import OptionsClass
   83 from spambayes.Options import options, optionsPathname
   84 
   85 def move_to_next_free_port(port):
   86     # Increment port until we get to one that isn't taken.
   87     # I doubt this will work if there is a firewall that prevents
   88     # localhost connecting to particular ports, but I'm not sure
   89     # how else we can do this - Richie says that bind() doesn't
   90     # necessarily fail if the port is already bound.
   91     while True:
   92         try:
   93             port += 1
   94             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
   95             s.connect(("127.0.0.1", port))
   96             s.close()
   97         except socket.error:
   98             portStr = str(port)
   99             if portStr in options["pop3proxy", "listen_ports"] or \
  100                portStr in options["smtpproxy", "listen_ports"]:
  101                 continue
  102             else:
  103                 return port
  104 
  105 # Let's be safe and use high ports, starting at 1110 and 1025, and going up
  106 # as required.
  107 pop_proxy_port = move_to_next_free_port(1109)
  108 smtp_proxy_port = move_to_next_free_port(1024)
  109 
  110 def configure_eudora(config_location):
  111     """Configure Eudora to use the SpamBayes POP3 and SMTP proxies, and
  112     configure SpamBayes to proxy the servers that Eudora was connecting to.
  113     """
  114     ini_filename = "%s%seudora.ini" % (config_location, os.sep)
  115     c = ConfigParser.ConfigParser()
  116     c.read(ini_filename)
  117 
  118     translate = {("PopServer", "POPPort") : "pop3proxy",
  119                  ("SMTPServer", "SMTPPort") : "smtpproxy",
  120                  }
  121 
  122     pop_proxy = pop_proxy_port
  123     smtp_proxy = smtp_proxy_port
  124 
  125     results = []
  126     for sect in c.sections():
  127         if sect.startswith("Persona-") or sect == "Settings":
  128             if c.get(sect, "UsesIMAP") == "0":
  129                 # Eudora stores the POP3 server name in two places.
  130                 # Why?  Who cares.  We do the popaccount one
  131                 # separately, because it also has the username.
  132                 p = c.get(sect, "popaccount")
  133                 c.set(sect, "popaccount", "%s@localhost" % \
  134                       (p[:p.index('@')],))
  135                 for (eud_name, eud_port), us_name in translate.items():
  136                     try:
  137                         port = c.get(sect, eud_port)
  138                     except ConfigParser.NoOptionError:
  139                         port = None
  140 
  141                     if us_name.lower()[:4] == "pop3":
  142                         if port is None:
  143                             port = 110
  144                         pop_proxy = move_to_next_free_port(pop_proxy)
  145                         proxy_port = pop_proxy
  146                     else:
  147                         if port is None:
  148                             port = 25
  149                         smtp_proxy = move_to_next_free_port(smtp_proxy)
  150                         proxy_port = smtp_proxy
  151                     server = "%s:%s" % (c.get(sect, eud_name), port)
  152                     options[us_name, "remote_servers"] += (server,)
  153                     options[us_name, "listen_ports"] += (proxy_port,)
  154                     results.append("[%s] Proxy %s on localhost:%s" % \
  155                                    (sect, server, proxy_port))
  156                     c.set(sect, eud_name, "localhost")
  157                     c.set(sect, eud_port, proxy_port)
  158             else:
  159                 # Setup imapfilter instead
  160                 pass
  161 
  162     out = file(ini_filename, "w")
  163     c.write(out)
  164     out.close()
  165     options.update_file(optionsPathname)
  166 
  167     # Setup filtering rule
  168     # This assumes that the spam and unsure folders already exist!
  169     # (Creating them shouldn't be that difficult - it's just a mbox file,
  170     # and I think the .toc file is automatically created).  Left for
  171     # another day, however.
  172     filter_filename = "%s%sFilters.pce" % (config_location, os.sep)
  173     spam_folder_name = "Junk"
  174     unsure_folder_name = "Possible Junk"
  175     header_name = options["Headers", "classification_header_name"]
  176     spam_tag = options["Headers", "header_spam_string"]
  177     unsure_tag = options["Headers", "header_unsure_string"]
  178     # We are assuming that a rules file already exists, otherwise there
  179     # is a bit more to go at the top.
  180     filter_rules = "rule SpamBayes-Spam\n" \
  181                    "transfer %s.mbx\n" \
  182                    "incoming\n" \
  183                    "header %s\n" \
  184                    "verb contains\n" \
  185                    "value %s\n" \
  186                    "conjunction ignore\n" \
  187                    "header \n" \
  188                    "verb contains\n" \
  189                    "value \n" \
  190                    "rule SpamBayes-Unsure\n" \
  191                    "transfer %s.mbx\n" \
  192                    "incoming\n" \
  193                    "header %s\n" \
  194                    "verb contains\n" \
  195                    "value %s\n" \
  196                    "conjunction ignore\n" \
  197                    "header \n" \
  198                    "verb contains\n" \
  199                    "value \n" % (spam_folder_name, header_name, spam_tag,
  200                                  unsure_folder_name, header_name, unsure_tag)
  201     filter_file = file(filter_filename, "a")
  202     filter_file.write(filter_rules)
  203     filter_file.close()
  204     return results
  205 
  206 def configure_mozilla(config_location):
  207     """Configure Mozilla to use the SpamBayes POP3 and SMTP proxies, and
  208     configure SpamBayes to proxy the servers that Mozilla was connecting
  209     to."""
  210     prefs_file = file("%s%sprefs.js" % (config_location, os.sep), "r")
  211     prefs = prefs_file.read()
  212     prefs_file.close()
  213     save_prefs = prefs
  214     pop_accounts = {}
  215     smtp_accounts = {}
  216 
  217     r = re.compile(r"user_pref\(\"mail.server.server(\d+).(real)?hostname\", \"([^\"]*)\"\);")
  218     current_pos = 0
  219     results = []
  220     while True:
  221         m = r.search(prefs[current_pos:])
  222         if not m:
  223             break
  224         server_num = m.group(1)
  225         real = m.group(2) or ''
  226         server = m.group(3)
  227         current_pos += m.end()
  228         old_pref = 'user_pref("mail.server.server%s.%shostname", "%s");' % \
  229                    (server_num, real, server)
  230 
  231         # Find the port, if there is one
  232         port_string = 'user_pref("mail.server.server%s.port", ' % \
  233                       (server_num,)
  234         port_loc = prefs.find(port_string)
  235         if port_loc == -1:
  236             port = "110"
  237             old_port = None
  238         else:
  239             loc_plus_len = port_loc + len(port_string)
  240             end_of_number = loc_plus_len + prefs[loc_plus_len:].index(')')
  241             port = prefs[loc_plus_len : end_of_number]
  242             old_port = "%s%s);" % (port_string, port)
  243 
  244         # Find the type of connection
  245         type_string = 'user_pref("mail.server.server%s.type", "' % \
  246                       (server_num,)
  247         type_loc = prefs.find(type_string)
  248         if type_loc == -1:
  249             # no type, so ignore this one
  250             continue
  251         type_loc += len(type_string)
  252         account_type = prefs[type_loc : \
  253                              type_loc + prefs[type_loc:].index('"')]
  254 
  255         if account_type == "pop3":
  256             new_pref = 'user_pref("mail.server.server%s.%shostname", ' \
  257                        '"127.0.0.1");' % (server_num, real)
  258             if not pop_accounts.has_key(server_num) or real:
  259                 pop_accounts[server_num] = (new_pref, old_pref,
  260                                             old_port, server, port)
  261         elif account_type == "imap":
  262             # Setup imapfilter instead
  263             pass
  264 
  265     proxy_port = pop_proxy_port
  266     for num, (pref, old_pref, old_port, server, port) in pop_accounts.items():
  267         server = "%s:%s" % (server, port)
  268         proxy_port = move_to_next_free_port(proxy_port)
  269         port_pref = 'user_pref("mail.server.server%s.port", %s);' % \
  270                     (num, proxy_port)
  271         options["pop3proxy", "remote_servers"] += (server,)
  272         options["pop3proxy", "listen_ports"] += (proxy_port,)
  273         if old_port is None:
  274             pref = "%s\n%s" % (pref, port_pref)
  275         else:
  276             save_prefs = save_prefs.replace(old_port, port_pref)
  277         save_prefs = save_prefs.replace(old_pref, pref)
  278         results.append("[%s] Proxy %s on localhost:%s" % \
  279                        (num, server, proxy_port))
  280 
  281     # Do the SMTP server.
  282     # Mozilla recommends that only advanced users setup more than one,
  283     # so we'll just set that one up.  Advanced users can setup SpamBayes
  284     # themselves <wink>.
  285     prefs = save_prefs
  286     r = re.compile(r"user_pref\(\"mail.smtpserver.smtp(\d+).hostname\", \"([^\"]*)\"\);")
  287     current_pos = 0
  288     while True:
  289         m = r.search(prefs[current_pos:])
  290         if not m:
  291             break
  292         current_pos = m.end()
  293         server_num = m.group(1)
  294         server = m.group(2)
  295         old_pref = 'user_pref("mail.smtpserver.smtp%s.hostname", ' \
  296                    '"%s");' % (server_num, server)
  297         new_pref = 'user_pref("mail.smtpserver.smtp%s.hostname", ' \
  298                    '"127.0.0.1");' % (server_num,)
  299 
  300         # Find the port
  301         port_string = 'user_pref("mail.smtpserver.smtp1.port", '
  302         port_loc = prefs.find(port_string)
  303         if port_loc == -1:
  304             port = "25"
  305             old_port = None
  306         else:
  307             loc_plus_len = port_loc + len(port_string)
  308             end_of_number = loc_plus_len + prefs[loc_plus_len:].index(')')
  309             port = prefs[loc_plus_len : end_of_number]
  310             old_port = 'user_pref("mail.smtpserver.smtp%s.port", %s);' % \
  311                        (server_num, port)
  312         smtp_accounts[server_num] = (new_pref, old_pref, old_port,
  313                                      server, port)
  314 
  315     proxy_port = smtp_proxy_port
  316     for num, (pref, old_pref, old_port, server, port) in smtp_accounts.items():
  317         server = "%s:%s" % (server, port)
  318         proxy_port = move_to_next_free_port(proxy_port)
  319         port_pref = 'user_pref("mail.smtpserver.smtp%s.port", %s);' % \
  320                     (num, proxy_port)
  321         options["smtpproxy", "remote_servers"] += (server,)
  322         options["smtpproxy", "listen_ports"] += (proxy_port,)
  323         if old_port is None:
  324             pref = "%s\n%s" % (pref, port_pref)
  325         else:
  326             save_prefs = save_prefs.replace(old_port, port_pref)
  327         save_prefs = save_prefs.replace(old_pref, pref)
  328         results.append("[%s] Proxy %s on localhost:%s" % \
  329                        (num, server, proxy_port))
  330 
  331     prefs_file = file("%s%sprefs.js" % (config_location, os.sep), "w")
  332     prefs_file.write(save_prefs)
  333     prefs_file.close()
  334     options.update_file(optionsPathname)
  335 
  336     # Setup filtering rules.
  337     # Assumes that the folders already exist!  I don't know how difficult
  338     # it would be to create new Mozilla mail folders.
  339     filter_filename = "%s%smsgFilterRules.dat" % (config_location, os.sep)
  340     store_name = "" # how do we get this?
  341     spam_folder_url = "mailbox:////%s//Junk%20Mail" % (store_name,)
  342     unsure_folder_url = "mailbox:////%s//Possible%20Junk" % (store_name,)
  343     header_name = options["Headers", "classification_header_name"]
  344     spam_tag = options["Headers", "header_spam_string"]
  345     unsure_tag = options["Headers", "header_unsure_string"]
  346     rule = 'name="SpamBayes-Spam"\n' \
  347            'enabled="yes"\n' \
  348            'type="1"\n' \
  349            'action="Move to folder"\n' \
  350            'actionValue="%s"\n' \
  351            'condition="OR (\"%s\",contains,%s)"\n' \
  352            'name="SpamBayes-Unsure"\n' \
  353            'enabled="yes"\n' \
  354            'type="1"\n' \
  355            'action="Move to folder"\n' \
  356            'actionValue="%s"\n' \
  357            'condition="OR (\"%s\",contains,%s)"\n' % \
  358            (spam_folder_url, header_name, spam_tag,
  359             unsure_folder_url, header_name, unsure_tag)
  360     # This should now be written to the file, but I'm not sure how we
  361     # determine which subdirectory it goes into - does it have to go
  362     # into them all?
  363     # We are assuming that a rules file already exists, otherwise there
  364     # is a bit more to go at the top.
  365     return results
  366 
  367 def configure_m2(config_location):
  368     """Configure M2 (Opera's mailer) to use the SpamBayes POP3 and SMTP
  369     proxies, and configure SpamBayes to proxy the servers that M2 was
  370     connecting to."""
  371     ini_filename = os.path.join(config_location, "Mail", "accounts.ini")
  372     ini_file = file(ini_filename, "r")
  373     faked_up = StringIO.StringIO()
  374     faked_up.write(";") # Missing at the start
  375     faked_up.write(ini_file.read())
  376     faked_up.seek(0)
  377     ini_file.close()
  378     c = ConfigParser.ConfigParser()
  379     c.readfp(faked_up)
  380 
  381     translate = {("Incoming Servername", "Incoming Port") : "pop3proxy",
  382                  ("Outgoing Servername", "Outgoing Port") : "smtpproxy",
  383                  }
  384 
  385     pop_proxy = pop_proxy_port
  386     smtp_proxy = smtp_proxy_port
  387 
  388     results = []
  389     for sect in c.sections():
  390         if sect.startswith("Account") and sect != "Accounts":
  391             if c.get(sect, "Incoming Protocol") == "POP":
  392                 for (m2_name, m2_port), us_name in translate.items():
  393                     try:
  394                         port = c.get(sect, m2_port)
  395                     except ConfigParser.NoOptionError:
  396                         port = None
  397 
  398                     if us_name.lower()[:4] == "pop3":
  399                         if port is None:
  400                             port = 110
  401                         pop_proxy = move_to_next_free_port(pop_proxy)
  402                         proxy_port = pop_proxy
  403                     else:
  404                         if port is None:
  405                             port = 25
  406                         smtp_proxy = move_to_next_free_port(smtp_proxy)
  407                         proxy_port = smtp_proxy
  408                     server = "%s:%s" % (c.get(sect, m2_name), port)
  409                     options[us_name, "remote_servers"] += (server,)
  410                     options[us_name, "listen_ports"] += (proxy_port,)
  411                     results.append("[%s] Proxy %s on localhost:%s" % \
  412                                    (sect, server, proxy_port))
  413                     c.set(sect, m2_name, "localhost")
  414                     c.set(sect, m2_port, proxy_port)
  415             elif c.get(sect, "Incoming Protocol") == "IMAP":
  416                 # Setup imapfilter instead
  417                 pass
  418 
  419     out = file(ini_filename, "w")
  420     c.write(out)
  421     out.close()
  422     options.update_file(optionsPathname)
  423 
  424     # Setting up a filter in M2 is very simple, but I'm not sure what the
  425     # right rule is - M2 doesn't move mail, it just displays a subset.
  426     # If someone can describe the best all-purpose rule, I'll pop it in
  427     # here.
  428     return results
  429 
  430 def configure_outlook_express(unused):
  431     """Configure OE to use the SpamBayes POP3 and SMTP proxies, and
  432     configure SpamBayes to proxy the servers that OE was connecting to."""
  433     # Requires win32all to be available (or for someone to write a
  434     # Mac version <wink>)
  435     if win32api is None:
  436         raise ImportError("win32 extensions required")
  437 
  438     accounts = oe_mailbox.OEAccountKeys()
  439 
  440     translate = {("POP3 Server", "POP3 Port") : "pop3proxy",
  441                  ("SMTP Server", "SMTP Port") : "smtpproxy",
  442                  }
  443 
  444     pop_proxy = pop_proxy_port
  445     smtp_proxy = smtp_proxy_port
  446 
  447     results = []
  448     for proto, subkey, account in accounts:
  449         if proto == "POP3":
  450             for (server_key, port_key), sect in translate.items():
  451                 server = "%s:%s" % (account[server_key][0],
  452                                     account[port_key][0])
  453                 if sect[:4] == "pop3":
  454                     pop_proxy = move_to_next_free_port(pop_proxy)
  455                     proxy = pop_proxy
  456                 else:
  457                     smtp_proxy = move_to_next_free_port(smtp_proxy)
  458                     proxy = smtp_proxy
  459                 options[sect, "remote_servers"] += (server,)
  460                 options[sect, "listen_ports"] += (proxy,)
  461                 win32api.RegSetValueEx(subkey, server_key, 0,
  462                                        win32con.REG_SZ, "127.0.0.1")
  463                 win32api.RegSetValueEx(subkey, port_key, 0,
  464                                        win32con.REG_SZ, str(proxy))
  465                 results.append("[%s] Proxy %s on localhost:%s" % \
  466                                (account["Account Name"][0], server, proxy))
  467         elif proto == "IMAP4":
  468             # Setup imapfilter instead.
  469             pass
  470 
  471     options.update_file(optionsPathname)
  472 
  473     # Outlook Express rules are done in much the same way.  Should one
  474     # be set up to work with notate_to or notate_subject?  (and set that
  475     # option, obviously)
  476     return results
  477 
  478 def configure_pegasus_mail(config_location):
  479     """Configure Pegasus Mail to use the SpamBayes POP3 and SMTP proxies,
  480     and configure SpamBayes to proxy the servers that Pegasus Mail was
  481     connecting to."""
  482 
  483     # We can't use ConfigParser here, as we want 'surgical' editing,
  484     # so we want to use out OptionsClass.  There is the additional trouble
  485     # that the Pegasus Mail config file doesn't have a section header.
  486 
  487     pop_proxy = pop_proxy_port
  488     smtp_proxy = smtp_proxy_port
  489 
  490     results = []
  491     for filename in os.listdir(config_location):
  492         if filename.lower().startswith("pop") or filename.lower().startswith("smt"):
  493             full_filename = os.path.join(config_location, filename)
  494             working_filename = "%s.tmp" % (filename, )
  495             shutil.copyfile(filename, working_filename)
  496             c = OptionsClass.OptionsClass()
  497             c.merge_file(working_filename)
  498             server = "%s:%s" % (c.get("all", "host"), c.get("all", "port"))
  499             if filename[:3] == "pop":
  500                 pop_proxy = move_to_next_free_port(pop_proxy)
  501                 proxy = pop_proxy
  502                 sect = "pop3proxy"
  503             else:
  504                 smtp_proxy = move_to_next_free_port(smtp_proxy)
  505                 proxy = smtp_proxy
  506                 sect = "smtpproxy"
  507             options[sect, "remote_servers"] += (server,)
  508             options[sect, "listen_ports"] += (proxy,)
  509             # Write in the new options!!
  510             c.set("all", "host", "127.0.0.1")
  511             c.set("all", "port", proxy)
  512             c.update_file(working_filename)
  513             results.append("[%s] Proxy %s on localhost:%s" % \
  514                            (c.get("all", "title"), server, proxy))
  515         elif filename.lower() == "IMAP.PM":
  516             # Setup imapfilter instead.
  517             pass
  518 
  519     # Pegasus Mail has a 'weight' system for determining junk mail.
  520     # The best plan would probably be to just add to this.  Something like:
  521     rules_filename = os.path.join(config_location, "spambust.dat")
  522     header_name = options["Headers", "classification_header_name"]
  523     spam_tag = options["Headers", "header_spam_string"]
  524     unsure_tag = options["Headers", "header_unsure_string"]
  525     ham_tag = options["Headers", "header_ham_string"]
  526     spam_weight = 500
  527     ham_weight = -500
  528     unsure_weight = -50 # leave judgement up to the rest of the rules
  529     rule = '# SpamBayes adjustments\n' \
  530            'if header "%s" contains "%s" weight %s\n' \
  531            'if header "%s" contains "%s" weight %s\n' \
  532            'if header "%s" contains "%s" wieght %s\n\n' % \
  533            (header_name, spam_tag, spam_weight,
  534             header_name, unsure_tag, unsure_weight,
  535             header_name, ham_tag, ham_weight)
  536     rules_file = file(rules_filename, "a")
  537     rules_file.write(rule)
  538     rules_file.close()
  539     return results
  540 
  541 def pocomail_accounts_filename():
  542     if win32api is None:
  543         # If we don't have win32, then we don't know.
  544         return ""
  545     key = "Software\\Poco Systems Inc"
  546 
  547     pop_proxy  = pop_proxy_port
  548     smtp_proxy = smtp_proxy_port
  549 
  550     try:
  551         reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, key)
  552     except pywintypes.error:
  553         # It seems that we don't have PocoMail
  554         return ""
  555     else:
  556         subkey_name   = "%s\\%s" % (key, win32api.RegEnumKey(reg, 0))
  557         reg           = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER,
  558                                               subkey_name)
  559         pocomail_path = win32api.RegQueryValueEx(reg, "Path")[0]
  560 
  561     return os.path.join(pocomail_path, "accounts.ini")
  562 
  563 def configure_pocomail(pocomail_accounts_file):
  564     if os.path.exists(pocomail_accounts_file):
  565         f = open(pocomail_accounts_file, "r")
  566 
  567         accountName       = ""
  568         pocomail_accounts = { }
  569 
  570         # Builds the dictionary with all the existing accounts.
  571         for line in f.readlines():
  572             line = line.rstrip('\n\r')
  573             if line == '':
  574                 continue
  575 
  576             if line[0] == '[' and line[-1] == ']':
  577                 accountName = line[1:-1]
  578                 pocomail_accounts[accountName] = { }
  579             else:
  580                 separator   = line.find('=')
  581                 optionName  = line[:separator]
  582                 optionValue = line[separator + 1:]
  583 
  584                 if optionName == "POPServer":
  585                     pop3 = optionValue.split(':')
  586                     if len(pop3) == 1:
  587                         pop3.append(110)
  588                     server = "%s:%s" % tuple(pop3)
  589 
  590                     proxy     = pop_proxy
  591                     pop_proxy = move_to_next_free_port(pop_proxy)
  592 
  593                     if not server in options["pop3proxy", "remote_servers"]:
  594                         options["pop3proxy", "remote_servers"] += (server,)
  595                         options["pop3proxy", "listen_ports"]   += (proxy,)
  596                     else:
  597                         serverIndex = 0
  598                         for remoteServer in options["pop3proxy",
  599                                                     "remote_servers"]:
  600                             if remoteServer == server:
  601                                 break
  602                             serverIndex += 1
  603                         proxy = options["pop3proxy", "listen_ports"][serverIndex]
  604 
  605                     optionValue = "%s:%s" % ('localhost', proxy)
  606 
  607                 pocomail_accounts[accountName][optionName] = optionValue
  608 
  609         f.close()
  610         f = open(pocomail_accounts_file, "w")
  611         for accountName in pocomail_accounts.keys():
  612             f.write('[' + accountName + ']\n')
  613             for optionName, optionValue in pocomail_accounts[accountName].items():
  614                 f.write("%s=%s\n" % (optionName, optionValue))
  615             f.write('\n')
  616         f.close()
  617 
  618         options.update_file(optionsPathname)
  619 
  620         # Add a filter to pocomail
  621         pocomail_filters_file = os.path.join(pocomail_path, "filters.ini")
  622 
  623         if os.path.exists(pocomail_filters_file):
  624             f = open(pocomail_filters_file, "r")
  625 
  626             pocomail_filters = { }
  627             filterName       = ""
  628 
  629             for line in f.readlines():
  630                 line = line.rstrip('\n\r')
  631                 if line == '': continue
  632 
  633                 if line[0] == '[' and line[-1] == ']':
  634                     filterName = line[1:-1]
  635                     pocomail_filters[filterName] = []
  636                 elif line[0] != '{':
  637                     pocomail_filters[filterName].append(line)
  638             f.close()
  639 
  640             spamBayesFilter = 'spam,X-Spambayes-Classification,move,' \
  641                               '"Junk Mail",0,0,,,0,,,move,In,0,0,,0,,,' \
  642                               'move,In,0,0,,0,,,move,In,0,0,,0,,,move,' \
  643                               'In,0,0,,0,,,move,In,0,0,1,0'
  644             if pocomail_filters.has_key("Incoming") and \
  645                spamBayesFilter not in pocomail_filters["Incoming"]:
  646                 pocomail_filters["Incoming"].append(spamBayesFilter)
  647 
  648             f = open(pocomail_filters_file, "w")
  649             f.write('{ Filter list generated by PocoMail 3.01 (1661)' \
  650                     '- Licensed Version}\n')
  651             for filterName in pocomail_filters.keys():
  652                 f.write('\n[' + filterName + ']\n')
  653                 for filter in pocomail_filters[filterName]:
  654                     f.write(filter + '\n')
  655             f.close()
  656     return []
  657 
  658 
  659 def find_config_location(mailer):
  660     """Attempt to find the location of the config file for
  661     the given mailer, to pass to the configure_* scripts
  662     above."""
  663     # Requires win32all to be available, until someone
  664     # fixes the function to look in the right places for *nix/Mac.
  665     if win32api is None:
  666         raise ImportError("win32 extensions required")
  667     if mailer in ["Outlook Express", ]:
  668         # Outlook Express can be configured without a
  669         # config location, because it's all in the registry.
  670         return ""
  671     windowsUserDirectory = shell.SHGetFolderPath(0,shellcon.CSIDL_APPDATA,0,0)
  672     potential_locations = \
  673                         {"Eudora" : ("%(wud)s%(sep)sQualcomm%(sep)sEudora",),
  674                          "Mozilla" : \
  675                          ("%(wud)s%(sep)sMozilla%(sep)sProfiles%(sep)s%(user)s",
  676                           "%(wud)s%(sep)sMozilla%(sep)sProfiles%(sep)sdefault",),
  677                          "M2" : ("%(wud)s%(sep)sOpera%(sep)sOpera7",),
  678                          "PocoMail" : (pocomail_accounts_filename(),),
  679                          }
  680     # We try with the username that the user uses
  681     # for Windows, even though that might not be the same as their profile
  682     # names for mailers.  We can get smarter later.
  683     username = win32api.GetUserName()
  684     loc_dict = {"sep" : os.sep,
  685                 "wud" : windowsUserDirectory,
  686                 "user" : username}
  687     for loc in potential_locations[mailer]:
  688         loc = loc % loc_dict
  689         if os.path.exists(loc):
  690             return loc
  691     return None
  692 
  693 def configure(mailer):
  694     """Automatically configure the specified mailer and SpamBayes."""
  695     loc = find_config_location(mailer)
  696     if loc is None:
  697         # Can't set it up, so do nothing.
  698         return
  699     funcs = {"Eudora" : configure_eudora,
  700              "Mozilla" : configure_mozilla,
  701              "M2" : configure_m2,
  702              "Outlook Express" : configure_outlook_express,
  703              "PocoMail" : configure_pocomail,
  704              }
  705     return funcs[mailer](loc)
  706 
  707 def is_installed(mailer):
  708     """Return True if we believe that the mailer is installed."""
  709     # For the simpler mailers, we believe it is installed if the
  710     # configuration path can be found and exists.
  711     config_location = find_config_location(mailer)
  712     if config_location:
  713         if os.path.exists(config_location):
  714             return True
  715         return False
  716     # For the ones based in the registry, we have different
  717     # techniques.
  718     if mailer == "Outlook Express":
  719         if oe_mailbox.OEIsInstalled():
  720             return True
  721         return False
  722 
  723     # If we don't know, guess that it isn't.
  724     return False
  725 
  726 def offer_to_configure(mailer):
  727     """If the mailer appears to be installed, offer to set it up for
  728     SpamBayes (and SpamBayes for it)."""
  729     # At the moment, the test we use to check if the mailer is installed
  730     # is whether a valid path to the configuration file can be found.
  731     # This is ok, except for those that are setup in the registry - there
  732     # will always be a valid path, whether they are installed or not.
  733     if find_config_location(mailer) is not None:
  734         confirm_text = "Would you like %s setup for SpamBayes, and " \
  735                        "SpamBayes setup with your %s settings?\n" \
  736                        "(This is alpha software! We recommend that you " \
  737                        "only do this if you know how to re-setup %s " \
  738                        "if necessary.)" % (mailer, mailer, mailer)
  739         ans = MessageBox(confirm_text, "Configure?",
  740                          win32con.MB_YESNO | win32con.MB_ICONQUESTION)
  741         if ans == win32con.IDYES:
  742             results = configure(mailer)
  743             if results is None:
  744                 MessageBox("Configuration unsuccessful.", "Error",
  745                            win32con.MB_OK | win32con.MB_ICONERROR)
  746             else:
  747                 text = "Configuration complete.\n\n" + "\n".join(results)
  748                 MessageBox(text, "Complete", win32con.MB_OK)
  749 
  750 def GetConsoleHwnd():
  751     """Returns the window handle of the console window in which this script is
  752     running, or 0 if not running in a console window.  This function is taken
  753     directly from Pythonwin\dllmain.cpp in the win32all source, ported to
  754     Python."""
  755 
  756     # fetch current window title
  757     try:
  758         oldWindowTitle = win32api.GetConsoleTitle()
  759     except:
  760         return 0
  761 
  762     # format a "unique" NewWindowTitle
  763     newWindowTitle = "%d/%d" % (win32api.GetTickCount(),
  764                                 win32api.GetCurrentProcessId())
  765 
  766     # change current window title
  767     win32api.SetConsoleTitle(newWindowTitle)
  768 
  769     # ensure window title has been updated
  770     import time
  771     time.sleep(0.040)
  772 
  773     # look for NewWindowTitle
  774     hwndFound = win32gui.FindWindow(0, newWindowTitle)
  775 
  776     # restore original window title
  777     win32api.SetConsoleTitle(oldWindowTitle)
  778 
  779     return hwndFound
  780 
  781 hwndOwner = GetConsoleHwnd()
  782 def MessageBox(message, title=None, style=win32con.MB_OK):
  783     return win32gui.MessageBox(hwndOwner, message, title, style)
  784 
  785 
  786 if __name__ == "__main__":
  787     pmail_ini_dir = "C:\\Program Files\\PMAIL\\MAIL\\ADMIN"
  788     for mailer in