From mhammond at users.sourceforge.net Tue Dec 2 01:05:28 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Dec 2 01:05:31 2003 Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.11,1.12 Message-ID: Update of /cvsroot/spambayes/spambayes/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv25000 Modified Files: sb_server.py Log Message: As reported by Richie in Email, sb_server ignored command-line args Index: sb_server.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** sb_server.py 7 Oct 2003 00:36:30 -0000 1.11 --- sb_server.py 2 Dec 2003 06:05:25 -0000 1.12 *************** *** 847,851 **** def prepare(state): - state.init() state.prepare() # Launch any SMTP proxies. Note that if the user hasn't specified any --- 847,850 ---- From mhammond at users.sourceforge.net Tue Dec 2 19:34:19 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Dec 2 19:34:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes/test sb_test_support.py, NONE, 1.1 test_programs.py, NONE, 1.1 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/test In directory sc8-pr-cvs1:/tmp/cvs-serv5358 Added Files: sb_test_support.py test_programs.py Log Message: New sb_test_support file for utility functions used by test cases. test_programs attempts to start and stop a number of programs - currently sb_server.py, sb_server.py with a custom UI port, and the windows service. --- NEW FILE: sb_test_support.py --- # General utilities for the SpamBayes test suite # # import sys, os import unittest def fix_sys_path(): # XXX - MarkH had the bright idea *after* writing this that we should # ensure the CVS version of SpamBayes is *not* used to resolve SB imports. # This would allow us to effectively test the distutils setup script, so # any modules or files missing from the installed version raise errors. """Fix sys.path so that the core SpamBayes package, *and* the SpamBayes scripts can be imported. """ this_dir = os.path.dirname(__file__) try: import spambayes.Version except ImportError: # Apparently SpamBayes is yet to be "setup.py install" # We are in 'spambayes\spambayes\test' - 2 parents up should # do it. sb_dir = os.path.abspath( os.path.join(this_dir, "..", "..")) sys.path.insert(0, sb_dir) import SpamBayes.Version # Now do the same for the sb_* scripts try: import sb_server except ImportError: # Scripts are usually in "spambayes/scripts" (for an # installed SpamBayes, they appear to be in # os.path.join(sys.prefix(), "scripts"), which we may like to # leverage - however, these test scripts are not currently # installed). script_dir = os.path.abspath( os.path.join(this_dir, "..", "..", "scripts")) sys.path.insert(0, script_dir) import sb_server # Entry point for all our 'simple' based test programs def unittest_main(*args, **kwargs): # I bet one day this will be more than this unittest.main(*args, **kwargs) --- NEW FILE: test_programs.py --- import os import sys import errno import unittest import time import sb_test_support sb_test_support.fix_sys_path() try: True;False except NameError: # 2.2 compat True=(None is None); False=not True import sb_server from spambayes.Options import options default_shutdown_port = options["html_ui", "port"] verbose = 0 class Spawner: def __init__(self, test_case, spawn_args): self.test_case = test_case self.spawn_args = spawn_args # If the command is a .py file, insert an executable. if os.path.splitext(self.spawn_args[0])[1]=='.py': self.spawn_args.insert(0, sys.executable) self.pid = None def _spawn(self, args): return os.spawnv(os.P_NOWAIT, self.spawn_args[0], self.spawn_args) def start(self): raise NotImplementedError def stop(self): raise NotImplementedError def is_running(self): if self.pid is None: return False # damn - we could implement os.waitpid correctly # using the win32api - but as at 2.3, you can't check # if a pid is still running with os.waitpid if sys.platform.startswith("win32"): import win32process # sorry, ya gotta have win32all to run tests import win32con try: rc = win32process.GetExitCodeProcess(self.pid) result = rc==win32con.STILL_ACTIVE except win32process.error: result = False else: try: os.waitpid(self.pid, os.WNOHANG) result = True except os.error, details: if details.errno == errno.ECHILD: result = False # other exceptions invalid? raise # Wait a few seconds for the global mutex to catch up for i in range(20): time.sleep(0.25) if result==is_any_sb_server_running(): break # Check the platform agrees (could do xor, but even I wont be able # to read it in a few weeks if result: self.test_case.failUnless(is_any_sb_server_running(), "My server stopped, but global server mutex held") else: self.test_case.failUnless(not is_any_sb_server_running(), "My server running, but no global server mutex held") return result class Spawner_sb_server(Spawner): def __init__(self, test_case, args, shutdown_port = default_shutdown_port): self.shutdown_port = shutdown_port f = sb_server.__file__ if f.endswith(".pyc") or f.endswith(".pyo"): f = f[:-1] Spawner.__init__(self, test_case, [f]+args) def start(self): self.test_case.failUnless(not is_any_sb_server_running(), "Should be no server running") if verbose > 1: print "Spawning", self.spawn_args self.pid = self._spawn(self.spawn_args) # wait for it to start - 5 secs, 0.25 per check for i in range(20): time.sleep(0.25) if verbose > 1: print "Waiting for start flags: running=%s, global_mutex=%s" \ % (self.is_running(), is_any_sb_server_running()) if self.is_running() and is_any_sb_server_running(): return # gave up waiting. self.test_case.fail("sb_server appeared to not start") def stop(self): # Copied from sb_server.stop() # Shutdown as though through the web UI. This will save the DB, allow # any open proxy connections to complete, etc. from urllib import urlopen, urlencode urlopen('http://localhost:%d/save' % self.shutdown_port, urlencode({'how': 'Save & shutdown'})).read() # wait for it to stop - 5 secs, 0.25 per check for i in range(20): time.sleep(0.25) if not self.is_running() and not is_any_sb_server_running(): return # gave up waiting. self.test_case.fail("sb_server appeared to not stop") def is_any_sb_server_running(): # reach into sb_server internals, as it is authoritative (sometimes ) try: mutex = sb_server.open_platform_mutex() sb_server.close_platform_mutex(mutex) return False except sb_server.AlreadyRunningException: return True class TestServer(unittest.TestCase): def setUp(self): self.failUnless(not is_any_sb_server_running(), "Can't do sb_server tests while a server is running "\ "(platform mutex held)") def tearDown(self): # If we cause failure here, we mask the underlying error which left # the server running - so just print the warning. if is_any_sb_server_running(): print "WARNING:", self, "completed with the platform mutex held" def _start_spawner(self, spawner): self.failUnless(not spawner.is_running(), "this spawneer can't be running") spawner.start() self.failUnless(spawner.is_running(), "this spawner must be running after successful start") self.failUnless(is_any_sb_server_running(), "Platform mutex not held after starting") def _stop_spawner(self, spawner): self.failUnless(spawner.is_running(), "must be running to stop") self.failUnless(is_any_sb_server_running(), "Platform mutex must be held to stop") spawner.stop() self.failUnless(not spawner.is_running(), "didn't stop after stop") self.failUnless(not is_any_sb_server_running(), "Platform mutex still help after stop") def test_sb_server_default(self): # Should be using the default port from the options file. from spambayes.Options import options port = options["html_ui", "port"] s = Spawner_sb_server(self, []) self._start_spawner(s) self._stop_spawner(s) def test_sb_server_ui_port(self): # Should be using the default port from the options file. s = Spawner_sb_server(self, ["-u8899"], 8899) self._start_spawner(s) self._stop_spawner(s) if sys.platform.startswith("win"): import win32service # You need win32all to run the tests! import win32serviceutil import winerror service_name = "pop3proxy" class TestService(unittest.TestCase): def setUp(self): try: win32serviceutil.QueryServiceStatus(service_name) except win32service.error, details: if details[0]==winerror.ERROR_SERVICE_DOES_NOT_EXIST: self.was_installed = False raise else: self.was_installed = True self.failUnless(not is_any_sb_server_running(), "Can't do service tests while a server is running "\ "(platform mutex held)") def tearDown(self): if is_any_sb_server_running(): print "WARNING:", self, "completed with the platform mutex held" def _start_service(self): win32serviceutil.StartService(service_name) for i in range(10): time.sleep(0.5) status = win32serviceutil.QueryServiceStatus(service_name) if status[1] == win32service.SERVICE_RUNNING: break if verbose > 1: print "Service status is %d - still waiting" % status[1] else: self.fail("Gave up waiting for service to start") def _stop_service(self): # StopServiceWithDeps checks the status of each service as it # stops it, which is exactly what we want here. win32serviceutil.StopServiceWithDeps(service_name) def test_simple_startstop(self): self._start_service() self._stop_service() def test_remote_shutdown(self): self._start_service() # Should be using the default port from the options file. from spambayes.Options import options from urllib import urlopen, urlencode port = options["html_ui", "port"] urlopen('http://localhost:%d/save' % port, urlencode({'how': 'Save & shutdown'})).read() # wait for it to stop - 5 secs, 0.25 per check for i in range(10): time.sleep(0.5) status = win32serviceutil.QueryServiceStatus(service_name) if status[1] == win32service.SERVICE_STOPPED: break else: self.fail("Gave up waiting for service to stop") self.failUnless(not is_any_sb_server_running(), "Should be no platform mutex held after stopping") if __name__=='__main__': sb_test_support.unittest_main() From mhammond at users.sourceforge.net Tue Dec 2 19:35:18 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Dec 2 19:35:23 2003 Subject: [Spambayes-checkins] spambayes/spambayes/test test_storage.py, 1.3, 1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/test In directory sc8-pr-cvs1:/tmp/cvs-serv5530 Modified Files: test_storage.py Log Message: Use sb_test_support utilities, and prevent a warning message we explicitly test for from being written to stdout Index: test_storage.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_storage.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** test_storage.py 11 Sep 2003 07:47:13 -0000 1.3 --- test_storage.py 3 Dec 2003 00:35:16 -0000 1.4 *************** *** 2,14 **** import unittest, os, sys - - # Hack sys.path - 2 levels up must be on sys.path. try: ! this_file = __file__ ! except NameError: ! this_file = sys.argv[0] ! sb_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(this_file)))) ! if sb_dir not in sys.path: ! sys.path.append(sb_dir) from spambayes.storage import DBDictClassifier, PickledClassifier --- 2,12 ---- import unittest, os, sys try: ! import cStringIO as StringIO ! except ImportError: ! import StringIO ! ! import sb_test_support ! sb_test_support.fix_sys_path() from spambayes.storage import DBDictClassifier, PickledClassifier *************** *** 154,162 **** sys_exit = sys.exit sys.exit = self.success self.succeeded = False db_name = tempfile.mktemp("nodbmtest") ! s = open_storage(db_name, True) ! DBDictClassifier.load = DBDictClassifier_load ! sys.exit = sys_exit if not self.succeeded: self.fail() --- 152,168 ---- sys_exit = sys.exit sys.exit = self.success + # redirect sys.stderr and sys.exit, as storage.py prints + # an error to stderr, then attempts to sys.exit + # (we probably could just catch SystemExit for sys.exit?) + sys_stderr = sys.stderr + sys.stderr = StringIO.StringIO() self.succeeded = False db_name = tempfile.mktemp("nodbmtest") ! try: ! s = open_storage(db_name, True) ! finally: ! DBDictClassifier.load = DBDictClassifier_load ! sys.exit = sys_exit ! sys.stderr = sys_stderr if not self.succeeded: self.fail() *************** *** 172,174 **** if __name__=='__main__': ! unittest.main(argv=sys.argv + ['suite']) --- 178,180 ---- if __name__=='__main__': ! sb_test_support.unittest_main(argv=sys.argv + ['suite']) From mhammond at users.sourceforge.net Tue Dec 2 19:36:00 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Dec 2 19:36:02 2003 Subject: [Spambayes-checkins] spambayes/spambayes/test test_sb-server.py, 1.4, 1.5 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/test In directory sc8-pr-cvs1:/tmp/cvs-serv5670 Modified Files: test_sb-server.py Log Message: sys.path munging didn't work for me, so I replaced it with a sb_test_support utility function. Index: test_sb-server.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_sb-server.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** test_sb-server.py 19 Sep 2003 22:47:13 -0000 1.4 --- test_sb-server.py 3 Dec 2003 00:35:58 -0000 1.5 *************** *** 82,93 **** import re import getopt ! # We need to import sb_server, but it may not be on the PYTHONPATH. ! # Hack around this, so that if we are running in a cvs-like setup ! # everything still works. ! import sys ! import os ! sys.path.insert(-1, os.getcwd()) ! sys.path.insert(-1, os.path.join(os.getcwd(), "scripts")) from spambayes import Dibbler --- 82,89 ---- import re import getopt + import sys, os ! import sb_test_support ! sb_test_support.fix_sys_path() from spambayes import Dibbler From anadelonbrin at users.sourceforge.net Tue Dec 2 22:29:28 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Tue Dec 2 22:29:33 2003 Subject: [Spambayes-checkins] website faq.txt,1.51,1.52 Message-ID: Update of /cvsroot/spambayes/website In directory sc8-pr-cvs1:/tmp/cvs-serv564 Modified Files: faq.txt Log Message: Two new FAQs: Missing mail: it's odd that this is very suddenly a FAQ, and nowhere near a release. I'm confident that it's not the plug-in, though, so that's what I've said. I've put some general 'how to find mail' tips there to try and help people along. (Anyone else wondering if ISPs are to blame because they've put in too stringent filters to try and block all the worm crud recently?) Uninstall: how to uninstall the plug-in, including removing the toolbar (the oldest still-open bug!). I'm pretty sure that this is the current situation... Index: faq.txt =================================================================== RCS file: /cvsroot/spambayes/website/faq.txt,v retrieving revision 1.51 retrieving revision 1.52 diff -C2 -d -r1.51 -r1.52 *** faq.txt 5 Nov 2003 12:11:04 -0000 1.51 --- faq.txt 3 Dec 2003 03:29:26 -0000 1.52 *************** *** 601,604 **** --- 601,672 ---- + Some of my mail is going missing! + --------------------------------- + + The plug-in *can not* delete your mail - if it arrived in Outlook, then + either it's still there, or something else removed it. + + Check that the mail isn't in your 'Spam' folder *or* your 'Unsure' + folder, and that you are checking the correct folders (i.e. go to the + 'Filtering' tab of the SpamBayes dialog, and click on 'Browse' next to + the two movement options and check that you're moving to the folders you + think that you are). + + You can try to search for the mail, too, using Outlook's 'Advanced Find' + (in the 'Tools' menu). Even if you don't know the subject of the 'missing' + mail, you can search for all messages that have arrived since a certain date + or time. + + Sometimes mail is in the Inbox (or another folder), but not visible. This, + too, isn't SpamBayes's doing. You can set Outlook up to only show a subset + of the messages in the folder, with a "View Filter". The Outlook documentation + has more information about this (although the 'Advanced Find' will still + show the message). + + Note that if you are using sb_server to receive your mail, then (1) you're + reading the wrong section of the FAQ , and (2) there is a slim chance + that SpamBayes is at fault - please use the normal bug reporting procedures. + + + How do I uninstall the plug-in? + ------------------------------- + + Note that if you simply want to disable the plug-in for a while, you can + do this by unticking the "Enable SpamBayes" box on the front page of the + "Manager" dialog. + + If you installed the plug-in from source, then you simply need to re-run the + "addin.py" script, with the parameter "--unregister", and then delete the + toolbar in Outlook. + + Otherwise, you uninstall the plug-in like you uninstall (almost) any other + Windows program, using the "Add/Remove Programs" Control Panel. You'll see + the plug-in listed under "S" for "SpamBayes". This will stop remove all the + program files that the plug-in installed, and stop the plug-in from working. + It would be a good idea to do this while Outlook is *not* running. + + Note that this does *not* remove your personal setup files (deliberately). + This includes your databases and configuration files. This means that if you + later reinstall the plug-in (or a later version), those files will still be + there, ready to use. If you wish to remove these as well, you can remove them + like you would delete any other file (move them to the Recycle Bin). The + `backup question`_ explains where you can find these files. + + Note that `a bug with the plug-in`_ means that the SpamBayes toolbar is not + deleted on uninstall - although it *will* stop working. You can delete this + yourself in Outlook: right-click on the toolbar, choose "Customize", then + select the SpamBayes toolbar and click "Delete". + + Recently, there have been reports that the uninstaller has left some entries + in the registry - if this is the case with you, please add to that `bug tracker`_; + the next release will hopefully resolve this issue. These should be harmless, + but if they bother you (and you are confident mucking about with the registry, + which we do *not* recommend), then you can remove those keys yourself. + + .. _`backup question`: #can-i-back-up-the-outlook-database-should-i-do-this + .. _`bug tracker`: http://sourceforge.net/tracker/index.php?func=detail&aid=832183&group_id=61702&atid=498103 + .. _`a bug with the plug-in`: http://sourceforge.net/tracker/index.php?func=detail&aid=675811&group_id=61702&atid=498103 + + Using SpamBayes =============== From anadelonbrin at users.sourceforge.net Wed Dec 3 18:45:41 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 3 18:45:43 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs/resources dialogs.rc, 1.40, 1.41 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources In directory sc8-pr-cvs1:/tmp/cvs-serv28382/Outlook2000/dialogs/resources Modified Files: dialogs.rc Log Message: Add a period to make the dialog consistent. Index: dialogs.rc =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.rc,v retrieving revision 1.40 retrieving revision 1.41 diff -C2 -d -r1.40 -r1.41 *** dialogs.rc 29 Sep 2003 02:14:26 -0000 1.40 --- dialogs.rc 3 Dec 2003 23:45:38 -0000 1.41 *************** *** 520,524 **** SS_CENTERIMAGE | SS_SUNKEN,11,21,175,12 PUSHBUTTON "&Browse...",IDC_BROWSE_HAM,192,20,50,14 ! LTEXT "Folders with spam or other junk messages",IDC_STATIC,11, 36,171,9 CONTROL "Static",IDC_STATIC_SPAM,"Static",SS_LEFTNOWORDWRAP | --- 520,524 ---- SS_CENTERIMAGE | SS_SUNKEN,11,21,175,12 PUSHBUTTON "&Browse...",IDC_BROWSE_HAM,192,20,50,14 ! LTEXT "Folders with spam or other junk messages.",IDC_STATIC,11, 36,171,9 CONTROL "Static",IDC_STATIC_SPAM,"Static",SS_LEFTNOWORDWRAP | From anadelonbrin at users.sourceforge.net Wed Dec 3 21:57:45 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 3 21:57:49 2003 Subject: [Spambayes-checkins] spambayes/windows pop3proxy_tray.py,1.15,1.16 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv1181/windows Modified Files: pop3proxy_tray.py Log Message: Remove an old comment. Change the default (double-click) behaviour of the tray to "review messages" rather than "display information". It seems likely that this would be the most commonly needed action. Since 15/10/03, SetDefaultItem has been available for menus in win32all, so use that as we should. (It appears to set the font of the item correctly, but not have any effect in terms of action, so still capture the double-click ourselves. Someone correct me if I've done this wrongly). Index: pop3proxy_tray.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_tray.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** pop3proxy_tray.py 25 Sep 2003 00:10:32 -0000 1.15 --- pop3proxy_tray.py 4 Dec 2003 02:57:43 -0000 1.16 *************** *** 166,172 **** stoppedIconPathName = "%s\\..\\windows\\resources\\sb-stopped.ico" % \ (os.path.dirname(sb_server.__file__),) - # When 1.0a6 is released, the above line will need to change to: - ## iconPathName = "%s\\..\\windows\\resources\\sbicon.ico" % \ - ## (os.path.dirname(sb_server.__file__),) if hasattr(sys, "frozen"): self.hstartedicon = self.hstoppedicon = None --- 166,169 ---- *************** *** 352,356 **** # XXX text) through the win32 calls, but win32all doesn't # XXX include SetDefault(), which it needs to... ! self.OpenInterface() elif lparam==win32con.WM_RBUTTONUP: # check our state before creating the menu, so it reflects the --- 349,353 ---- # XXX text) through the win32 calls, but win32all doesn't # XXX include SetDefault(), which it needs to... ! self.OpenReview() elif lparam==win32con.WM_RBUTTONUP: # check our state before creating the menu, so it reflects the *************** *** 368,371 **** --- 365,378 ---- pos = GetCursorPos() SetForegroundWindow(self.hwnd) + # Set the default menu item ("Review messages", currently). + # Make sure that the item here matches the behaviour in + # DBCLICK above! + # This is only available in recent versions of win32all, + # so for those that don't have it, they just get a dull + # menu. + try: + SetMenuDefaultItem(menu, 2, 1) + except NameError: + pass TrackPopupMenu(menu, win32con.TPM_LEFTALIGN, pos[0], pos[1], 0, self.hwnd, None) From anadelonbrin at users.sourceforge.net Wed Dec 3 22:12:44 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 3 22:12:48 2003 Subject: [Spambayes-checkins] spambayes/windows spambayes.iss,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv3138/windows Modified Files: spambayes.iss Log Message: These dlls end up in the lib directory here, and I'm pretty sure that that's where they're meant to be these days. An old .iss, maybe? Index: spambayes.iss =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/spambayes.iss,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** spambayes.iss 23 Oct 2003 22:54:09 -0000 1.2 --- spambayes.iss 4 Dec 2003 03:12:41 -0000 1.3 *************** *** 19,24 **** Source: "py2exe\dist\lib\*.*"; DestDir: "{app}\lib"; Flags: ignoreversion Source: "py2exe\dist\bin\python23.dll"; DestDir: "{app}\bin"; Flags: ignoreversion ! Source: "py2exe\dist\bin\pythoncom23.dll"; DestDir: "{app}\bin"; Flags: ignoreversion ! Source: "py2exe\dist\bin\PyWinTypes23.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "py2exe\dist\bin\outlook_addin.dll"; DestDir: "{app}\bin"; Check: InstallingOutlook; Flags: ignoreversion regserver --- 19,24 ---- Source: "py2exe\dist\lib\*.*"; DestDir: "{app}\lib"; Flags: ignoreversion Source: "py2exe\dist\bin\python23.dll"; DestDir: "{app}\bin"; Flags: ignoreversion ! Source: "py2exe\dist\lib\pythoncom23.dll"; DestDir: "{app}\bin"; Flags: ignoreversion ! Source: "py2exe\dist\lib\PyWinTypes23.dll"; DestDir: "{app}\bin"; Flags: ignoreversion Source: "py2exe\dist\bin\outlook_addin.dll"; DestDir: "{app}\bin"; Check: InstallingOutlook; Flags: ignoreversion regserver From anadelonbrin at users.sourceforge.net Wed Dec 3 23:29:49 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 3 23:29:52 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/sandbox mapi_driver.py, 1.5, 1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/sandbox In directory sc8-pr-cvs1:/tmp/cvs-serv16818/Outlook2000/sandbox Modified Files: mapi_driver.py Log Message: If the *first* name returned wasn't the default store, then dump_props would say that there wasn't one. Fix that. Index: mapi_driver.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/mapi_driver.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** mapi_driver.py 1 Sep 2003 05:36:34 -0000 1.5 --- mapi_driver.py 4 Dec 2003 04:29:47 -0000 1.6 *************** *** 81,90 **** names = [n.lower() for n in name.split("\\")] if names[0]: for store, name, is_default in self.GetMessageStores(): if is_default: store_name = name.lower() break ! else: ! raise RuntimeError, "Can't find a default message store" folder_names = names else: --- 81,91 ---- names = [n.lower() for n in name.split("\\")] if names[0]: + store_name = None for store, name, is_default in self.GetMessageStores(): if is_default: store_name = name.lower() break ! if store_name is None: ! raise RuntimeError, "Can't find a default message store" folder_names = names else: From mhammond at users.sourceforge.net Thu Dec 4 23:41:28 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Thu Dec 4 23:41:31 2003 Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.23, 1.24 __init__.py, 1.7, 1.8 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv31600 Modified Files: ImapUI.py __init__.py Log Message: Merging old 1.0 trunk onto the branch. Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.23 retrieving revision 1.24 diff -C2 -d -r1.23 -r1.24 *** ImapUI.py 6 Oct 2003 01:24:12 -0000 1.23 --- ImapUI.py 5 Dec 2003 04:41:25 -0000 1.24 *************** *** 168,191 **** self._writePreamble("Select Filter Folders") self._login_to_imap() ! available_folders = self.imap.folder_list() ! content = self.html.configForm.clone() ! content.configFormContent = "" ! content.introduction = """This page allows you to change which ! folders are filtered, and where filtered mail ends up.""" ! content.config_submit.value = "Save Filter Folders" ! content.optionsPathname = optionsPathname ! for opt in ("unsure_folder", "spam_folder", ! "filter_folders"): ! folderBox = self._buildFolderBox("imap", opt, available_folders) ! content.configFormContent += folderBox ! self.write(content) ! self._writePostamble() def _login_to_imap(self): if self.imap_logged_in: return ! if self.imap is None: server = options["imap", "server"][0] if server.find(':') > -1: --- 168,192 ---- self._writePreamble("Select Filter Folders") self._login_to_imap() ! if self.imap_logged_in: ! available_folders = self.imap.folder_list() ! content = self.html.configForm.clone() ! content.configFormContent = "" ! content.introduction = """This page allows you to change which ! folders are filtered, and where filtered mail ends up.""" ! content.config_submit.value = "Save Filter Folders" ! content.optionsPathname = optionsPathname ! for opt in ("unsure_folder", "spam_folder", ! "filter_folders"): ! folderBox = self._buildFolderBox("imap", opt, available_folders) ! content.configFormContent += folderBox ! self.write(content) ! self._writePostamble() def _login_to_imap(self): if self.imap_logged_in: return ! if self.imap is None and len(options["imap", "server"]) > 0: server = options["imap", "server"][0] if server.find(':') > -1: *************** *** 217,235 **** self._writePreamble("Select Training Folders") self._login_to_imap() ! available_folders = self.imap.folder_list() ! content = self.html.configForm.clone() ! content.configFormContent = "" ! content.introduction = """This page allows you to change which ! folders contain mail to train Spambayes.""" ! content.config_submit.value = "Save Training Folders" ! content.optionsPathname = optionsPathname ! for opt in ("ham_train_folders", ! "spam_train_folders"): ! folderBox = self._buildFolderBox("imap", opt, available_folders) ! content.configFormContent += folderBox ! self.write(content) ! self._writePostamble() def onChangeopts(self, **parms): --- 218,237 ---- self._writePreamble("Select Training Folders") self._login_to_imap() ! if self.imap_logged_in: ! available_folders = self.imap.folder_list() ! content = self.html.configForm.clone() ! content.configFormContent = "" ! content.introduction = """This page allows you to change which ! folders contain mail to train Spambayes.""" ! content.config_submit.value = "Save Training Folders" ! content.optionsPathname = optionsPathname ! for opt in ("ham_train_folders", ! "spam_train_folders"): ! folderBox = self._buildFolderBox("imap", opt, available_folders) ! content.configFormContent += folderBox ! self.write(content) ! self._writePostamble() def onChangeopts(self, **parms): Index: __init__.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/__init__.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** __init__.py 19 Sep 2003 01:04:16 -0000 1.7 --- __init__.py 5 Dec 2003 04:41:25 -0000 1.8 *************** *** 1,3 **** # package marker. ! __version__ = '1.0a6' --- 1,3 ---- # package marker. ! __version__ = '1.0a7' From mhammond at users.sourceforge.net Thu Dec 4 23:45:48 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Thu Dec 4 23:45:52 2003 Subject: [Spambayes-checkins] spambayes README.txt, 1.60, 1.61 README-DEVEL.txt, 1.10, 1.11 WHAT_IS_NEW.txt, 1.20, 1.21 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv32249 Modified Files: README.txt README-DEVEL.txt WHAT_IS_NEW.txt Log Message: Merging old 1.0 branch Index: README.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/README.txt,v retrieving revision 1.60 retrieving revision 1.61 diff -C2 -d -r1.60 -r1.61 *** README.txt 17 Nov 2003 21:47:47 -0000 1.60 --- README.txt 5 Dec 2003 04:45:43 -0000 1.61 *************** *** 123,127 **** All you need to do to configure SpamBayes is to open a web page to , click on the "Configuration" link at the top ! right, and fill in the relevant details. Everything should be ok with the defaults, except for the POP3 and SMTP server information at the top, which is required. Note that *nix users may not have permission to bind ports --- 123,127 ---- All you need to do to configure SpamBayes is to open a web page to , click on the "Configuration" link at the top ! right, and fill in the relevant details. Everything should be OK with the defaults, except for the POP3 and SMTP server information at the top, which is required. Note that *nix users may not have permission to bind ports *************** *** 146,150 **** To configure SpamBayes, run "sb_imapfilter.py -b", which should open a web page to , click on the "Configuration" link at the ! top right, and fill in the relevant details. Everything should be ok with the defaults, except for the server information at the top. --- 146,150 ---- To configure SpamBayes, run "sb_imapfilter.py -b", which should open a web page to , click on the "Configuration" link at the ! top right, and fill in the relevant details. Everything should be OK with the defaults, except for the server information at the top. *************** *** 186,190 **** (Replace the latter with the location of the .hammiedb file that ! sb_filter created in the first step). Once you've trained SpamBayes on your --- 186,190 ---- (Replace the latter with the location of the .hammiedb file that ! sb_filter.py created in the first step). Once you've trained SpamBayes on your *************** *** 203,210 **** will still complain if you don't specify a lock file.) ! The result of running sb_filter.py in filter mode is that Procmail will use the ! output from the run as the mail message for further processing downstream. ! sb_filter.py inserts an X-SpamBayes-Classification header in the output message ! which looks like: X-SpamBayes-Classification: ham; 0.00; '*H*': 1.00; '*S*': 0.00; 'python': 0.00; --- 203,210 ---- will still complain if you don't specify a lock file.) ! The result of running sb_filter.py in filter mode is that Procmail will ! use the output from the run as the mail message for further processing ! downstream. sb_filter.py inserts an X-SpamBayes-Classification header in ! the output message which looks like: X-SpamBayes-Classification: ham; 0.00; '*H*': 1.00; '*S*': 0.00; 'python': 0.00; *************** *** 267,274 **** same number of spams as hams. ! You can train it on lots of messages in one go by either using the Hammie ! script as explained in the "Command-line training" section, or by giving ! messages to the web interface via the "Train" form on the Home page. You ! can train on individual messages (which is tedious) or using mbox files. --- 267,275 ---- same number of spams as hams. ! You can train it on lots of messages in one go by either using the ! sb_filter.py script as explained in the "Command-line training" section, ! or by giving messages to the web interface via the "Train" form on the ! Home page. You can train on individual messages (which is tedious) or ! using mbox files. *************** *** 385,389 **** contain the word Viagra"?), find particular messages, and most importantly, train it on the emails you've received. When you start ! using the system, unless you train it using the Hammie script it will classify most things as Unsure, and often make mistakes. But it keeps copies of all the email's its seen, and through the web interface you --- 386,390 ---- contain the word Viagra"?), find particular messages, and most importantly, train it on the emails you've received. When you start ! using the system, unless you train it using the sb_filter script it will classify most things as Unsure, and often make mistakes. But it keeps copies of all the email's its seen, and through the web interface you *************** *** 402,411 **** easy to file all the suspected spam into its own folder, for instance. ! o The Hammie script. This does three jobs: command-line training, procmail filtering, and XML-RPC. See below for details of how to use ! Hammie for training, and how to use it as procmail filter. Hammie can ! also run as an XML-RPC server, so that a programmer can write code that uses a remote server to classify emails programmatically - see ! hammiesrv.py. o The IMAP filter. This is a cross between the POP3 proxy and the Outlook --- 403,412 ---- easy to file all the suspected spam into its own folder, for instance. ! o The sb_filter.py script. This does three jobs: command-line training, procmail filtering, and XML-RPC. See below for details of how to use ! sb_filter for training, and how to use it as procmail filter. You can ! also run an XML-RPC server, so that a programmer can write code that uses a remote server to classify emails programmatically - see ! sb_xmlrpcserver.py. o The IMAP filter. This is a cross between the POP3 proxy and the Outlook Index: README-DEVEL.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/README-DEVEL.txt,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** README-DEVEL.txt 5 Nov 2003 12:50:13 -0000 1.10 --- README-DEVEL.txt 5 Dec 2003 04:45:44 -0000 1.11 *************** *** 77,80 **** --- 77,86 ---- following section refers to old application names. + IMPORTANT NOTE + ============== + + The applications have all been renamed in preparation for 1.0 - the + following section refers to old application names. + Apps ==== *************** *** 246,250 **** currently asking for. The idea is, if you have a standard directory structure (below), you can run this thing, go have some tea while it ! works, then paste the output to the spambayes list for good karma. --- 252,256 ---- currently asking for. The idea is, if you have a standard directory structure (below), you can run this thing, go have some tea while it ! works, then paste the output to the SpamBayes list for good karma. *************** *** 507,518 **** Anthony's Alternate Approach to Building the Zipfile ! o Unpack the tarball somewhere, making a spambayes-1.0a7 directory ! (version number will obviously change in future releases) ! o Run the following two commands: ! find spambayes-1.0a7 -type f -name '*.txt' | xargs zip -l sb107.zip ! find spambayes-1.0a7 -type f \! -name '*.txt' | xargs zip sb107.zip ! o This makes a tarball where the .txt files are mangled, but everything ! else is left alone. --- 513,524 ---- Anthony's Alternate Approach to Building the Zipfile ! o Unpack the tarball somewhere, making a spambayes-1.0a7 directory ! (version number will obviously change in future releases) ! o Run the following two commands: ! find spambayes-1.0a7 -type f -name '*.txt' | xargs zip -l sb107.zip ! find spambayes-1.0a7 -type f \! -name '*.txt' | xargs zip sb107.zip ! o This makes a tarball where the .txt files are mangled, but everything ! else is left alone. Index: WHAT_IS_NEW.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/WHAT_IS_NEW.txt,v retrieving revision 1.20 retrieving revision 1.21 diff -C2 -d -r1.20 -r1.21 *** WHAT_IS_NEW.txt 5 Nov 2003 12:50:13 -0000 1.20 --- WHAT_IS_NEW.txt 5 Dec 2003 04:45:44 -0000 1.21 *************** *** 10,14 **** noted in the "Transition" section. ! New in Alpha Release 6 ====================== --- 10,14 ---- noted in the "Transition" section. ! New in Alpha Release 7 ====================== *************** *** 17,22 **** -------------------------- ! There are two major changes in this release, which *will* effect you if you ! are upgrading from an older version: o The scripts have all moved (in the archive), and their names have been --- 17,33 ---- -------------------------- ! o If you are using a pickle for storage, your 'message info' database ! would previously still have been a dbm (where available). This is ! no longer the case - if you are using a pickle for the statistics ! database, you have a pickle for everything. Your old 'message info' ! database is not converted (and there is no utility provided to do so). ! If you are using a pickle for storage, you should delete your old ! 'spambayes.messageinfo.db' file before restarting after the upgrade. ! You should not suffer any ill effects from this, *unless* you are ! using sb_imapfilter.py. In that case, you will find that the filter ! trains and classifies all messages in the folders it examines, even ! if it has seen them before - this will only occur once, however. ! ! There should be no other incompatible changes (from 1.0a6) in this release. o The scripts have all moved (in the archive), and their names have been *************** *** 74,106 **** POP3 Proxy / SMTP Proxy ----------------------- ! o If running Windows, and have the win32 extensions installed, and a ! configuration file cannot be found, then default to placing it in the ! "Application Data" directory (as defined by Windows). Also default to ! storing the caches and databases in this directory. ! o Correctly save and close the database when changing options. This ! fixes a bug for those using gdbm databases. Web Interface ------------- ! o Place a threshold on the number of items displayed per section in the ! review page. ! o Correctly display the proxy data when it is specified on the command ! line. POP3 Proxy Service / POP3 Proxy Tray Application ------------------------------------------------ ! o The POP3 proxy tray application (for Windows users), which quietly ! appeared in the 1.0a5 release, should be ready for use. If you have ! the service installed, it will attempt to use that; if you do not, ! it will start up sb_server for you. ! ** Note that the tray application currently only works with Windows ! NT, Windows 2000 and Windows XP, not Windows 95, 98, or ME. This ! will be remedied in a future version, and in the binary release of ! the tray application. ** ! o As the service starts, it reports the username and configuration file ! that it is using. ! o Added a version check option to the tray application. ! o Improved logged for both the service and the tray application. IMAP Filter --- 85,103 ---- POP3 Proxy / SMTP Proxy ----------------------- ! o An error where a failure message would be printed by ! the SMTP proxy, even on success, was fixed. Web Interface ------------- ! o The bug which caused the "TypeError" when trying to access ! the database after setting a configuration option via the ! interface has been fixed. POP3 Proxy Service / POP3 Proxy Tray Application ------------------------------------------------ ! o Both the pop3proxy_service.py and pop3proxy_tray.py ! scripts are now installed (with "setup.py install") if ! the user is using Windows. IMAP Filter *************** *** 117,137 **** General ------- ! o Add a new file: NEWTRICKS.TXT to record ideas that have and haven't ! been tried. If you would like to add to this, please submit a patch ! via the Sourceforge system. ! o Change the default to tokenizer the "To", "CC", "Reply-To", "Sender", ! and "From" address headers, rather than just "From". ! o Fix the database opening code so that hammie* again works with both ! dbm and pickles. ! o Minor performance boosts to hammiebulk and mboxtrain. ! ! Developer ! --------- ! o Add a close method to the various storage classes. ! o Removed the gary_combining option and code. Transition ========== ! If you are transitioning from a version older than 1.0a5, please also read the notes in the previous release notes (accessible from ). --- 114,137 ---- General ------- ! o Various improvements have been made to the management of the ! 'message info' database. As outlined above, it will now be ! stored as a pickle, if your statistics database uses a pickle. ! In addition, we attempt to close the database when we should, ! and make sure that we explicitly update it. This should hopefully ! go some way to solving the "DB_RUN_RECOVERY" errors that have ! been regularly reported - we would be interested to hear from ! you if upgrading to 1.0a7 does appear to solve this problem ! for you (email spambayes@python.org). ! o We now try to determine the type of dbm storage used from the ! file, if one already exists. This should make the transition ! between formats a little easier. ! o Fix sb_xmlrpcserver to work with the renamed (since 1.0a5) ! scripts. ! o Fix the sense of include_trained in sb_mboxtrain. ! Transition ========== ! If you are transitioning from a version older than 1.0a6, please also read the notes in the previous release notes (accessible from ). *************** *** 144,149 **** =================== The following bugs tracked via the Sourceforge system were fixed: ! 803501, 802545, 802347, 801952, 798362, 800555, 806632, 795145, 806238, ! 805351 A URL containing the details of these bugs can be made by appending the --- 144,148 ---- =================== The following bugs tracked via the Sourceforge system were fixed: ! 809769, 814322, 816400, 810342, 818552 A URL containing the details of these bugs can be made by appending the From mhammond at users.sourceforge.net Fri Dec 5 00:12:35 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Fri Dec 5 00:12:37 2003 Subject: [Spambayes-checkins] website/apps/outlook bugs.txt,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/website/apps/outlook In directory sc8-pr-cvs1:/tmp/cvs-serv3813 Modified Files: bugs.txt Log Message: Add new Frequently Reported Bug - "Could not watch the specified folders" error Index: bugs.txt =================================================================== RCS file: /cvsroot/spambayes/website/apps/outlook/bugs.txt,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** bugs.txt 27 Oct 2003 09:40:38 -0000 1.2 --- bugs.txt 5 Dec 2003 05:12:33 -0000 1.3 *************** *** 75,78 **** --- 75,96 ---- .. _FAQ entry on this issue: http://spambayes.sourceforge.net/faq.html#how-can-i-get-rid-of-the-envelope-tray-icon-for-spam + Addin loads with "Could not watch the specified folders" error + -------------------------------------------------------------- + In this bug, the log file contains: + + :: + + ... + File "out1.pyz/addin", line 1273, in _HookFolderEvents + File "out1.pyz/msgstore", line 539, in GetOutlookItem + File "...", line 4597, in GetFolderFromID + ... + com_error: (-2147352567, 'Exception occurred.', (4096, 'Microsoft Office Outlook', 'The operation failed.',None, 0, -2147221241), None) + + We are tracking this bug in + `[ 843521 ] E_FAIL exception watching folders disables SpamBayes`_. + + .. _[ 843521 ] E_FAIL exception watching folders disables SpamBayes: https://sourceforge.net/tracker/index.php?func=detail&aid=843521&group_id=61702&atid=498103 + Customize Outlook Today stops working ------------------------------------- From montanaro at users.sourceforge.net Fri Dec 5 16:23:37 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Fri Dec 5 16:23:40 2003 Subject: [Spambayes-checkins] spambayes/contrib spamcounts.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/contrib In directory sc8-pr-cvs1:/tmp/cvs-serv1794 Modified Files: spamcounts.py Log Message: allow the user to see the 'saved state' token Index: spamcounts.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/contrib/spamcounts.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** spamcounts.py 20 Nov 2003 02:50:03 -0000 1.2 --- spamcounts.py 5 Dec 2003 21:23:35 -0000 1.3 *************** *** 71,75 **** seen.add(t) ! sc, hc = db.get(t, (0, 0)) if sc == hc == 0: continue --- 71,78 ---- seen.add(t) ! try: ! sc, hc = db.get(t, (0, 0)) ! except ValueError: ! _, sc, hc = db.get(t, (0, 0, 0)) if sc == hc == 0: continue From mhammond at users.sourceforge.net Sun Dec 7 19:55:45 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 7 19:55:49 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.114,1.115 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv29168 Modified Files: addin.py Log Message: A number of changes to better support us existing in the 'COM Addins' list when running the binary version: * By default, no longer register in both HKLM and HKCU - registering in both means we don't appear in the addins list. * Support special registration command-line to register in HKLM instead of the default HKCU - see the comments on this * Support the addin being selected and de-selected multiple times via the Outlook GUI. This was mainly removing old object references when 'Close' is called. * When the user manually de-selects us via the 'Addins' list, we remove the SpamBayes toolbar. TODO: somehow exploit this so that at uninstall time we can also remove the toolbar. Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.114 retrieving revision 1.115 diff -C2 -d -r1.114 -r1.115 *** addin.py 27 Oct 2003 03:36:24 -0000 1.114 --- addin.py 8 Dec 2003 00:55:43 -0000 1.115 *************** *** 3,6 **** --- 3,7 ---- import warnings import traceback + import _winreg # *sigh* - this is for the binary installer, and for the sake of one line *************** *** 86,94 **** gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True, bValidateFile=bValidateGencache) # Office 9 - # Register what vtable based interfaces we need to implement. - # Damn - we should use EnsureModule for the _IDTExtensibility2 typelib, but - # win32all 155 and earlier don't like us pre-generating :( - universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, ["_IDTExtensibility2"]) try: from win32com.client import CastTo, WithEvents --- 87,97 ---- gencache.EnsureModule('{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}', 0, 2, 1, bForDemand=True, bValidateFile=bValidateGencache) # Office 9 + # We the "Addin Designer" typelib for its constants + gencache.EnsureModule('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, + bForDemand=True, bValidateFile=bValidateGencache) + # ... and also for its _IDTExtensibility2 vtable interface. + universal.RegisterInterfaces('{AC0714F2-3D04-11D1-AE7D-00A0C90F26F4}', 0, 1, 0, + ["_IDTExtensibility2"]) try: from win32com.client import CastTo, WithEvents *************** *** 965,980 **** # for not broken - can't find toolbar. Create a new one. # Create it as a permanent one (which is default) - if self.explorers_collection.have_created_toolbar: - # Eeek - we have already created a toolbar, but - # now we can't find it. It is likely this is the - # first time we are being run, and outlook is - # being started with multiple Windows open. - # Hopefully things will get back to normal once - # Outlook is restarted (which testing shows it does) - return - print "Creating new SpamBayes toolbar to host our buttons" ! self.toolbar = bars.Add(toolbar_name, constants.msoBarTop, Temporary=False) ! self.explorers_collection.have_created_toolbar = True self.toolbar.Visible = True parent = self.toolbar --- 968,975 ---- # for not broken - can't find toolbar. Create a new one. # Create it as a permanent one (which is default) print "Creating new SpamBayes toolbar to host our buttons" ! self.toolbar = bars.Add(toolbar_name, ! constants.msoBarTop, ! Temporary=False) self.toolbar.Visible = True parent = self.toolbar *************** *** 1122,1129 **** self.manager = manager self.explorers = [] - self.have_created_toolbar = False self.button_event_map = {} def Close(self): self.explorers = None --- 1117,1125 ---- self.manager = manager self.explorers = [] self.button_event_map = {} def Close(self): + while self.explorers: + self._DoDeadExplorer(self.explorers[0]) self.explorers = None *************** *** 1228,1231 **** --- 1224,1232 ---- # still screw up. self.manager.LogDebug(0, "*** SpamBayes is NOT enabled, so will not filter incoming mail. ***") + if connectMode == constants.ext_cm_AfterStartup: + # We are being enabled after startup, which means we don't get + # the 'OnStartupComplete()' event - call it manually so we + # bootstrap code that can't happen until startup is complete. + self.OnStartupComplete(None) except: print "Error connecting to Outlook!" *************** *** 1331,1336 **** hook.Close() self.folder_hooks = None ! self.application = None ! self.explorers_events = None if self.manager is not None: # Save database - bsddb databases will generally do nothing here --- 1332,1338 ---- hook.Close() self.folder_hooks = None ! if self.explorers_events is not None: ! self.explorers_events.Close() ! self.explorers_events = None if self.manager is not None: # Save database - bsddb databases will generally do nothing here *************** *** 1344,1347 **** --- 1346,1370 ---- self.manager = None + if mode==constants.ext_dm_UserClosed: + # The user has de-selected us. Remove the toolbars we created + # (Maybe we can exploit this later to remove toolbars as part + # of uninstall?) + print "SpamBayes is being manually disabled - deleting toolbar" + try: + explorers = self.application.Explorers + for i in range(explorers.Count): + explorer = explorers.Item(i+1) + try: + toolbar = explorer.CommandBars.Item(toolbar_name) + except pythoncom.com_error: + print "Could not find our toolbar to delete!" + else: + toolbar.Delete() + except: + print "ERROR deleting toolbar" + traceback.print_exc() + + self.application = None + print "Addin terminating: %d COM client and %d COM servers exist." \ % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()) *************** *** 1373,1377 **** def _DoRegister(klass, root): - import _winreg key = _winreg.CreateKey(root, "Software\\Microsoft\\Office\\Outlook\\Addins") --- 1396,1399 ---- *************** *** 1382,1404 **** _winreg.SetValueEx(subkey, "FriendlyName", 0, _winreg.REG_SZ, "SpamBayes") ! def RegisterAddin(klass): ! import _winreg ! # Try and register twice - once in HKLM, and once in HKCU. This seems ! # to help roaming profiles, etc. Once registered, it is both registered ! # on this machine for the current user (even when they roam, assuming it ! # has been installed on the remote machine also) and for any user on this ! # machine. ! try: _DoRegister(klass, _winreg.HKEY_LOCAL_MACHINE) except WindowsError: - # But they may not have the rights to install there. pass - # We don't catch exception registering just for this user though - # that is fatal! _DoRegister(klass, _winreg.HKEY_CURRENT_USER) print "Registration complete." ! def UnregisterAddin(klass): ! import _winreg try: _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, --- 1404,1452 ---- _winreg.SetValueEx(subkey, "FriendlyName", 0, _winreg.REG_SZ, "SpamBayes") ! # Note that Addins can be registered either in HKEY_CURRENT_USER or ! # HKEY_LOCAL_MACHINE. If the former, then: ! # * Only available for the user that installed the addin. ! # * Appears in the 'COM Addins' list, and can be removed by the user. ! # If HKEY_LOCAL_MACHINE: ! # * Available for every user who uses the machine. This is useful for site ! # admins, so it works with "roaming profiles" as users move around. ! # * Does not appear in 'COM Addins', and thus can not be disabled by the user. ! ! # Note that if the addin is registered in both places, it acts as if it is ! # only installed in HKLM - ie, does not appear in the addins list. ! # For this reason, the addin can be registered in HKEY_LOCAL_MACHINE ! # by executing 'regsvr32 /i:hkey_local_machine outlook_addin.dll' ! # (or 'python addin.py hkey_local_machine' for source code users. ! # Note to Binary Builders: You need py2exe dated 8-Dec-03+ for this to work. ! ! # Called when "regsvr32 /i:whatever" is used. We support 'hkey_local_machine' ! def DllInstall(bInstall, cmdline): ! klass = OutlookAddin ! if bInstall and cmdline.lower().find('hkey_local_machine')>=0: ! # Unregister the old installation, if one exists. ! DllUnregisterServer() ! # Don't catch exceptions here - if it fails, the Dll registration ! # must fail. _DoRegister(klass, _winreg.HKEY_LOCAL_MACHINE) + print "Registration (in HKEY_LOCAL_MACHINE) complete." + + def DllRegisterServer(): + klass = OutlookAddin + # *sigh* - we used to *also* register in HKLM, but as above, this makes + # things work like we are *only* installed in HKLM. Thus, we explicitly + # remove the HKLM registration here (but it can be re-added - see the + # notes above.) + try: + _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, + "Software\\Microsoft\\Office\\Outlook\\Addins\\" \ + + klass._reg_progid_) except WindowsError: pass _DoRegister(klass, _winreg.HKEY_CURRENT_USER) print "Registration complete." ! def DllUnregisterServer(): ! klass = OutlookAddin ! # Try to remove the HKLM version. try: _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, *************** *** 1415,1429 **** pass - def DllRegisterServer(): - RegisterAddin(OutlookAddin) - - def DllUnregisterServer(): - UnregisterAddin(OutlookAddin) - if __name__ == '__main__': import win32com.server.register win32com.server.register.UseCommandLine(OutlookAddin) if "--unregister" in sys.argv: DllUnregisterServer() else: DllRegisterServer() --- 1463,1483 ---- pass if __name__ == '__main__': import win32com.server.register win32com.server.register.UseCommandLine(OutlookAddin) + # todo - later win32all versions of UseCommandLine support + # finalize_register and finalize_unregister keyword args, passing the + # functions. + # (But DllInstall may get support in UseCommandLine later, so let's + # wait and see) if "--unregister" in sys.argv: DllUnregisterServer() else: DllRegisterServer() + # Support 'hkey_local_machine' on the commandline, to work in + # the same way as 'regsvr32 /i:hkey_local_machine' does. + # regsvr32 calls it after DllRegisterServer, (and our registration + # logic relies on that) so we will too. + for a in sys.argv[1:]: + if a.lower()=='hkey_local_machine': + DllInstall(True, 'hkey_local_machine') From mhammond at users.sourceforge.net Sun Dec 7 19:59:21 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 7 19:59:24 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.88,1.89 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv29828 Modified Files: manager.py Log Message: * Add/Fix a number of 'unicode file' related comments. * Allow multiple manager objects to work in the same process (but not at the same time): - If the global manager is being closed, drop the global var holding it, so the next request for the manager will make a new one. - In this case, 'spambayes.Options' has already been imported, so our simple 'imported too early' check failed. If spambayes.Options has been imported, we assert that the INI file it is using is our INI. * Wrap a few long lines. Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.88 retrieving revision 1.89 diff -C2 -d -r1.88 -r1.89 *** manager.py 29 Sep 2003 02:14:25 -0000 1.88 --- manager.py 8 Dec 2003 00:59:19 -0000 1.89 *************** *** 60,66 **** win32con.MB_ICONQUESTION) == win32con.IDYES ! # Notes on Unicode directory names ! # You will have much more success with extended characters in ! # directory names using Python 2.3. try: filesystem_encoding = sys.getfilesystemencoding() --- 60,65 ---- win32con.MB_ICONQUESTION) == win32con.IDYES ! # Non-ascii characters in file or directory names only fully work in ! # Python 2.3.3+, but latin-1 "compatible" filenames should work in 2.2 try: filesystem_encoding = sys.getfilesystemencoding() *************** *** 71,85 **** # the directory of our main .py/.dll/.exe file we # are running from. ! try: ! if hasattr(sys, "frozen"): ! if sys.frozen == "dll": ! this_filename = win32api.GetModuleFileName(sys.frozendllhandle) ! else: ! # Don't think we will ever run as a .EXE, but... ! this_filename = os.path.abspath(sys.argv[0]) ! else: this_filename = os.path.abspath(__file__) ! except NameError: # no __file__ ! this_filename = os.path.abspath(sys.argv[0]) # See if we can use the new bsddb module. (The old one is unreliable --- 70,81 ---- # the directory of our main .py/.dll/.exe file we # are running from. ! if hasattr(sys, "frozen"): ! assert sys.frozen == "dll", "outlook only supports inproc servers" ! this_filename = win32api.GetModuleFileName(sys.frozendllhandle) ! else: ! try: this_filename = os.path.abspath(__file__) ! except NameError: # no __file__ - means Py2.2 and __name__=='__main__' ! this_filename = os.path.abspath(sys.argv[0]) # See if we can use the new bsddb module. (The old one is unreliable *************** *** 96,99 **** --- 92,97 ---- except ImportError: # No DB library at all! + assert not hasattr(sys, "frozen"), \ + "Don't build binary versions without bsbbd!" use_db = False *************** *** 117,122 **** def import_core_spambayes_stuff(ini_filename): ! assert "spambayes.Options" not in sys.modules, \ ! "'spambayes.Options' was imported too early" global bayes_classifier, bayes_tokenize, bayes_storage # ini_filename is Unicode, but environ not unicode aware --- 115,129 ---- def import_core_spambayes_stuff(ini_filename): ! if "spambayes.Options" in sys.modules: ! # Manager probably being re-initialized (via the Outlook 'addin' GUI ! # Check that nothing has changed underneath us. ! if __debug__: ! import spambayes.Options ! assert spambayes.Options.optionsPathname == \ ! ini_filename.encode(filesystem_encoding), \ ! "'spambayes.Options' was imported too early, with the " \ ! "incorrect directory %r" \ ! % (spambayes.Options.optionsPathname,) ! return global bayes_classifier, bayes_tokenize, bayes_storage # ini_filename is Unicode, but environ not unicode aware *************** *** 131,138 **** "Expected 'spambayes.Options' to be loaded here" - class ManagerError(Exception): - pass - - # Function to "safely" save a pickle, only overwriting # the existing file after a successful write. --- 138,141 ---- *************** *** 351,361 **** if not os.path.isdir(value): os.makedirs(value) ! if not os.path.isdir(value): ! raise os.error value = os.path.abspath(value) except os.error: ! print "The configuration files have specified a data directory of" ! print repr(value) ! print "but it is not valid. Using default" value = None if value: --- 354,363 ---- if not os.path.isdir(value): os.makedirs(value) ! assert os.path.isdir(value), "just made the *ucker" value = os.path.abspath(value) except os.error: ! print "The configuration files have specified a data " \ ! "directory of", repr(value), "but it is not valid. " \ ! "Using default." value = None if value: *************** *** 373,377 **** self.LoadConfig() ! bayes_options_filename = os.path.join(self.data_directory, "default_bayes_customize.ini") import_core_spambayes_stuff(bayes_options_filename) --- 375,380 ---- self.LoadConfig() ! bayes_options_filename = os.path.join(self.data_directory, ! "default_bayes_customize.ini") import_core_spambayes_stuff(bayes_options_filename) *************** *** 487,491 **** # A bit of a nod to save people doing a full retrain. # Try and locate our files in the old location, and move ! # then to the new one. # Also used first time SpamBayes is run - this will cause # the ini file to be *copied* to the correct directory --- 490,494 ---- # A bit of a nod to save people doing a full retrain. # Try and locate our files in the old location, and move ! # them to the new one. # Also used first time SpamBayes is run - this will cause # the ini file to be *copied* to the correct directory *************** *** 753,762 **** def Close(self): self.classifier_data.Close() ! self.config = None if self.message_store is not None: self.message_store.Close() self.message_store = None self.outlook = None def score(self, msg, evidence=False): --- 756,770 ---- def Close(self): + global _mgr self.classifier_data.Close() ! self.config = self.options = None if self.message_store is not None: self.message_store.Close() self.message_store = None self.outlook = None + self.addin = None + # If we are the global manager, reset that + if _mgr is self: + _mgr = None def score(self, msg, evidence=False): *************** *** 850,860 **** def main(verbose_level = 1): ! try: ! mgr = GetManager() ! mgr.verbose = max(mgr.verbose, verbose_level) ! except ManagerError, d: ! print "Error initializing Bayes manager" ! print d ! return 1 ShowManager(mgr) mgr.Save() --- 858,863 ---- def main(verbose_level = 1): ! mgr = GetManager() ! mgr.verbose = max(mgr.verbose, verbose_level) ShowManager(mgr) mgr.Save() From mhammond at users.sourceforge.net Sun Dec 7 20:00:53 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 7 20:00:56 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 about.html,1.25,1.26 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv30500 Modified Files: about.html Log Message: Add notes about how to register SpamBayes globally for all users on the machine. Index: about.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/about.html,v retrieving revision 1.25 retrieving revision 1.26 diff -C2 -d -r1.25 -r1.26 *** about.html 5 Nov 2003 13:05:14 -0000 1.25 --- about.html 8 Dec 2003 01:00:49 -0000 1.26 *************** *** 136,139 **** --- 136,147 ---- rules based on user-defined fields. That's why this addin supplies its own filtering rules.
+

Installing for all users on a machine

+ By default, SpamBayes will install itself only for the user who + actually installed it.  If other users log onto the same machine, + their Outlook installation will not have SpamBayes available.  + Even though this is the way SpamBayes is designed, our troubleshooting + guide has details + on how you can change this.
+

Your help is needed!

This is free software.  Please offer any help you are able From mhammond at users.sourceforge.net Sun Dec 7 20:00:54 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 7 20:00:58 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html, 1.16, 1.17 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv30500/docs Modified Files: troubleshooting.html Log Message: Add notes about how to register SpamBayes globally for all users on the machine. Index: troubleshooting.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** troubleshooting.html 5 Nov 2003 13:05:14 -0000 1.16 --- troubleshooting.html 8 Dec 2003 01:00:51 -0000 1.17 *************** *** 18,21 **** --- 18,24 ----

  • Messages have incorrect or unexpected Spam values
  • +
  • SpamBayes is not available for all + users on the machine
    +
  • All other problems
  • *************** *** 98,102 ****
  • If the SpamBayes addin is not listed, then SpamBayes should be reinstalled (Note that running regsvr32.exe ! spambayes_addin.dll from the SpamBayes directory may also solve this problem)
  • If the SpamBayes addin is listed but not checked, then simply --- 101,105 ----
  • If the SpamBayes addin is not listed, then SpamBayes should be reinstalled (Note that running regsvr32.exe ! outlook_addin.dll from the SpamBayes directory may also solve this problem)
  • If the SpamBayes addin is listed but not checked, then simply *************** *** 192,195 **** --- 195,223 ---- +

    SpamBayes is not available for all + users on the machine.

    + When SpamBayes is installed, by default it is available only for the + user who installed it.  This is to allow SpamBayes to appear in + Microsoft Outlook's Com-Addin list, and therefore able to be activated + and de-activated by the user inside Outlook.
    + It is possible to register the addin so it is available to all users on + a particular machine, which can be useful in enterprise arrangements + where users have 'roaming profiles'
    + To register SpamBayes in this way, you must execute the command + (obviously with the correct patch substituted):
    +     regsvr32.exe + /i:hkey_local_machine "c:\Program Files\SpamBayes\bin\outlook_addin.dll"
    + If you check the installation log after + performing such an install, you should see the following messages:
    +     Registered: + SpamBayes.OutlookAddin
    +     Registration + complete.
    +     Registration (in + HKEY_LOCAL_MACHINE) complete.
    + Note the last line, which does not exist when registration is performed + only for the current user.  Once you have performed this + registration, the Addin will be available for all users - but as noted + above, it will no longer appear in Outlook's Com-Addin list.

    All other problems

    If you are simply unsure about what SpamBayes is doing, please Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv20527 Modified Files: manager.py Log Message: Oops - typo in comment - latin-1, non-ascii filenames work in 2.3, not 2.2 Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.89 retrieving revision 1.90 diff -C2 -d -r1.89 -r1.90 *** manager.py 8 Dec 2003 00:59:19 -0000 1.89 --- manager.py 8 Dec 2003 12:07:37 -0000 1.90 *************** *** 61,65 **** # Non-ascii characters in file or directory names only fully work in ! # Python 2.3.3+, but latin-1 "compatible" filenames should work in 2.2 try: filesystem_encoding = sys.getfilesystemencoding() --- 61,65 ---- # Non-ascii characters in file or directory names only fully work in ! # Python 2.3.3+, but latin-1 "compatible" filenames should work in 2.3 try: filesystem_encoding = sys.getfilesystemencoding() From mhammond at users.sourceforge.net Mon Dec 8 22:59:04 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Dec 8 22:59:07 2003 Subject: [Spambayes-checkins] website faq.txt,1.52,1.53 Message-ID: Update of /cvsroot/spambayes/website In directory sc8-pr-cvs1:/tmp/cvs-serv6929 Modified Files: faq.txt Log Message: Clarification in Outlook training, as suggested by a user. Index: faq.txt =================================================================== RCS file: /cvsroot/spambayes/website/faq.txt,v retrieving revision 1.52 retrieving revision 1.53 diff -C2 -d -r1.52 -r1.53 *** faq.txt 3 Dec 2003 03:29:26 -0000 1.52 --- faq.txt 9 Dec 2003 03:59:00 -0000 1.53 *************** *** 757,770 **** ----------------------------------------------- ! Instructions about training the Outlook plugin can be found in the documentation ! for the plugin. However, basically what you need to do is move as much spam ! as you have into your spam folder, tell the plugin which folder that is and ! which folders contain examples of ham, and it will do the rest. The plugin does *not* train on all incoming mail. However, if you use the "Delete as spam" and "Recover from spam" buttons, those messages will be (re)trained as necessary. If you have set it to use incremental training ! then it will also train on messages which are moved into the spam folder ! and those folders that you are 'watching'. --- 757,772 ---- ----------------------------------------------- ! Instructions about training the Outlook plugin can be found in the ! documentation for the plugin, and the 'Configuration Wizard' will attempt to ! guide you through an initial training process. Basically what you need to do ! is move as much spam as you have into your spam folder, tell the plugin which ! folder that is and which folders contain examples of ham, and it will do ! the rest. The plugin does *not* train on all incoming mail. However, if you use the "Delete as spam" and "Recover from spam" buttons, those messages will be (re)trained as necessary. If you have set it to use incremental training ! then it will also train on messages which are manually moved into the spam ! folder and those folders that you are 'watching'. From mhammond at users.sourceforge.net Mon Dec 8 23:02:46 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Dec 8 23:02:48 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs/resources dialogs.rc, 1.41, 1.42 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources In directory sc8-pr-cvs1:/tmp/cvs-serv7459 Modified Files: dialogs.rc Log Message: Make it clear in the config wizard that using the 'Browse' button allows them to de-select the list of folders. Incremental training dialog: s/Inbox/watched folders/, as suggested by a user. Index: dialogs.rc =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/dialogs.rc,v retrieving revision 1.41 retrieving revision 1.42 diff -C2 -d -r1.41 -r1.42 *** dialogs.rc 3 Dec 2003 23:45:38 -0000 1.41 --- dialogs.rc 9 Dec 2003 04:02:43 -0000 1.42 *************** *** 224,229 **** LTEXT "SpamBayes needs to know what folders are used to receive new messages. In most cases, this will be your Inbox, but you may also specify additional folders to be watched for spam.", IDC_STATIC,20,21,247,25 ! LTEXT "The following folders will be watched for new messages", ! IDC_STATIC,20,90,247,13 LTEXT "If you use the Outlook rule wizard to move messages into folders, you may like to select these folders in addition to your inbox.", IDC_STATIC,20,51,241,20 --- 224,229 ---- LTEXT "SpamBayes needs to know what folders are used to receive new messages. In most cases, this will be your Inbox, but you may also specify additional folders to be watched for spam.", IDC_STATIC,20,21,247,25 ! LTEXT "The following folders will be watched for new messages. Use the Browse button to change the list, or Next if the list of folders is correct.", ! IDC_STATIC,20,79,247,20 LTEXT "If you use the Outlook rule wizard to move messages into folders, you may like to select these folders in addition to your inbox.", IDC_STATIC,20,51,241,20 *************** *** 520,525 **** SS_CENTERIMAGE | SS_SUNKEN,11,21,175,12 PUSHBUTTON "&Browse...",IDC_BROWSE_HAM,192,20,50,14 ! LTEXT "Folders with spam or other junk messages.",IDC_STATIC,11, ! 36,171,9 CONTROL "Static",IDC_STATIC_SPAM,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_SUNKEN | WS_GROUP,11,46,174,12 --- 520,525 ---- SS_CENTERIMAGE | SS_SUNKEN,11,21,175,12 PUSHBUTTON "&Browse...",IDC_BROWSE_HAM,192,20,50,14 ! LTEXT "Folders with spam or other junk messages.",IDC_STATIC, ! 11,36,171,9 CONTROL "Static",IDC_STATIC_SPAM,"Static",SS_LEFTNOWORDWRAP | SS_CENTERIMAGE | SS_SUNKEN | WS_GROUP,11,46,174,12 *************** *** 535,539 **** IDC_PROGRESS_TEXT,75,89,149,17 GROUPBOX "Incremental Training",IDC_STATIC,4,117,244,87 ! CONTROL "Train that a message is good when it is moved from a spam folder back to the Inbox", IDC_BUT_TRAIN_FROM_SPAM_FOLDER,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,11,127,204,18 --- 535,539 ---- IDC_PROGRESS_TEXT,75,89,149,17 GROUPBOX "Incremental Training",IDC_STATIC,4,117,244,87 ! CONTROL "Train that a message is good when it is moved from a spam folder back to the Inbox.", IDC_BUT_TRAIN_FROM_SPAM_FOLDER,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,11,127,204,18 *************** *** 542,546 **** COMBOBOX IDC_RECOVER_RS,127,145,114,54,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP ! CONTROL "Train that a message is spam when it is moved to the spam folder", IDC_BUT_TRAIN_TO_SPAM_FOLDER,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,11,163,204,16 --- 542,546 ---- COMBOBOX IDC_RECOVER_RS,127,145,114,54,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP ! CONTROL "Train that a message is spam when it is moved to the spam folder.", IDC_BUT_TRAIN_TO_SPAM_FOLDER,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,11,163,204,16 From montanaro at users.sourceforge.net Tue Dec 9 10:36:32 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Tue Dec 9 10:36:36 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py,1.15,1.16 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv17103 Modified Files: tokenizer.py Log Message: tweak docstring to match current options practice Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** tokenizer.py 18 Sep 2003 21:00:10 -0000 1.15 --- tokenizer.py 9 Dec 2003 15:36:29 -0000 1.16 *************** *** 1356,1361 **** """Generate a stream of tokens from an email Message. ! If options.check_octets is True, the first few undecoded characters ! of application/octet-stream parts of the message body become tokens. """ --- 1356,1362 ---- """Generate a stream of tokens from an email Message. ! If options['Tokenizer', 'check_octets'] is True, the first few ! undecoded characters of application/octet-stream parts of the ! message body become tokens. """ From montanaro at users.sourceforge.net Tue Dec 9 10:52:15 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Tue Dec 9 10:52:19 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionsClass.py,1.15,1.16 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv20010 Modified Files: OptionsClass.py Log Message: Loosen constraints on HEADER_VALUE regular expression. Add an experimental/deprecation capability. (This is more difficult to describe than to use...) Options which are experimental or deprecated are prefixed with 'X-'. The distinction between the two rather artificial. The description of deprecated options should start with "(DEPRECATED)". There are four cases: * option is foo, user sets foo - Set foo silently (current behavior) * option is X-foo, user sets X-foo - Set X-foo silently (current behavior) * option is foo, user sets X-foo - option has moved from experimental to accepted, but the user still sets the option using the experimental form. Set foo silently. * option is X-foo, user sets foo - option has moved from accepted to deprecated, but the user still sets the non-prefixed option. Set X-foo and emit a warning. Of course, this should all be considered experimental for the time being and is subject to change... Index: OptionsClass.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** OptionsClass.py 12 Nov 2003 22:01:39 -0000 1.15 --- OptionsClass.py 9 Dec 2003 15:52:12 -0000 1.16 *************** *** 22,25 **** --- 22,41 ---- via an instance of this class. + Experimental or deprecated options are prefixed with 'X-', borrowing the + practice from RFC-822 mail. If the user sets an option like: + + [Tokenizer] + X-transmogrify: True + + and an 'X-transmogrify' or 'transmogrify' option exists, it is set silently + to the value given by the user. If the user sets an option like: + + [Tokenizer] + transmogrify: True + + and no 'transmogrify' option exists, but an 'X-transmogrify' option does, + the latter is set to the value given by the users and a deprecation message + is printed to standard error. + To Do: o Stop allowing invalid options in configuration files *************** *** 495,498 **** --- 511,520 ---- self.merge_file(file) + def convert_and_set(self, section, option, value): + if self.multiple_values_allowed(section, option): + value = self.convert(section, option, value) + value = self.convert(section, option, value) + self.set(section, option, value) + def merge_file(self, filename): import ConfigParser *************** *** 505,516 **** option = opt if not self._options.has_key((section, option)): ! print >> sys.stderr, ("Invalid option %s in" ! " section %s in file %s" % ! (opt, sect, filename)) else: ! if self.multiple_values_allowed(section, option): ! value = self.convert(section, option, value) ! value = self.convert(section, option, value) ! self.set(section, option, value) # not strictly necessary, but convenient shortcuts to self._options --- 527,554 ---- option = opt if not self._options.has_key((section, option)): ! if option.startswith('x-'): ! # try setting option without the x- prefix ! option = option[2:] ! if self._options.has_key((section, option)): ! self.convert_and_set(section, option, value) ! # not an error if an x- option is missing ! else: ! option = 'x-'+option ! # going the other way, if the option has been ! # deprecated, set its x-prefixed version and ! # emit a warning ! if self._options.has_key((section, option)): ! self.convert_and_set(section, option, value) ! print >> sys.stderr, ( ! "warning: option %s in" ! " section %s is deprecated" % ! (opt, sect)) ! else: ! print >> sys.stderr, ( ! "warning: Invalid option %s in" ! " section %s in file %s" % ! (opt, sect, filename)) else: ! self.convert_and_set(section, option, value) # not strictly necessary, but convenient shortcuts to self._options *************** *** 637,641 **** def options(self, prepend_section_name=False): ! '''Return a alphabetical list of all the options, optionally prefixed with [section_name]''' all = [] --- 675,679 ---- def options(self, prepend_section_name=False): ! '''Return an alphabetical list of all the options, optionally prefixed with [section_name]''' all = [] *************** *** 691,695 **** # you may use any regex or tuple you wish. HEADER_NAME = r"[\w\.\-\*]+" ! HEADER_VALUE = r"[\w\.\-\*]+" INTEGER = r"[\d]+" # actually, a *positive* integer REAL = r"[\d]+[\.]?[\d]*" # likewise, a *positive* real --- 729,733 ---- # you may use any regex or tuple you wish. HEADER_NAME = r"[\w\.\-\*]+" ! HEADER_VALUE = r".+" INTEGER = r"[\d]+" # actually, a *positive* integer REAL = r"[\d]+[\.]?[\d]*" # likewise, a *positive* real From montanaro at users.sourceforge.net Tue Dec 9 11:04:18 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Tue Dec 9 11:04:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py, 1.16, 1.17 Options.py, 1.83, 1.84 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv22154 Modified Files: tokenizer.py Options.py Log Message: deprecate generate_time_buckets and extract_dow Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** tokenizer.py 9 Dec 2003 15:36:29 -0000 1.16 --- tokenizer.py 9 Dec 2003 16:04:16 -0000 1.17 *************** *** 1292,1296 **** # Date: ! if options["Tokenizer", "generate_time_buckets"]: for header in msg.get_all("date", ()): mat = self.date_hms_re.search(header) --- 1292,1296 ---- # Date: ! if options["Tokenizer", "x-generate_time_buckets"]: for header in msg.get_all("date", ()): mat = self.date_hms_re.search(header) *************** *** 1302,1306 **** yield 'time:%02d:%d' % (h, bucket) ! if options["Tokenizer", "extract_dow"]: for header in msg.get_all("date", ()): # extract the day of the week --- 1302,1306 ---- yield 'time:%02d:%d' % (h, bucket) ! if options["Tokenizer", "x-extract_dow"]: for header in msg.get_all("date", ()): # extract the day of the week Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.83 retrieving revision 1.84 diff -C2 -d -r1.83 -r1.84 *** Options.py 21 Oct 2003 21:42:22 -0000 1.83 --- Options.py 9 Dec 2003 16:04:16 -0000 1.84 *************** *** 137,147 **** INTEGER, RESTORE), ! ("generate_time_buckets", "Generate time buckets", False, ! """Generate tokens which resemble the posting time in 10-minute ! buckets: 'time:' hour ':' minute//10""", BOOLEAN, RESTORE), ! ("extract_dow", "Extract day-of-week", False, ! """Extract day of the week tokens from the Date: header.""", BOOLEAN, RESTORE), --- 137,147 ---- INTEGER, RESTORE), ! ("X-generate_time_buckets", "Generate time buckets", False, ! """(DEPRECATED) Generate tokens which resemble the posting time ! in 10-minute buckets: 'time:' hour ':' minute//10""", BOOLEAN, RESTORE), ! ("X-extract_dow", "Extract day-of-week", False, ! """(DEPRECATED) Extract day of the week tokens from the Date: header.""", BOOLEAN, RESTORE), From montanaro at users.sourceforge.net Tue Dec 9 11:31:41 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Tue Dec 9 11:31:55 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.84, 1.85 OptionsClass.py, 1.16, 1.17 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27984/spambayes Modified Files: Options.py OptionsClass.py Log Message: Friggin' Windows... Why couldn't they do case sensitivity? Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.84 retrieving revision 1.85 diff -C2 -d -r1.84 -r1.85 *** Options.py 9 Dec 2003 16:04:16 -0000 1.84 --- Options.py 9 Dec 2003 16:31:39 -0000 1.85 *************** *** 137,146 **** INTEGER, RESTORE), ! ("X-generate_time_buckets", "Generate time buckets", False, """(DEPRECATED) Generate tokens which resemble the posting time in 10-minute buckets: 'time:' hour ':' minute//10""", BOOLEAN, RESTORE), ! ("X-extract_dow", "Extract day-of-week", False, """(DEPRECATED) Extract day of the week tokens from the Date: header.""", BOOLEAN, RESTORE), --- 137,146 ---- INTEGER, RESTORE), ! ("x-generate_time_buckets", "Generate time buckets", False, """(DEPRECATED) Generate tokens which resemble the posting time in 10-minute buckets: 'time:' hour ':' minute//10""", BOOLEAN, RESTORE), ! ("x-extract_dow", "Extract day-of-week", False, """(DEPRECATED) Extract day of the week tokens from the Date: header.""", BOOLEAN, RESTORE), Index: OptionsClass.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** OptionsClass.py 9 Dec 2003 15:52:12 -0000 1.16 --- OptionsClass.py 9 Dec 2003 16:31:39 -0000 1.17 *************** *** 528,536 **** if not self._options.has_key((section, option)): if option.startswith('x-'): ! # try setting option without the x- prefix option = option[2:] if self._options.has_key((section, option)): self.convert_and_set(section, option, value) ! # not an error if an x- option is missing else: option = 'x-'+option --- 528,536 ---- if not self._options.has_key((section, option)): if option.startswith('x-'): ! # try setting option without the X- prefix option = option[2:] if self._options.has_key((section, option)): self.convert_and_set(section, option, value) ! # not an error if an X- option is missing else: option = 'x-'+option From mhammond at users.sourceforge.net Wed Dec 10 00:06:25 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 10 00:06:29 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/sandbox dump_props.py, 1.10, 1.11 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/sandbox In directory sc8-pr-cvs1:/tmp/cvs-serv15812 Modified Files: dump_props.py Log Message: Add new options to dump tables, and dump the magic variable with Outlook's user defined properties. Index: dump_props.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/dump_props.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** dump_props.py 1 Sep 2003 06:04:08 -0000 1.10 --- dump_props.py 10 Dec 2003 05:06:23 -0000 1.11 *************** *** 10,13 **** --- 10,28 ---- import mapi_driver + try: + TBL_ALL_COLUMNS = mapi.TBL_ALL_COLUMNS + except AttributeError: # missing in early versions + TBL_ALL_COLUMNS = 1 + + PR_USERFIELDS = 0x36E30102 # PROP_TAG(PT_BINARY, 0x36e3) + + def GetPropTagName(obj, prop_tag): + hr, tags, array = obj.GetNamesFromIDs( (prop_tag,) ) + if type(array[0][1])==type(u''): + name = array[0][1] + else: + name = mapiutil.GetPropTagName(prop_tag) + return name + # Also in new versions of mapituil def GetAllProperties(obj, make_pretty = True): *************** *** 17,28 **** for tag, val in data: if make_pretty: ! hr, tags, array = obj.GetNamesFromIDs( (tag,) ) ! if type(array[0][1])==type(u''): ! name = array[0][1] ! else: ! name = mapiutil.GetPropTagName(tag) ! # pretty value transformations ! if PROP_TYPE(tag)==PT_ERROR: ! val = mapiutil.GetScodeString(val) else: name = tag --- 32,36 ---- for tag, val in data: if make_pretty: ! name = GetPropTagName(obj, tag) else: name = tag *************** *** 43,67 **** return "".join(chunks) ! def DumpItemProps(item, shorten, get_large_props): ! all_props = GetAllProperties(item) ! all_props.sort() # sort by first tuple item, which is name :) ! for prop_name, prop_tag, prop_val in all_props: ! # Do some magic rtf conversion ! if PROP_ID(prop_tag) == PROP_ID(PR_RTF_COMPRESSED): ! rtf_stream = item.OpenProperty(PR_RTF_COMPRESSED, pythoncom.IID_IStream, ! 0, 0) ! html_stream = mapi.WrapCompressedRTFStream(rtf_stream, 0) ! prop_val = mapi.RTFStreamToHTML(html_stream) ! prop_name = "PR_RTF_COMPRESSED (to HTML)" ! prop_tag = PROP_TAG(PT_STRING8, PR_RTF_COMPRESSED) if get_large_props and \ ! PROP_TYPE(prop_tag)==PT_ERROR and \ ! prop_val in [mapi.MAPI_E_NOT_ENOUGH_MEMORY,'MAPI_E_NOT_ENOUGH_MEMORY']: # Use magic to get a large property. prop_val = GetLargeProperty(item, prop_tag) ! prop_repr = repr(prop_val) ! if shorten: ! prop_repr = prop_repr[:50] print "%-20s: %s" % (prop_name, prop_repr) --- 51,88 ---- return "".join(chunks) ! def FormatPropertyValue(prop_tag, prop_val, item, shorten, get_large_props): ! # Do some magic rtf conversion ! if PROP_ID(prop_tag) == PROP_ID(PR_RTF_COMPRESSED): ! rtf_stream = item.OpenProperty(PR_RTF_COMPRESSED, ! pythoncom.IID_IStream, 0, 0) ! html_stream = mapi.WrapCompressedRTFStream(rtf_stream, 0) ! prop_val = mapi.RTFStreamToHTML(html_stream) ! prop_tag = PROP_TAG(PT_STRING8, PR_RTF_COMPRESSED) ! prop_repr = None ! if PROP_TYPE(prop_tag)==PT_ERROR: if get_large_props and \ ! prop_val in [mapi.MAPI_E_NOT_ENOUGH_MEMORY, ! 'MAPI_E_NOT_ENOUGH_MEMORY']: # Use magic to get a large property. prop_val = GetLargeProperty(item, prop_tag) ! prop_repr = repr(prop_val) ! else: ! prop_val = prop_repr = mapiutil.GetScodeString(prop_val) ! if prop_repr is None: prop_repr = repr(prop_val) ! if shorten: ! prop_repr = prop_repr[:50] ! return prop_repr ! ! def DumpItemProps(item, shorten, get_large_props): ! all_props = GetAllProperties(item) ! all_props.sort() # sort by first tuple item, which is name :) ! for prop_name, prop_tag, prop_val in all_props: ! # If we want 'short' variables, drop 'not found' props. ! if shorten and PROP_TYPE(prop_tag)==PT_ERROR \ ! and prop_val == mapi.MAPI_E_NOT_FOUND: ! continue ! prop_repr = FormatPropertyValue(prop_tag, prop_val, item, ! shorten, get_large_props) print "%-20s: %s" % (prop_name, prop_repr) *************** *** 78,98 **** attach_num = row[0][1] print "Dumping attachment (PR_ATTACH_NUM=%d)" % (attach_num,) ! attach = item.OpenAttach(attach_num, None, mapi.MAPI_DEFERRED_ERRORS) DumpItemProps(attach, shorten, get_large) print print ! def usage(driver): folder_doc = driver.GetFolderNameDoc() msg = """\ ! Usage: %s [-f foldername] subject of the message -f - Search for the message in the specified folder (default = Inbox) -s - Shorten long property values. -a - Include attachments ! -l - Get the data for very large properties -n - Show top-level folder names and exit ! ! Dumps all properties for all messages that match the subject. Subject ! matching is substring and ignore-case. %s --- 99,166 ---- attach_num = row[0][1] print "Dumping attachment (PR_ATTACH_NUM=%d)" % (attach_num,) ! attach = item.OpenAttach(attach_num, None, ! mapi.MAPI_DEFERRED_ERRORS) DumpItemProps(attach, shorten, get_large) print print ! # Generic table dumper. ! def DumpTable(driver, table, name_query_ob, shorten, large_props): ! cols = table.QueryColumns(TBL_ALL_COLUMNS) ! table.SetColumns(cols, 0) ! rows = mapi.HrQueryAllRows(table, cols, None, None, 0) ! print "Table has %d rows, each with %d columns" % (len(rows), len(cols)) ! for row in rows: ! print "-- new row --" ! for col in row: ! prop_tag, prop_val = col ! # If we want 'short' variables, drop 'not found' props. ! if shorten and PROP_TYPE(prop_tag)==PT_ERROR \ ! and prop_val == mapi.MAPI_E_NOT_FOUND: ! continue ! prop_name = GetPropTagName(name_query_ob, prop_tag) ! prop_repr = FormatPropertyValue(prop_tag, prop_val, name_query_ob, ! shorten, large_props) ! print "%-20s: %s" % (prop_name, prop_repr) ! ! # This dumps the raw binary data of the property Outlook uses to store ! # user defined fields. ! def FindAndDumpTableUserProps(driver, table, folder, shorten, get_large_props): ! restriction = (mapi.RES_PROPERTY, ! (mapi.RELOP_EQ, ! PR_MESSAGE_CLASS_A, ! (PR_MESSAGE_CLASS_A, 'IPC.MS.REN.USERFIELDS'))) ! cols = (PR_USERFIELDS,) ! table.SetColumns(cols, 0) ! rows = mapi.HrQueryAllRows(table, cols, restriction, None, 0) ! assert len(rows)<=1, "Only expecting 1 (or 0) rows" ! tag, val = rows[0][0] ! prop_name = GetPropTagName(folder, tag) ! prop_repr = FormatPropertyValue(tag, val, folder, ! shorten, get_large_props) ! print "%-20s: %s" % (prop_name, prop_repr) ! ! def usage(driver, extra = None): folder_doc = driver.GetFolderNameDoc() + if extra: + print extra + print msg = """\ ! Usage: %s [options ...] subject of the message ! ! Dumps all properties for all messages that match the subject. Subject ! matching is substring and ignore-case. ! -f - Search for the message in the specified folder (default = Inbox) -s - Shorten long property values. -a - Include attachments ! -l - Get the data for very large properties via a stream -n - Show top-level folder names and exit ! --dump-folder ! Dump the properties of the specified folder. ! --dump-folder-assoc-contents ! Dump the 'associated contents' table of the specified folder. ! --dump-folder-user-props ! Find and dump the PR_USERFIELDS field for the specified table. %s *************** *** 100,103 **** --- 168,172 ---- % (os.path.basename(sys.argv[0]),folder_doc) print msg + sys.exit(1) def main(): *************** *** 106,115 **** import getopt try: ! opts, args = getopt.getopt(sys.argv[1:], "af:snl") except getopt.error, e: ! print e ! print ! usage(driver) ! sys.exit(1) folder_name = "" --- 175,185 ---- import getopt try: ! opts, args = getopt.getopt(sys.argv[1:], "af:snl", ! ["dump-folder", ! "dump-folder-assoc-contents", ! "dump-folder-user-props", ! ]) except getopt.error, e: ! usage(driver, e) folder_name = "" *************** *** 117,123 **** --- 187,200 ---- get_large_props = False include_attach = False + dump_folder = dump_folder_assoc_contents = dump_folder_user_props = False for opt, opt_val in opts: if opt == "-f": folder_name = opt_val + elif opt == "--dump-folder": + dump_folder = True + elif opt == "--dump-folder-assoc-contents": + dump_folder_assoc_contents = True + elif opt == "--dump-folder-user-props": + dump_folder_user_props = True elif opt == "-s": shorten = True *************** *** 130,135 **** sys.exit(1) else: ! print "Invalid arg" ! return if not folder_name: --- 207,211 ---- sys.exit(1) else: ! usage(driver, "Unknown arg '%s'" % opt) if not folder_name: *************** *** 137,146 **** subject = " ".join(args) ! if not subject: ! print "You must specify a subject" ! print ! usage(driver) ! sys.exit(1) ! try: folder = driver.FindFolder(folder_name) --- 213,224 ---- subject = " ".join(args) ! is_table_dump = dump_folder_assoc_contents or \ ! dump_folder or dump_folder_user_props ! if is_table_dump and subject or not is_table_dump and not subject: ! if is_table_dump: ! extra = "You must not specify a subject with '-p'" ! else: ! extra = "You must specify a subject (unless you use '-p')" ! usage(driver, extra) try: folder = driver.FindFolder(folder_name) *************** *** 149,153 **** sys.exit(1) ! DumpProps(driver, folder, subject, include_attach, shorten, get_large_props) if __name__=='__main__': --- 227,243 ---- sys.exit(1) ! if is_table_dump: ! if dump_folder: ! DumpItemProps(folder, shorten, get_large_props) ! if dump_folder_assoc_contents: ! table = folder.GetContentsTable(mapi.MAPI_ASSOCIATED) ! DumpTable(driver, table, folder, shorten, get_large_props) ! if dump_folder_user_props: ! table = folder.GetContentsTable(mapi.MAPI_ASSOCIATED) ! FindAndDumpTableUserProps(driver, table, folder, ! shorten, get_large_props) ! else: ! DumpProps(driver, folder, subject, include_attach, ! shorten, get_large_props) if __name__=='__main__': From mhammond at users.sourceforge.net Wed Dec 10 00:08:39 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 10 00:08:42 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html, 1.17, 1.18 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv16226/docs Modified Files: troubleshooting.html Log Message: Add link to the "Frequently Reported Bug List", and minor tweaks. Index: troubleshooting.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** troubleshooting.html 8 Dec 2003 01:00:51 -0000 1.17 --- troubleshooting.html 10 Dec 2003 05:08:36 -0000 1.18 *************** *** 23,33 ****
  • All other problems
  • ! There is also an online ! FAQ for SpamBayes which may have additional information of ! use.  You may also wish to see the latest ! online version of this document for problems added since ! release.  If you must send someone a mail about SpamBayes, please read this first.

    Toolbar items appear, but fail to work

    --- 23,43 ----
  • All other problems
  • ! Some other resources that may be useful in tracking down any problems:
    ! ! If you must send someone a mail about SpamBayes, please ! read this first.

    Toolbar items appear, but fail to work

    *************** *** 204,208 **** a particular machine, which can be useful in enterprise arrangements where users have 'roaming profiles'
    ! To register SpamBayes in this way, you must execute the command (obviously with the correct patch substituted):
        regsvr32.exe --- 214,219 ---- a particular machine, which can be useful in enterprise arrangements where users have 'roaming profiles'
    ! To register SpamBayes in this way, you must log on as a user with ! permissions to modify the system registry, then execute the command (obviously with the correct patch substituted):
        regsvr32.exe From mhammond at users.sourceforge.net Wed Dec 10 00:28:12 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 10 00:28:16 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.115,1.116 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv18472 Modified Files: addin.py Log Message: Try and add the Spam field to the 'Unsure' folder in the same way we do for the Spam and watch folders. Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.115 retrieving revision 1.116 diff -C2 -d -r1.115 -r1.116 *** addin.py 8 Dec 2003 00:55:43 -0000 1.115 --- addin.py 10 Dec 2003 05:28:10 -0000 1.116 *************** *** 1266,1269 **** --- 1266,1283 ---- self.manager.ReportFatalStartupError( "Could not watch the specified folders") + # UpdateFolderHooks takes care of ensuring the Outlook field exists + # for all folders we watch - but we never watch the 'Unsure' + # folder, and this one is arguably the most important to have it. + unsure_id = self.manager.config.filter.unsure_folder_id + if unsure_id: + try: + self.manager.EnsureOutlookFieldsForFolder(unsure_id) + except: + # If this fails, just log an error - don't bother with + # the traceback + print "Error adding field to 'Unsure' folder %r" % (unsure_id,) + etype, value, tb = sys.exc_info() + tb = None # dont want it, and nuke circular ref + traceback.print_exception(etype, value, tb) def UpdateFolderHooks(self): From mhammond at users.sourceforge.net Wed Dec 10 00:30:53 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 10 00:30:58 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py, 1.90, 1.91 msgstore.py, 1.77, 1.78 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv18693 Modified Files: manager.py msgstore.py Log Message: Change the way we check for and create user properties: * Reach into where Outlook actually stores UserProperties to check if the field exists. This can be done without opening an item in the folder. * If the field does not exist, create a temporary message and add the properties via that, then delete the temporary message. This then leaves the field in the folder, even when the folder has no items. Fixes [ 856141 ] Spam field not added to unsure or empty folders Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.90 retrieving revision 1.91 diff -C2 -d -r1.90 -r1.91 *** manager.py 8 Dec 2003 12:07:37 -0000 1.90 --- manager.py 10 Dec 2003 05:30:50 -0000 1.91 *************** *** 528,539 **** def EnsureOutlookFieldsForFolder(self, folder_id, include_sub=False): # Ensure that our fields exist on the Outlook *folder* ! # Setting properties via our msgstore (via Ext Mapi) gets the props # on the message OK, but Outlook doesn't see it as a "UserProperty". # Using MAPI to set them directly on the folder also has no effect. ! # So until we know better, use Outlook to hack this in. ! # Should be called once per folder you are watching/filtering etc ! # ! # Oh the tribulations of our property grail # We originally wanted to use the "Integer" Outlook field, # but it seems this property type alone is not expose via the Object --- 528,554 ---- def EnsureOutlookFieldsForFolder(self, folder_id, include_sub=False): + # Should be called at least once once per folder you are + # watching/filtering etc # Ensure that our fields exist on the Outlook *folder* ! # Setting properties via our msgstore (via Ext Mapi) sets the props # on the message OK, but Outlook doesn't see it as a "UserProperty". # Using MAPI to set them directly on the folder also has no effect. ! # Later: We have since discovered that Outlook stores user property ! # information in the 'associated contents' folder - see ! # msgstore.MAPIMsgStoreFolder.DoesFolderHaveOutlookField() for more ! # details. We can reverse engineer this well enough to determine ! # if a property exists, but not well enough to actually add a ! # property. Thus, we resort to the Outlook object model to actually ! # add it. ! # Note that this means we need an object in the folder to modify. ! # We could go searching for an existing item then modify and save it ! # (indeed, we did once), but this could be bad-form, as the message ! # we randomly choose to modify will then have a meaningless 'Spam' ! # field. If we are going to go to the effort of creating a temp ! # item when no item exists, we may as well do it all the time, ! # especially now we know how to check if the folder has the field ! # without opening an Outlook item. ! ! # Regarding the property type: # We originally wanted to use the "Integer" Outlook field, # but it seems this property type alone is not expose via the Object *************** *** 541,600 **** # (which really is OK!) assert self.outlook is not None, "I need outlook :(" ! try: ! msgstore_folder = self.message_store.GetFolder(folder_id) ! except self.message_store.MsgStoreException: ! print "Checking a folder for our field failed - "\ ! "there is no such folder." ! return ! ! folder = msgstore_folder.GetOutlookItem() ! folder_name = msgstore_folder.GetFQName() ! self.LogDebug(2, "Checking folder '%s' for field '%s'" \ ! % (folder_name, self.config.general.field_score_name)) ! items = folder.Items ! item = items.GetFirst() ! while item is not None: ! if item.Class != win32com.client.constants.olMail: ! item = items.GetNext() continue ! break ! # OK - item is either a mail item, or None ! if item is not None: ! ups = item.UserProperties ! # *sigh* - need to search by int index ! for i in range(ups.Count): ! up = ups[i+1] ! if up.Name == self.config.general.field_score_name: ! break ! else: # for not broken ! try: ! # Display format is documented as being the 1-based index in ! # the combo box in the outlook UI for the given data type. ! # 1 is the first - "Rounded", which seems fine. ! format = 1 ! ups.Add(self.config.general.field_score_name, ! win32com.client.constants.olPercent, ! True, # Add to folder ! format) ! item.Save() ! self.LogDebug(2, "Created the UserProperty!") ! except pythoncom.com_error, details: ! if msgstore.IsReadOnlyCOMException(details): ! self.LogDebug(1, "The folder '%s' is read-only - user property can't be added" % \ ! (folder_name,)) ! else: ! print "Warning: failed to create the Outlook " \ ! "user-property in folder '%s'" \ ! % (folder_name,) ! print "", details ! if include_sub: ! # Recurse down the folder list. ! folders = item.Parent.Folders ! folder = folders.GetFirst() ! while folder is not None: ! this_id = folder.StoreID, folder.EntryID ! self.EnsureOutlookFieldsForFolder(this_id, True) ! folder = folders.GetNext() ! # else no items in this folder - not much worth doing! def PrepareConfig(self): --- 556,602 ---- # (which really is OK!) assert self.outlook is not None, "I need outlook :(" ! field_name = self.config.general.field_score_name ! for msgstore_folder in self.message_store.GetFolderGenerator( ! [folder_id], include_sub): ! folder_name = msgstore_folder.GetFQName() ! if msgstore_folder.DoesFolderHaveOutlookField(field_name): ! self.LogDebug(1, "Folder '%s' already has field '%s'" \ ! % (folder_name, field_name)) continue ! self.LogDebug(0, "Folder '%s' has no field named '%s' - creating" \ ! % (folder_name, field_name)) ! # Creating the item via the Outlook model does some strange ! # things (such as moving it to "Drafts" on save), so we create ! # it using extended MAPI (via our msgstore) ! message = msgstore_folder.CreateTemporaryMessage(msg_flags=1) ! outlook_message = message.GetOutlookItem() ! ups = outlook_message.UserProperties ! try: ! # Display format is documented as being the 1-based index in ! # the combo box in the outlook UI for the given data type. ! # 1 is the first - "Rounded", which seems fine. ! format = 1 ! ups.Add(field_name, ! win32com.client.constants.olPercent, ! True, # Add to folder ! format) ! outlook_message.Save() ! except pythoncom.com_error, details: ! if msgstore.IsReadOnlyCOMException(details): ! self.LogDebug(1, "The folder '%s' is read-only - user " ! "property can't be added" % (folder_name,)) ! else: ! print "Warning: failed to create the Outlook " \ ! "user-property in folder '%s'" \ ! % (folder_name,) ! print "", details ! msgstore_folder.DeleteMessages((message,)) ! # Check our DoesFolderHaveOutlookField logic holds up. ! if not msgstore_folder.DoesFolderHaveOutlookField(field_name): ! self.LogDebug(0, ! "WARNING: We just created the user field in folder " ! "%s, but it appears to not exist. Something is " ! "probably wrong with DoesFolderHaveOutlookField()" % \ ! folder_name) def PrepareConfig(self): *************** *** 815,818 **** --- 817,824 ---- # And re-save now, just incase Outlook dies on the way down. self.SaveConfig() + # And tell the addin that our filters may have changed. + if self.addin is not None: + self.addin.FiltersChanged() + def ShowFilterNow(self): import dialogs Index: msgstore.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v retrieving revision 1.77 retrieving revision 1.78 diff -C2 -d -r1.77 -r1.78 *** msgstore.py 10 Oct 2003 01:01:12 -0000 1.77 --- msgstore.py 10 Dec 2003 05:30:50 -0000 1.78 *************** *** 624,627 **** --- 624,708 ---- return self._FolderFromMAPIFolder(ret) + def DoesFolderHaveOutlookField(self, field_name): + # Returns True if the specified folder has an *Outlook* field with + # the given name, False if the folder does not have it, or None + # if we can't tell, or there was an error, etc. + # We have discovered that Outlook stores 'Fields' for a folder as a + # PR_USERFIELDS field in the hidden, 'associated' message with + # message class IPC.MS.REN.USERFIELDS. This is a binary property + # which is undocumented, but probably could be reverse-engineered + # with a little effort (see 'dump_props --dump-folder-user-props' for + # an example of the raw data. For now, the simplest thing appears + # to be to check for a \0 character, followed by the property name + # as an ascii string. + try: + folder = self.msgstore._OpenEntry(self.id) + table = folder.GetContentsTable(mapi.MAPI_ASSOCIATED) + restriction = (mapi.RES_PROPERTY, + (mapi.RELOP_EQ, + PR_MESSAGE_CLASS_A, + (PR_MESSAGE_CLASS_A, 'IPC.MS.REN.USERFIELDS'))) + cols = (PR_USERFIELDS,) + table.SetColumns(cols, 0) + rows = mapi.HrQueryAllRows(table, cols, restriction, None, 0) + if len(rows)>1: + print "Eeek - only expecting one row from IPC.MS.REN.USERFIELDS" + print "got", repr(rows) + return None + if len(rows)==0: + # New folders with no userdefined fields do not have such a row, + # but this is a clear indication it does not exist. + return False + row = rows[0] + val = GetPotentiallyLargeStringProp(folder, cols[0], row[0]) + except pythoncom.com_error, details: + raise MsgStoreExceptionFromCOMException(details) + if type(val) != type(''): + print "Value type incorrect - expected string, got", repr(val) + return None + return val.find("\0" + field_name) >= 0 + + def DeleteMessages(self, message_things): + # A *permanent* delete - MAPI has no concept of 'Deleted Items', + # only Outlook does. If you want a "soft" delete, you must locate + # deleted item (via a special ID) and move it to there yourself + # message_things may be ID tuples, or MAPIMsgStoreMsg instances. + real_ids = [] + for thing in message_things: + if isinstance(thing, MAPIMsgStoreMsg): + real_ids.append( thing.id[1] ) + thing.mapi_object = thing.id = thing.folder_id = None + else: + real_ids.append(self.msgstore.NormalizeID(thing)[1]) + try: + folder = self.msgstore._OpenEntry(self.id) + # Nuke my MAPI reference, and set my ID to None + rc = folder.DeleteMessages(real_ids, 0, None, 0) + except pythoncom.com_error, details: + raise MsgStoreExceptionFromCOMException(details) + + def CreateTemporaryMessage(self, msg_flags = None): + # Create a message designed to be used temporarily. It is your + # responsibility to delete when you are done with it. + # If msg_flags is not None, it should be an integer for the + # PR_MESSAGE_FLAGS property. Note that Outlook appears to refuse + # to set user properties on a message marked as 'unsent', which + # is the default. Setting to, eg, 1 marks it as a "not unsent, read" + # message, which works fine with user properties. + try: + folder = self.msgstore._OpenEntry(self.id) + imsg = folder.CreateMessage(None, 0) + if msg_flags is not None: + props = (PR_MESSAGE_FLAGS,msg_flags), + imsg.SetProps(props) + imsg.SaveChanges(0) + hr, data = imsg.GetProps((PR_ENTRYID, PR_STORE_ENTRYID), 0) + eid = data[0][1] + storeid = data[1][1] + msg_id = mapi.HexFromBin(storeid), mapi.HexFromBin(eid) + except pythoncom.com_error, details: + raise MsgStoreExceptionFromCOMException(details) + return self.msgstore.GetMessage(msg_id) + class MAPIMsgStoreMsg: # All the properties we must initialize a message with. From mhammond at users.sourceforge.net Wed Dec 10 02:18:55 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 10 02:18:59 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.78,1.79 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv1736 Modified Files: msgstore.py Log Message: PR_USERFIELDS only in newer win32alls. Index: msgstore.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v retrieving revision 1.78 retrieving revision 1.79 diff -C2 -d -r1.78 -r1.79 *** msgstore.py 10 Dec 2003 05:30:50 -0000 1.78 --- msgstore.py 10 Dec 2003 07:18:53 -0000 1.79 *************** *** 17,20 **** --- 17,25 ---- import winerror + try: + PR_USERFIELDS # only in new win32all + except NameError: + PR_USERFIELDS = 0x36E30102 # PROP_TAG(PT_BINARY, 0x36e3) + # Additional MAPI constants we dont have in Python MESSAGE_MOVE = 0x1 # from MAPIdefs.h From anadelonbrin at users.sourceforge.net Wed Dec 10 21:56:09 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 10 21:56:13 2003 Subject: [Spambayes-checkins] website faq.txt,1.53,1.54 Message-ID: Update of /cvsroot/spambayes/website In directory sc8-pr-cvs1:/tmp/cvs-serv30214 Modified Files: faq.txt Log Message: Everyone seems to be deleting their spam folder these days. Explain how to fix that. Index: faq.txt =================================================================== RCS file: /cvsroot/spambayes/website/faq.txt,v retrieving revision 1.53 retrieving revision 1.54 diff -C2 -d -r1.53 -r1.54 *** faq.txt 9 Dec 2003 03:59:00 -0000 1.53 --- faq.txt 11 Dec 2003 02:56:07 -0000 1.54 *************** *** 629,632 **** --- 629,658 ---- + Help! I deleted the Unsure/Spam folder. + ---------------------------------------- + Firstly, if you haven't emptied your Deleted Items folder, you can probably + find the unsure/spam folder in it and recover it that way. The little plus + next to the Deleted Items folder will reveal it (if there's no plus, there's + no folder to recover), and you can drag it to where it belongs. + + Otherwise, you need to open up the SpamBayes Manager dialog, click the + "Filtering" tab, and reenter the folder selection (you might need to create + a new unsure/spam folder first - just like you'd create a folder normally). + The dialog will probably have "" in the place where the + unsure/spam folder used to be. Just click Browse and select the correct + folder. + + This should always work. If you're really stuck, though, you can reset + your configuration completely (you'll have to redo any setup, but *not* + any training) by deleting the appropriate .ini file from your `Data Directory`_. + You want to remove the .ini ("Configuration file") that has the same + name as your Outlook profile (for some, this will be something generic like + "Outlook.ini", or "Outlook Internet Settings.ini"). Just remove or rename + this file and your configuration will be reset. Then select the folders as you + would normally. + + .. _`Data Directory`: #can-i-back-up-the-outlook-database-should-i-do-this + + How do I uninstall the plug-in? ------------------------------- From montanaro at users.sourceforge.net Thu Dec 11 13:40:23 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:40:41 2003 Subject: [Spambayes-checkins] spambayes/spambayes Corpus.py,1.9,1.10 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv1217 Modified Files: Corpus.py Log Message: conservative pychecker cleanup Index: Corpus.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Corpus.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** Corpus.py 2 Oct 2003 06:01:12 -0000 1.9 --- Corpus.py 11 Dec 2003 18:40:21 -0000 1.10 *************** *** 92,96 **** import sys # for output of docstring import time - import re from spambayes import tokenizer from spambayes.Options import options --- 92,95 ---- *************** *** 190,194 **** def get(self, key, default=None): ! if self.msgs.get(key, "") is "": return default else: --- 189,193 ---- def get(self, key, default=None): ! if self.msgs.get(key, "") == "": return default else: From montanaro at users.sourceforge.net Thu Dec 11 13:40:56 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:41:00 2003 Subject: [Spambayes-checkins] spambayes/spambayes Dibbler.py,1.9,1.10 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv1325 Modified Files: Dibbler.py Log Message: conservative pychecker cleanup Index: Dibbler.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Dibbler.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** Dibbler.py 6 Nov 2003 23:44:59 -0000 1.9 --- Dibbler.py 11 Dec 2003 18:40:54 -0000 1.10 *************** *** 728,732 **** """Run a self-test.""" # Run the calendar server in a separate thread. ! import re, threading, urllib testServerReady = threading.Event() threading.Thread(target=runTestServer, args=(testServerReady,)).start() --- 728,732 ---- """Run a self-test.""" # Run the calendar server in a separate thread. ! import threading, urllib testServerReady = threading.Event() threading.Thread(target=runTestServer, args=(testServerReady,)).start() From montanaro at users.sourceforge.net Thu Dec 11 13:41:36 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:41:41 2003 Subject: [Spambayes-checkins] spambayes/spambayes FileCorpus.py,1.8,1.9 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv1442 Modified Files: FileCorpus.py Log Message: conservative pychecker cleanup Index: FileCorpus.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/FileCorpus.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** FileCorpus.py 30 Sep 2003 03:05:13 -0000 1.8 --- FileCorpus.py 11 Dec 2003 18:41:34 -0000 1.9 *************** *** 88,92 **** from spambayes import message from spambayes import storage ! import sys, os, gzip, fnmatch, getopt, errno, time, stat from spambayes.Options import options --- 88,92 ---- from spambayes import message from spambayes import storage ! import sys, os, gzip, fnmatch, getopt, time, stat from spambayes.Options import options *************** *** 271,275 **** '''Instance as a representative string''' - elip = '' sub = self.as_string() --- 271,274 ---- *************** *** 298,302 **** try: stats = os.stat(self.pathname()) ! except OSError, e: ctime = time.time() else: --- 297,301 ---- try: stats = os.stat(self.pathname()) ! except OSError: ctime = time.time() else: *************** *** 506,510 **** raise else: ! for filename in os.listdir(dirname): fn = os.path.join(dirname, filename) os.unlink(fn) --- 505,509 ---- raise else: ! for filename in flist: fn = os.path.join(dirname, filename) os.unlink(fn) From montanaro at users.sourceforge.net Thu Dec 11 13:42:43 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:42:46 2003 Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py,1.24,1.25 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv1686 Modified Files: ImapUI.py Log Message: conservative pychecker cleanup Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.24 retrieving revision 1.25 diff -C2 -d -r1.24 -r1.25 *** ImapUI.py 5 Dec 2003 04:41:25 -0000 1.24 --- ImapUI.py 11 Dec 2003 18:42:41 -0000 1.25 *************** *** 116,119 **** --- 116,121 ---- parm_list.remove(("imap", "use_ssl")) parm_map = tuple(parm_list) + else: + del IMAP4_SSL UserInterface.UserInterface.__init__(self, cls, parm_map, adv_map) self.classifier = cls From montanaro at users.sourceforge.net Thu Dec 11 13:43:19 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:43:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes ProxyUI.py,1.29,1.30 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv1788 Modified Files: ProxyUI.py Log Message: conservative pychecker cleanup Index: ProxyUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v retrieving revision 1.29 retrieving revision 1.30 diff -C2 -d -r1.29 -r1.30 *** ProxyUI.py 21 Oct 2003 03:58:49 -0000 1.29 --- ProxyUI.py 11 Dec 2003 18:43:17 -0000 1.30 *************** *** 68,72 **** import UserInterface from spambayes.Options import options - import spambayes.mboxutils from email.Iterators import typed_subpart_iterator --- 68,71 ---- *************** *** 414,418 **** # that match those criteria. elif params.get('find') is not None: ! prior = this = next = 0 keys = Set() # so we don't end up with duplicates push = keys.add --- 413,417 ---- # that match those criteria. elif params.get('find') is not None: ! prior = next = 0 keys = Set() # so we don't end up with duplicates push = keys.add From montanaro at users.sourceforge.net Thu Dec 11 13:44:02 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:44:05 2003 Subject: [Spambayes-checkins] spambayes/spambayes Stats.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv1859 Modified Files: Stats.py Log Message: conservative pychecker cleanup Index: Stats.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Stats.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** Stats.py 6 Oct 2003 02:29:58 -0000 1.2 --- Stats.py 11 Dec 2003 18:44:00 -0000 1.3 *************** *** 88,93 **** perc_spam = 100.0 * self.cls_spam / self.total perc_unsure = 100.0 * self.cls_unsure / self.total ! format_dict = dict(perc_spam=perc_spam, perc_ham=perc_ham, ! perc_unsure=perc_unsure, num_seen = self.total) format_dict.update(self.__dict__) push("SpamBayes has processed %(num_seen)d messages - " \ --- 88,97 ---- perc_spam = 100.0 * self.cls_spam / self.total perc_unsure = 100.0 * self.cls_unsure / self.total ! format_dict = { ! 'perc_spam': perc_spam, ! 'perc_ham': perc_ham, ! 'perc_unsure': perc_unsure, ! 'num_seen': self.total ! } format_dict.update(self.__dict__) push("SpamBayes has processed %(num_seen)d messages - " \ From montanaro at users.sourceforge.net Thu Dec 11 13:44:25 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:44:28 2003 Subject: [Spambayes-checkins] spambayes/spambayes UserInterface.py, 1.34, 1.35 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2155 Modified Files: UserInterface.py Log Message: conservative pychecker cleanup Index: UserInterface.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v retrieving revision 1.34 retrieving revision 1.35 diff -C2 -d -r1.34 -r1.35 *** UserInterface.py 26 Nov 2003 00:03:30 -0000 1.34 --- UserInterface.py 11 Dec 2003 18:44:23 -0000 1.35 *************** *** 1011,1015 **** from email import Encoders - from email.Message import Message from email.MIMEBase import MIMEBase from email.MIMEAudio import MIMEAudio --- 1011,1014 ---- From montanaro at users.sourceforge.net Thu Dec 11 13:44:49 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:44:51 2003 Subject: [Spambayes-checkins] spambayes/spambayes Version.py,1.24,1.25 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2203 Modified Files: Version.py Log Message: conservative pychecker cleanup Index: Version.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Version.py,v retrieving revision 1.24 retrieving revision 1.25 diff -C2 -d -r1.24 -r1.25 *** Version.py 18 Sep 2003 22:56:09 -0000 1.24 --- Version.py 11 Dec 2003 18:44:47 -0000 1.25 *************** *** 203,206 **** --- 203,207 ---- def main(args): + import sys if '-g' in args: make_cfg(sys.stdout) From montanaro at users.sourceforge.net Thu Dec 11 13:45:09 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:45:20 2003 Subject: [Spambayes-checkins] spambayes/spambayes cdb.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2285 Modified Files: cdb.py Log Message: conservative pychecker cleanup Index: cdb.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/cdb.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** cdb.py 14 Jan 2003 05:38:20 -0000 1.2 --- cdb.py 11 Dec 2003 18:45:07 -0000 1.3 *************** *** 12,16 **** import struct import mmap - import sys def uint32_unpack(buf): --- 12,15 ---- *************** *** 52,56 **** def __iter__(self, fn=None): len = 2048 - ret = [] while len < self.eod: klen, vlen = struct.unpack(" Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2409 Modified Files: message.py Log Message: conservative pychecker cleanup Index: message.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v retrieving revision 1.43 retrieving revision 1.44 diff -C2 -d -r1.43 -r1.44 *** message.py 26 Nov 2003 22:09:51 -0000 1.43 --- message.py 11 Dec 2003 18:45:31 -0000 1.44 *************** *** 92,97 **** import math import re - import sys - import types import errno import shelve --- 92,95 ---- From montanaro at users.sourceforge.net Thu Dec 11 13:45:49 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Dec 11 13:45:52 2003 Subject: [Spambayes-checkins] spambayes/spambayes oe_mailbox.py,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2462 Modified Files: oe_mailbox.py Log Message: conservative pychecker cleanup Index: oe_mailbox.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/oe_mailbox.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** oe_mailbox.py 20 Aug 2003 08:33:26 -0000 1.1 --- oe_mailbox.py 11 Dec 2003 18:45:46 -0000 1.2 *************** *** 279,283 **** def getIndexDataType(self, dbxIndex): """Returns the data type of the given index.""" ! return dt_None def getValue(self, dbxIndex): --- 279,283 ---- def getIndexDataType(self, dbxIndex): """Returns the data type of the given index.""" ! return DT_NONE def getValue(self, dbxIndex): From mhammond at users.sourceforge.net Fri Dec 12 01:29:59 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Fri Dec 12 01:30:02 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html, 1.18, 1.19 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv23045 Modified Files: troubleshooting.html Log Message: Add information about the data directory and configuration files. Index: troubleshooting.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** troubleshooting.html 10 Dec 2003 05:08:36 -0000 1.18 --- troubleshooting.html 12 Dec 2003 06:29:56 -0000 1.19 *************** *** 18,21 **** --- 18,23 ----
  • Messages have incorrect or unexpected Spam values
  • +
  • Resetting SpamBayes configuration
    +
  • SpamBayes is not available for all users on the machine
    *************** *** 205,208 **** --- 207,241 ---- +

    Resetting SpamBayes configuration

    + In some cases, it may become necessary to reset your SpamBayes + configuration, especially if your configuration becomes invalid.  + SpamBayes attempts to detect this situation, but doesn't always get it + right.  This section details where critical configuration files + are stored.
    + SpamBayes stores all configuration data in your data directory.  The + configuration information is stored in a file called [profile name].ini, where profile name is the name of your + Microsoft Outlook profile.  The default profile name is usually Outlook + or Microsoft Outlook  Internet + Settings, but Outlook can be configured to use any number of + profiles, with any name.
    + Note that in this directory, you will also find a file named default_bayes_customize.ini - this + file is not used to configure + the Outlook side of SpamBayes - look for any other .ini files in that + directory.
    + If you delete the configuration file, SpamBayes will be completely + reset.  Note you will not lose your training data, only your + configuration information.  The next time you start Outlook, the + SpamBayes configuration wizard should appear, guiding you through the + configuration process
    + Your training data is also stored in this directory, but in files with + a .db extension.  If you + ever want to delete your training information, remove the two .db files in this directory.
    + You may like to consider backing up this directory.

    SpamBayes is not available for all users on the machine.

    *************** *** 311,315 **** information, or zip it up.  If you don't know what that means, please send a mail.
    !

    Report a bug

    All SpamBayes bugs are maintained in this --- 344,370 ---- information, or zip it up.  If you don't know what that means, please send a mail.
    !

    Locating your Data Directory

    ! SpamBayes stores all configuration and database information in a single ! directory.  By default, this directory is located under the user's ! Application Data ! directory.  You can locate this directory by ! using the Show Data Folder ! button on the Advanced tab of ! the main SpamBayes Manager dialog.
    ! If you need to locate it by hand, on ! Windows NT/2000/XP, it will probably be C:\Documents ! and Settings\[username]\Application Data\Spambayes, or on other ! versions of Windows it will probably be C:\Windows\Application Data\Spambayes.  ! Note that the Application Data folder ! may be hidden, so Windows ! Explorer may not show it by default, but you can enter the path into ! the Address Bar and Explorer will open it.
    ! Note that by modifying the configuration files, you can tell SpamBayes ! to store this data in any directory, so it is possible your data is ! being stored elsewhere - contact your network administrator if this ! appear to be the case. !

    Report a bug

    All SpamBayes bugs are maintained in this From montanaro at users.sourceforge.net Fri Dec 12 15:21:17 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Fri Dec 12 15:21:22 2003 Subject: [Spambayes-checkins] spambayes/contrib spamcounts.py,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/contrib In directory sc8-pr-cvs1:/tmp/cvs-serv2789/contrib Modified Files: spamcounts.py Log Message: initialize usepickle from persisten_use_database option Index: spamcounts.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/contrib/spamcounts.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** spamcounts.py 5 Dec 2003 21:23:35 -0000 1.3 --- spamcounts.py 12 Dec 2003 20:21:15 -0000 1.4 *************** *** 97,101 **** usere = False dbname = options["Storage", "persistent_storage_file"] ! ispickle = False tokenizestdin = False for opt, arg in opts: --- 97,101 ---- usere = False dbname = options["Storage", "persistent_storage_file"] ! ispickle = not options["Storage", "persistent_use_database"] tokenizestdin = False for opt, arg in opts: From anadelonbrin at users.sourceforge.net Sat Dec 13 21:08:27 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Dec 14 03:35:57 2003 Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py,1.26,1.27 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv7964/spambayes Modified Files: ImapUI.py Log Message: Fix bug found by Skip/PyChecker. (When logging in was done by the UI (to show available folders) we assigned the imap_session object to the wrong name). Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.26 retrieving revision 1.27 diff -C2 -d -r1.26 -r1.27 *** ImapUI.py 14 Dec 2003 01:36:49 -0000 1.26 --- ImapUI.py 14 Dec 2003 02:08:25 -0000 1.27 *************** *** 200,204 **** else: port = 143 ! imap = self.imap_session_class(server, port) if self.imap is None: content = self._buildBox("Error", None, --- 200,204 ---- else: port = 143 ! self.imap = self.imap_session_class(server, port) if self.imap is None: content = self._buildBox("Error", None, From mhammond at users.sourceforge.net Sat Dec 13 20:34:45 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 14 03:36:24 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py,1.85,1.86 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv15771 Modified Files: Options.py Log Message: Move the option loading code to a function, then call this function as the module loads. This will allows us to re-read the options file by calling this function, rather than reloading the entire module. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.85 retrieving revision 1.86 diff -C2 -d -r1.85 -r1.86 *** Options.py 9 Dec 2003 16:31:39 -0000 1.85 --- Options.py 14 Dec 2003 01:34:42 -0000 1.86 *************** *** 986,1061 **** optionsPathname = None ! options = OptionsClass() ! options.load_defaults(defaults) ! alternate = None ! if hasattr(os, 'getenv'): ! alternate = os.getenv('BAYESCUSTOMIZE') ! if alternate: ! filenames = alternate.split(os.pathsep) ! options.merge_files(filenames) ! optionsPathname = os.path.abspath(filenames[-1]) ! else: ! alts = [] ! for path in ['bayescustomize.ini', '~/.spambayesrc']: ! epath = os.path.expanduser(path) ! if os.path.exists(epath): ! alts.append(epath) ! if alts: ! options.merge_files(alts) ! optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: ! optionsPathname = os.path.abspath('bayescustomize.ini') ! if sys.platform.startswith("win") and not os.path.isfile(optionsPathname): ! # If we are on Windows and still don't have an INI, default to the ! # 'per-user' directory. ! try: ! from win32com.shell import shell, shellcon ! windowsUserDirectory = os.path.join( ! shell.SHGetFolderPath(0,shellcon.CSIDL_APPDATA,0,0), ! "SpamBayes", "Proxy") try: ! if not os.path.isdir(windowsUserDirectory): ! os.makedirs(windowsUserDirectory) ! optionsPathname = os.path.join(windowsUserDirectory, ! 'bayescustomize.ini') ! # Not everyone is unicode aware - keep it a string. ! optionsPathname = optionsPathname.encode("mbcs") ! # If the file exists, then load it. ! if os.path.exists(optionsPathname): ! options.merge_file(optionsPathname) ! else: ! # If the file doesn't exist, then let's get the user to ! # store their databases and caches here as well, by ! # default, and save the file. ! db_name = os.path.join(windowsUserDirectory, ! "statistics_database.db") ! mi_db = os.path.join(windowsUserDirectory, ! "message_info_database.db") ! h_cache = os.path.join(windowsUserDirectory, ! "ham_cache").encode("mbcs") ! u_cache = os.path.join(windowsUserDirectory, ! "unknown_cache").encode("mbcs") ! s_cache = os.path.join(windowsUserDirectory, ! "spam_cache").encode("mbcs") ! options["Storage", "spam_cache"] = s_cache ! options["Storage", "ham_cache"] = h_cache ! options["Storage", "unknown_cache"] = u_cache ! options["Storage", "persistent_storage_file"] = \ ! db_name.encode("mbcs") ! options["Storage", "messageinfo_storage_file"] = \ ! mi_db.encode("mbcs") ! options.update_file(optionsPathname) ! except os.error: ! # unable to make the directory - stick to default. ! pass ! except ImportError: ! # We are on Windows, with no BAYESCUSTOMIZE set, no ini file ! # in the current directory, and no win32 extensions installed ! # to locate the "user" directory - seeing things are so lamely ! # setup, it is worth printing a warning ! print "NOTE: We can not locate an INI file for SpamBayes, and the" ! print "Python for Windows extensions are not installed, meaning we" ! print "can't locate your 'user' directory. An empty configuration" ! print "file at '%s' will be used." % optionsPathname.encode('mbcs') --- 986,1074 ---- optionsPathname = None ! # The global options object - created by load_options ! options = None ! def load_options(): ! global optionsPathname, options ! options = OptionsClass() ! options.load_defaults(defaults) ! alternate = None ! if hasattr(os, 'getenv'): ! alternate = os.getenv('BAYESCUSTOMIZE') ! if alternate: ! filenames = alternate.split(os.pathsep) ! options.merge_files(filenames) ! optionsPathname = os.path.abspath(filenames[-1]) ! else: ! alts = [] ! for path in ['bayescustomize.ini', '~/.spambayesrc']: ! epath = os.path.expanduser(path) ! if os.path.exists(epath): ! alts.append(epath) ! if alts: ! options.merge_files(alts) ! optionsPathname = os.path.abspath(alts[-1]) ! ! if not optionsPathname: ! optionsPathname = os.path.abspath('bayescustomize.ini') ! if sys.platform.startswith("win") and \ ! not os.path.isfile(optionsPathname): ! # If we are on Windows and still don't have an INI, default to the ! # 'per-user' directory. try: ! from win32com.shell import shell, shellcon ! windowsUserDirectory = os.path.join( ! shell.SHGetFolderPath(0,shellcon.CSIDL_APPDATA,0,0), ! "SpamBayes", "Proxy") ! try: ! if not os.path.isdir(windowsUserDirectory): ! os.makedirs(windowsUserDirectory) ! optionsPathname = os.path.join(windowsUserDirectory, ! 'bayescustomize.ini') ! # Not everyone is unicode aware - keep it a string. ! optionsPathname = optionsPathname.encode("mbcs") ! # If the file exists, then load it. ! if os.path.exists(optionsPathname): ! options.merge_file(optionsPathname) ! else: ! # If the file doesn't exist, then let's get the user to ! # store their databases and caches here as well, by ! # default, and save the file. ! db_name = os.path.join(windowsUserDirectory, ! "statistics_database.db") ! mi_db = os.path.join(windowsUserDirectory, ! "message_info_database.db") ! h_cache = os.path.join(windowsUserDirectory, ! "ham_cache").encode("mbcs") ! u_cache = os.path.join(windowsUserDirectory, ! "unknown_cache").encode("mbcs") ! s_cache = os.path.join(windowsUserDirectory, ! "spam_cache").encode("mbcs") ! options["Storage", "spam_cache"] = s_cache ! options["Storage", "ham_cache"] = h_cache ! options["Storage", "unknown_cache"] = u_cache ! options["Storage", "persistent_storage_file"] = \ ! db_name.encode("mbcs") ! options["Storage", "messageinfo_storage_file"] = \ ! mi_db.encode("mbcs") ! options.update_file(optionsPathname) ! except os.error: ! # unable to make the directory - stick to default. ! pass ! except ImportError: ! # We are on Windows, with no BAYESCUSTOMIZE set, no ini file ! # in the current directory, and no win32 extensions installed ! # to locate the "user" directory - seeing things are so lamely ! # setup, it is worth printing a warning ! print "NOTE: We can not locate an INI file for SpamBayes, and the" ! print "Python for Windows extensions are not installed, meaning we" ! print "can't locate your 'user' directory. An empty configuration" ! print "file at '%s' will be used." % optionsPathname.encode('mbcs') ! ! # Ideally, we should not create the objects at import time - but we have ! # done it this way forever! ! # We avoid having the options loading code at the module level, as then ! # the only way to re-read is to reload this module, and as at 2.3, that ! # doesn't work in a .zip file. ! load_options() From mhammond at users.sourceforge.net Sat Dec 13 20:30:51 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 14 03:36:26 2003 Subject: [Spambayes-checkins] spambayes/scripts sb_server.py,1.12,1.13 Message-ID: Update of /cvsroot/spambayes/spambayes/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv15075/scripts Modified Files: sb_server.py Log Message: Fix [ 859215 ] "Restore Defaults" causes assertion error at exit. There was some confusion between the global 'state' and local state passed to the global sb_server functions. This meant that even when a state was recreated during run(), the old state was closed as run() finished. The global sb_server methods now all use the global sb_server state - if you need finer state control, use your own state but don't call the global sb_server functions. Index: sb_server.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** sb_server.py 2 Dec 2003 06:05:25 -0000 1.12 --- sb_server.py 14 Dec 2003 01:30:49 -0000 1.13 *************** *** 674,677 **** --- 674,678 ---- def prepare(self): # If we can, prevent multiple servers from running at the same time. + assert self.platform_mutex is None, "Should not already have the mutex" self.platform_mutex = open_platform_mutex() *************** *** 832,838 **** state = State() ! prepare(state) _createProxies(state.servers, state.proxyPorts) - return state --- 833,838 ---- state = State() ! prepare() _createProxies(state.servers, state.proxyPorts) return state *************** *** 846,850 **** Dibbler.run(launchBrowser=launchUI) ! def prepare(state): state.prepare() # Launch any SMTP proxies. Note that if the user hasn't specified any --- 846,850 ---- Dibbler.run(launchBrowser=launchUI) ! def prepare(): state.prepare() # Launch any SMTP proxies. Note that if the user hasn't specified any *************** *** 859,863 **** state.buildServerStrings() ! def start(state): # kick everything off assert state.prepared, "starting before preparing state" --- 859,863 ---- state.buildServerStrings() ! def start(): # kick everything off assert state.prepared, "starting before preparing state" *************** *** 867,871 **** state.close() ! def stop(state): # Shutdown as though through the web UI. This will save the DB, allow # any open proxy connections to complete, etc. --- 867,871 ---- state.close() ! def stop(): # Shutdown as though through the web UI. This will save the DB, allow # any open proxy connections to complete, etc. *************** *** 925,929 **** try: ! prepare(state=state) except AlreadyRunningException: print >>sys.stderr, \ --- 925,929 ---- try: ! prepare() except AlreadyRunningException: print >>sys.stderr, \ *************** *** 931,935 **** print >>sys.stderr, "Please stop the existing proxy and try again" return ! start(state=state) else: --- 931,935 ---- print >>sys.stderr, "Please stop the existing proxy and try again" return ! start() else: From mhammond at users.sourceforge.net Sat Dec 13 20:12:10 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 14 03:46:52 2003 Subject: [Spambayes-checkins] spambayes/spambayes/test test_programs.py, 1.1, 1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/test In directory sc8-pr-cvs1:/tmp/cvs-serv32042 Modified Files: test_programs.py Log Message: * When "calling" URLs, check the output for tracebacks * Check the exit code of processes we spawn. * Add test for "[ 859215 ] "Restore Defaults" causes assertion error at exit" by explicitly "calling" this reload URL from the test script. Index: test_programs.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_programs.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** test_programs.py 3 Dec 2003 00:34:17 -0000 1.1 --- test_programs.py 14 Dec 2003 01:12:08 -0000 1.2 *************** *** 4,7 **** --- 4,8 ---- import unittest import time + from urllib import urlopen, urlencode import sb_test_support *************** *** 21,24 **** --- 22,33 ---- verbose = 0 + def call_web_function(url, **kw): + got = urlopen(url, urlencode(kw)).read() + # Very simple - just look for tracebacks + if got.find("Traceback (most recent call last)")>=0: + print "FAILED calling URL", url + print got + raise AssertionError, "Opening URL %s appeared to fail" % (url,) + class Spawner: def __init__(self, test_case, spawn_args): *************** *** 101,111 **** # Shutdown as though through the web UI. This will save the DB, allow # any open proxy connections to complete, etc. ! from urllib import urlopen, urlencode ! urlopen('http://localhost:%d/save' % self.shutdown_port, ! urlencode({'how': 'Save & shutdown'})).read() # wait for it to stop - 5 secs, 0.25 per check for i in range(20): time.sleep(0.25) if not self.is_running() and not is_any_sb_server_running(): return # gave up waiting. --- 110,123 ---- # Shutdown as though through the web UI. This will save the DB, allow # any open proxy connections to complete, etc. ! call_web_function('http://localhost:%d/save' % self.shutdown_port, ! how='Save & shutdown') # wait for it to stop - 5 secs, 0.25 per check for i in range(20): time.sleep(0.25) if not self.is_running() and not is_any_sb_server_running(): + # stopped - check the exit code + temp_pid, rc = os.waitpid(self.pid, 0) + if rc: + self.test_case.fail("sb_server returned exit code %s" % rc) return # gave up waiting. *************** *** 161,164 **** --- 173,186 ---- self._stop_spawner(s) + def test_sb_server_restore(self): + # Make sure we can do a restore defaults and shutdown without incident. + from spambayes.Options import options + port = options["html_ui", "port"] + s = Spawner_sb_server(self, [], shutdown_port=port) + self._start_spawner(s) + # do the reload + call_web_function('http://localhost:%d/restoredefaults' % port, how='') + self._stop_spawner(s) + if sys.platform.startswith("win"): import win32service # You need win32all to run the tests! *************** *** 207,214 **** # Should be using the default port from the options file. from spambayes.Options import options - from urllib import urlopen, urlencode port = options["html_ui", "port"] ! urlopen('http://localhost:%d/save' % port, ! urlencode({'how': 'Save & shutdown'})).read() # wait for it to stop - 5 secs, 0.25 per check for i in range(10): --- 229,235 ---- # Should be using the default port from the options file. from spambayes.Options import options port = options["html_ui", "port"] ! call_web_function ('http://localhost:%d/save' % port, ! how='Save & shutdown') # wait for it to stop - 5 secs, 0.25 per check for i in range(10): From mhammond at users.sourceforge.net Sat Dec 13 20:30:51 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 14 03:47:16 2003 Subject: [Spambayes-checkins] spambayes/windows pop3proxy_service.py, 1.15, 1.16 pop3proxy_tray.py, 1.16, 1.17 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv15075/windows Modified Files: pop3proxy_service.py pop3proxy_tray.py Log Message: Fix [ 859215 ] "Restore Defaults" causes assertion error at exit. There was some confusion between the global 'state' and local state passed to the global sb_server functions. This meant that even when a state was recreated during run(), the old state was closed as run() finished. The global sb_server methods now all use the global sb_server state - if you need finer state control, use your own state but don't call the global sb_server functions. Index: pop3proxy_service.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_service.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** pop3proxy_service.py 29 Sep 2003 02:18:27 -0000 1.15 --- pop3proxy_service.py 14 Dec 2003 01:30:49 -0000 1.16 *************** *** 102,106 **** self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stopping.set() ! sb_server.stop(sb_server.state) def SvcDoRun(self): --- 102,106 ---- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stopping.set() ! sb_server.stop() def SvcDoRun(self): *************** *** 108,112 **** # Setup our state etc try: ! sb_server.prepare(state=sb_server.state) except sb_server.AlreadyRunningException: msg = "The SpamBayes proxy service could not be started, as "\ --- 108,112 ---- # Setup our state etc try: ! sb_server.prepare() except sb_server.AlreadyRunningException: msg = "The SpamBayes proxy service could not be started, as "\ *************** *** 169,173 **** try: try: ! sb_server.start(sb_server.state) except SystemExit: # user requested shutdown --- 169,173 ---- try: try: ! sb_server.start() except SystemExit: # user requested shutdown Index: pop3proxy_tray.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_tray.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** pop3proxy_tray.py 4 Dec 2003 02:57:43 -0000 1.16 --- pop3proxy_tray.py 14 Dec 2003 01:30:49 -0000 1.17 *************** *** 392,396 **** if self.started and not self.use_service: try: ! sb_server.stop(sb_server.state) except: print "Error stopping proxy at shutdown" --- 392,396 ---- if self.started and not self.use_service: try: ! sb_server.stop() except: print "Error stopping proxy at shutdown" *************** *** 405,409 **** self.started = True try: ! sb_server.start(sb_server.state) finally: self.started = False --- 405,409 ---- self.started = True try: ! sb_server.start() finally: self.started = False *************** *** 429,433 **** if not self.have_prepared_state: try: ! sb_server.prepare(state=sb_server.state) self.have_prepared_state = True except sb_server.AlreadyRunningException: --- 429,433 ---- if not self.have_prepared_state: try: ! sb_server.prepare() self.have_prepared_state = True except sb_server.AlreadyRunningException: *************** *** 461,465 **** if not use_service: if verbose: print "Stopping local server" ! sb_server.stop(sb_server.state) except: print "There was an error stopping the server" --- 461,465 ---- if not use_service: if verbose: print "Stopping local server" ! sb_server.stop() except: print "There was an error stopping the server" From mhammond at users.sourceforge.net Sat Dec 13 20:36:51 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 14 03:47:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.25, 1.26 ProxyUI.py, 1.30, 1.31 ServerUI.py, 1.2, 1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv16201/spambayes Modified Files: ImapUI.py ProxyUI.py ServerUI.py Log Message: Fix [ 856628 ] reload(Options) fails in windows binary Instead of reload(Options), we call the new load_options function. Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.25 retrieving revision 1.26 diff -C2 -d -r1.25 -r1.26 *** ImapUI.py 11 Dec 2003 18:42:41 -0000 1.25 --- ImapUI.py 14 Dec 2003 01:36:49 -0000 1.26 *************** *** 155,162 **** """Called by the config page when the user saves some new options, or restores the defaults.""" ! # Reload the options. self.classifier.store() import Options ! reload(Options) global options from Options import options --- 155,162 ---- """Called by the config page when the user saves some new options, or restores the defaults.""" ! # Re-read the options. self.classifier.store() import Options ! Options.load_options() global options from Options import options Index: ProxyUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v retrieving revision 1.30 retrieving revision 1.31 diff -C2 -d -r1.30 -r1.31 *** ProxyUI.py 11 Dec 2003 18:43:17 -0000 1.30 --- ProxyUI.py 14 Dec 2003 01:36:49 -0000 1.31 *************** *** 685,692 **** """Called by the config page when the user saves some new options, or restores the defaults.""" ! # Reload the options. global state import Options ! reload(Options) global options from Options import options --- 685,692 ---- """Called by the config page when the user saves some new options, or restores the defaults.""" ! # Re-read the options. global state import Options ! Options.load_options() global options from Options import options Index: ServerUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ServerUI.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** ServerUI.py 24 Sep 2003 05:28:53 -0000 1.2 --- ServerUI.py 14 Dec 2003 01:36:49 -0000 1.3 *************** *** 94,98 **** self.state.bayes.store() import Options ! reload(Options) global options from Options import options --- 94,98 ---- self.state.bayes.store() import Options ! Options.load_options() global options from Options import options From tim_one at users.sourceforge.net Sat Dec 13 23:12:34 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Sun Dec 14 03:50:34 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.86, 1.87 classifier.py, 1.10, 1.11 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv20174/spambayes Modified Files: Options.py classifier.py Log Message: Removed support code for the defunct experimental_ham_spam_imbalance_adjustment option, and fiddled some docs accordingly. Options.py still knows about it, and various UI components building on Options.py, to avoid breaking anything that's using it. Unsure to get rid of it completely. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.86 retrieving revision 1.87 diff -C2 -d -r1.86 -r1.87 *** Options.py 14 Dec 2003 01:34:42 -0000 1.86 --- Options.py 14 Dec 2003 04:12:32 -0000 1.87 *************** *** 409,413 **** # than min(# ham trained on, # spam trained on) justifies. I *expect* # this option will go away (and become the default), but people *with* ! # strong imbalance need to test it first. ("experimental_ham_spam_imbalance_adjustment", "Compensate for unequal numbers of spam and ham", False, --- 409,416 ---- # than min(# ham trained on, # spam trained on) justifies. I *expect* # this option will go away (and become the default), but people *with* ! # strong imbalance need to test it first.\ ! # LATER: this option sucked, creating more problems than it solved. ! # XXX The code in classifier.py is gone now. How can we get rid of ! # XXX the option gracefully? ("experimental_ham_spam_imbalance_adjustment", "Compensate for unequal numbers of spam and ham", False, Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** classifier.py 15 Sep 2003 01:08:11 -0000 1.10 --- classifier.py 14 Dec 2003 04:12:32 -0000 1.11 *************** *** 248,257 **** prob = spamratio / (hamratio + spamratio) - if options["Classifier", "experimental_ham_spam_imbalance_adjustment"]: - spam2ham = min(nspam / nham, 1.0) - ham2spam = min(nham / nspam, 1.0) - else: - spam2ham = ham2spam = 1.0 - S = options["Classifier", "unknown_word_strength"] StimesX = S * options["Classifier", "unknown_word_prob"] --- 248,251 ---- *************** *** 274,296 **** # less so the larger n is, or the smaller s is. ! # Experimental: ! # Picking a good value for n is interesting: how much empirical ! # evidence do we really have? If nham == nspam, ! # hamcount + spamcount makes a lot of sense, and the code here ! # does that by default. ! # But if, e.g., nham is much larger than nspam, p(w) can get a ! # lot closer to 0.0 than it can get to 1.0. That in turn makes ! # strong ham words (high hamcount) much stronger than strong ! # spam words (high spamcount), and that makes the accidental ! # appearance of a strong ham word in spam much more damaging than ! # the accidental appearance of a strong spam word in ham. ! # So we don't give hamcount full credit when nham > nspam (or ! # spamcount when nspam > nham): instead we knock hamcount down ! # to what it would have been had nham been equal to nspam. IOW, ! # we multiply hamcount by nspam/nham when nspam < nham; or, IOOW, ! # we don't "believe" any count to an extent more than ! # min(nspam, nham) justifies. ! ! n = hamcount * spam2ham + spamcount * ham2spam prob = (StimesX + n * prob) / (S + n) --- 268,272 ---- # less so the larger n is, or the smaller s is. ! n = hamcount + spamcount prob = (StimesX + n * prob) / (S + n) *************** *** 380,384 **** this point. Introduced to fix bug #797890.""" pass ! def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] --- 356,360 ---- this point. Introduced to fix bug #797890.""" pass ! def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] From tim_one at users.sourceforge.net Sat Dec 13 23:12:34 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Sun Dec 14 03:50:35 2003 Subject: [Spambayes-checkins] website background.ht,1.17,1.18 Message-ID: Update of /cvsroot/spambayes/website In directory sc8-pr-cvs1:/tmp/cvs-serv20174/website Modified Files: background.ht Log Message: Removed support code for the defunct experimental_ham_spam_imbalance_adjustment option, and fiddled some docs accordingly. Options.py still knows about it, and various UI components building on Options.py, to avoid breaking anything that's using it. Unsure to get rid of it completely. Index: background.ht =================================================================== RCS file: /cvsroot/spambayes/website/background.ht,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** background.ht 11 Sep 2003 07:04:22 -0000 1.17 --- background.ht 14 Dec 2003 04:12:32 -0000 1.18 *************** *** 190,200 ****
  • the system generates remarkably good results on very few trained messages, although obviously, the more the better. !
  • training on an extremely large number of messages actually starts to produce ! a slightly poorer result (once you're training on thousands and thousands of messages). !
  • severe imbalance between the number of ham and spam being trained causes ! some problems - there's a flag 'experimental_ham_spam_imbalance_adjustment' ! that you can set in your bayescustomize.ini file to attempt to compensate for ! this.
  • It's really important that you're careful about selecting your training sets to be from the same sources, where-ever possible. Several --- 190,199 ----
  • the system generates remarkably good results on very few trained messages, although obviously, the more the better. !
  • training on an extremely large number of messages may start to produce ! slightly poorer results (once you're training on thousands and thousands of messages). !
  • imbalance between the number of ham and spam trained can cause ! problems, and worse problems the greater the imbalance; the system works ! best when trained on an approximately equal number of ham and spam.
  • It's really important that you're careful about selecting your training sets to be from the same sources, where-ever possible. Several From mhammond at users.sourceforge.net Sun Dec 14 06:23:05 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Dec 14 06:23:18 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 default_bayes_customize.ini, 1.9, 1.10 manager.py, 1.91, 1.92 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv6746 Modified Files: default_bayes_customize.ini manager.py Log Message: Fix [ 833439 ] default_bayes_customize.ini is confusing. default_bayes_customize.ini is no longer copied to the data directory. Options are read from this filename in both the app_dir and data_dir. Thus, new users will *not* have default_bayes_customize.ini in their data_dir (it will remain for existing users, but causes no harm) Index: default_bayes_customize.ini =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/default_bayes_customize.ini,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** default_bayes_customize.ini 18 Sep 2003 04:30:59 -0000 1.9 --- default_bayes_customize.ini 14 Dec 2003 11:23:03 -0000 1.10 *************** *** 1,5 **** ! # This is the default INI file for the Outlook addin Bayes database ! # This file must exist, or the addin considers itself confused. ! # As we decide default options, we can add them! [Tokenizer] --- 1,22 ---- ! # This is the INI file for the *Bayes Engine* as used by the Outlook addin. ! # It is NOT where configuration information is stored for the addin - see ! # "[Profile Name].ini" for these settings. ! ! # Only options which Outlook requires different from the SpamBayes ! # defaults are listed here. Thus, there will be very few in the installed ! # version, but you may list any configuration option valid for the SpamBayes ! # engine in this file. Again, these are different from the Outlook addin ! # configuration options. ! ! # This file exists in the SpamBayes program directory, and may optionally ! # exist in the SpamBayes data directory. If you wish to make changes to ! # the default options, setting the option in a file in your data directory ! # will persist even when SpamBayes is upgraded. If you change the version in ! # the SpamBayes program directory, it will be upgraded along with SpamBayes, ! # so your changes will be lost. Options in the data directory file have ! # precedence over the app directory file. ! # Note that versions 0.81 and earlier of the plugin copied this file ! # to your data directory automatically. If a file with this name exists in ! # your data directory and you aren't sure you want it, you may delete it. [Tokenizer] *************** *** 15,22 **** # It's helpful for Tim . record_header_absence: True - - [Classifier] - # This will probably go away if testing confirms it's a Good Thing. - # Testing seems to be indicating that this is a Bad Thing, so we - # leave it disabled. - #experimental_ham_spam_imbalance_adjustment: True --- 32,33 ---- Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.91 retrieving revision 1.92 diff -C2 -d -r1.91 -r1.92 *** manager.py 10 Dec 2003 05:30:50 -0000 1.91 --- manager.py 14 Dec 2003 11:23:03 -0000 1.92 *************** *** 114,132 **** sys.path.insert(0, parent) ! def import_core_spambayes_stuff(ini_filename): if "spambayes.Options" in sys.modules: ! # Manager probably being re-initialized (via the Outlook 'addin' GUI ! # Check that nothing has changed underneath us. ! if __debug__: ! import spambayes.Options ! assert spambayes.Options.optionsPathname == \ ! ini_filename.encode(filesystem_encoding), \ ! "'spambayes.Options' was imported too early, with the " \ ! "incorrect directory %r" \ ! % (spambayes.Options.optionsPathname,) return ! global bayes_classifier, bayes_tokenize, bayes_storage ! # ini_filename is Unicode, but environ not unicode aware ! os.environ["BAYESCUSTOMIZE"] = ini_filename.encode(filesystem_encoding) from spambayes import classifier from spambayes.tokenizer import tokenize --- 114,141 ---- sys.path.insert(0, parent) ! def import_core_spambayes_stuff(ini_filenames): ! global bayes_classifier, bayes_tokenize, bayes_storage if "spambayes.Options" in sys.modules: ! # The only thing we are worried about here is spambayes.Options ! # being imported before we have determined the INI files we need to ! # use. ! # The only way this can happen otherwise is when the addin is ! # de-selected then re-selected via the Outlook GUI - and when ! # running from source-code, it never appears in this list. ! # So this should never happen from source-code, and if it does, then ! # the developer has recently changed something that causes the early ! # import ! assert hasattr(sys, "frozen") ! # And we don't care (we could try and reload the engine options, ! # but these are very unlikely to have changed) return ! # ini_filenames may contain Unicode, but environ not unicode aware. ! # Convert if necessary. ! use_names = [] ! for name in ini_filenames: ! if isinstance(name, unicode): ! name = name.encode(filesystem_encoding) ! use_names.append(name) ! os.environ["BAYESCUSTOMIZE"] = os.pathsep.join(use_names) from spambayes import classifier from spambayes.tokenizer import tokenize *************** *** 375,381 **** self.LoadConfig() ! bayes_options_filename = os.path.join(self.data_directory, ! "default_bayes_customize.ini") ! import_core_spambayes_stuff(bayes_options_filename) bayes_base = os.path.join(self.data_directory, "default_bayes_database") --- 384,399 ---- self.LoadConfig() ! # Load the options for the classifier. We support ! # default_bayes_customize.ini in the app directory and user data ! # directory (version 0.8 and earlier, we copied the app one to the ! # user dir - that was a mistake - but supporting a version in that ! # directory wasn't. ! bayes_option_filenames = [] ! # data dir last so options there win. ! for look_dir in [self.application_directory, self.data_directory]: ! look_file = os.path.join(look_dir, "default_bayes_customize.ini") ! if os.path.isfile(look_file): ! bayes_option_filenames.append(look_file) ! import_core_spambayes_stuff(bayes_option_filenames) bayes_base = os.path.join(self.data_directory, "default_bayes_database") *************** *** 491,497 **** # Try and locate our files in the old location, and move # them to the new one. ! # Also used first time SpamBayes is run - this will cause ! # the ini file to be *copied* to the correct directory ! self._MigrateFile("default_bayes_customize.ini", False) self._MigrateFile("default_bayes_database.pck") self._MigrateFile("default_bayes_database.db") --- 509,515 ---- # Try and locate our files in the old location, and move # them to the new one. ! # Note that this is migrating data for very old versions of the ! # plugin (before the first decent binary!). The next time it is ! # touched it can die :) self._MigrateFile("default_bayes_database.pck") self._MigrateFile("default_bayes_database.db") *************** *** 503,507 **** # By default (do_move not specified), the source file is deleted. # Pass do_move=False to leave the original file. ! def _MigrateFile(self, filename, do_move = True): src = os.path.join(self.application_directory, filename) dest = os.path.join(self.data_directory, filename) --- 521,525 ---- # By default (do_move not specified), the source file is deleted. # Pass do_move=False to leave the original file. ! def _MigrateFile(self, filename): src = os.path.join(self.application_directory, filename) dest = os.path.join(self.data_directory, filename) *************** *** 510,515 **** # Win95 and Win98 don't support MoveFileEx. shutil.copyfile(src, dest) ! if do_move: ! os.remove(src) def FormatFolderNames(self, folder_ids, include_sub): --- 528,532 ---- # Win95 and Win98 don't support MoveFileEx. shutil.copyfile(src, dest) ! os.remove(src) def FormatFolderNames(self, folder_ids, include_sub): From tim_one at users.sourceforge.net Sun Dec 14 19:16:22 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Sun Dec 14 19:16:26 2003 Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.27, 1.28 Options.py, 1.87, 1.88 ProxyUI.py, 1.31, 1.32 ServerUI.py, 1.3, 1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv25360/spambayes Modified Files: ImapUI.py Options.py ProxyUI.py ServerUI.py Log Message: Finished deprecating the experimental_ham_spam_imbalance_adjustment option. Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.27 retrieving revision 1.28 diff -C2 -d -r1.27 -r1.28 *** ImapUI.py 14 Dec 2003 02:08:25 -0000 1.27 --- ImapUI.py 15 Dec 2003 00:16:19 -0000 1.28 *************** *** 80,84 **** adv_map = ( ('Statistics Options', None), - ('Classifier', 'experimental_ham_spam_imbalance_adjustment'), ('Classifier', 'max_discriminators'), ('Classifier', 'minimum_prob_strength'), --- 80,83 ---- Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.87 retrieving revision 1.88 diff -C2 -d -r1.87 -r1.88 *** Options.py 14 Dec 2003 04:12:32 -0000 1.87 --- Options.py 15 Dec 2003 00:16:19 -0000 1.88 *************** *** 364,368 **** assigning certainty (0 or 1) to a word that has appeared in only ham or only spam. This is a disaster. ! As unknown_word_strength tends toward infintity, all probabilities tend toward unknown_word_prob. All reports were that a value near 0.4 --- 364,368 ---- assigning certainty (0 or 1) to a word that has appeared in only ham or only spam. This is a disaster. ! As unknown_word_strength tends toward infintity, all probabilities tend toward unknown_word_prob. All reports were that a value near 0.4 *************** *** 411,418 **** # strong imbalance need to test it first.\ # LATER: this option sucked, creating more problems than it solved. ! # XXX The code in classifier.py is gone now. How can we get rid of ! # XXX the option gracefully? ! ("experimental_ham_spam_imbalance_adjustment", "Compensate for unequal numbers of spam and ham", False, """If your training database has significantly more ham than spam, or vice versa, you may start seeing an increase in incorrect --- 411,420 ---- # strong imbalance need to test it first.\ # LATER: this option sucked, creating more problems than it solved. ! # It's deprecated, and the support code has gone away. ! # XXX The "x-" prefix can't be "X-" instead, else it's considered ! # XXX an invalid option instead of a deprecated one. That behavior ! # XXX doesn't match the OptionsClass comments. ! ("x-experimental_ham_spam_imbalance_adjustment", "Compensate for unequal numbers of spam and ham", False, """If your training database has significantly more ham than spam, or vice versa, you may start seeing an increase in incorrect *************** *** 951,955 **** PORT, RESTORE), ), ! "globals" : ( ("verbose", "Verbose", False, --- 953,957 ---- PORT, RESTORE), ), ! "globals" : ( ("verbose", "Verbose", False, *************** *** 1013,1017 **** options.merge_files(alts) optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: optionsPathname = os.path.abspath('bayescustomize.ini') --- 1015,1019 ---- options.merge_files(alts) optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: optionsPathname = os.path.abspath('bayescustomize.ini') Index: ProxyUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v retrieving revision 1.31 retrieving revision 1.32 diff -C2 -d -r1.31 -r1.32 *** ProxyUI.py 14 Dec 2003 01:36:49 -0000 1.31 --- ProxyUI.py 15 Dec 2003 00:16:19 -0000 1.32 *************** *** 105,109 **** adv_map = ( ('Statistics Options', None), - ('Classifier', 'experimental_ham_spam_imbalance_adjustment'), ('Classifier', 'max_discriminators'), ('Classifier', 'minimum_prob_strength'), --- 105,108 ---- Index: ServerUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ServerUI.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** ServerUI.py 14 Dec 2003 01:36:49 -0000 1.3 --- ServerUI.py 15 Dec 2003 00:16:20 -0000 1.4 *************** *** 57,61 **** ('Categorization', 'ham_cutoff'), ('Categorization', 'spam_cutoff'), - ('Classifier', 'experimental_ham_spam_imbalance_adjustment'), ) --- 57,60 ---- From anadelonbrin at users.sourceforge.net Mon Dec 15 02:02:35 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 02:02:39 2003 Subject: [Spambayes-checkins] spambayes/windows README.txt,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv23755/windows Modified Files: README.txt Log Message: Move a bit closer to current reality. Index: README.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/README.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** README.txt 12 Mar 2003 13:22:44 -0000 1.1 --- README.txt 15 Dec 2003 07:02:33 -0000 1.2 *************** *** 3,7 **** Currently this contains: * Windows Service version of the pop3 proxy ! Fairly short-term plans include: ! * Inno setup installer for pop3proxy and Outlook. --- 3,14 ---- Currently this contains: * Windows Service version of the pop3 proxy + * The beginnings of a script to automatically configure SpamBayes and + the user's mail client. + * A GUI 'tray' application to control SpamBayes (service or proxy). + * Inno setup installer for sb_server, Outlook and a few tools. ! If you have the latest version of py2exe, you can build the binary installer ! as follows: ! ! 1. Run "python setup_all.py" in the py2exe directory in this directory. ! 2. Run the Inno Setup "spambayes.iss" script in this directory. From anadelonbrin at users.sourceforge.net Mon Dec 15 02:06:26 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 02:06:29 2003 Subject: [Spambayes-checkins] spambayes/spambayes Corpus.py,1.10,1.11 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv24226/spambayes-old/spambayes Modified Files: Corpus.py Log Message: Typo in comment. Index: Corpus.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Corpus.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** Corpus.py 11 Dec 2003 18:40:21 -0000 1.10 --- Corpus.py 15 Dec 2003 07:06:23 -0000 1.11 *************** *** 11,15 **** A corpus is defined as a set of messages that share some common characteristic relative to spamness. Examples might be spam, ham, ! unsure, or untrained, or "bayes rating between .4 and .6. A corpus is a collection of messages. Corpus is a dictionary that is keyed by the keys of the messages within it. It is iterable, --- 11,15 ---- A corpus is defined as a set of messages that share some common characteristic relative to spamness. Examples might be spam, ham, ! unsure, or untrained, or "bayes rating between .4 and .6". A corpus is a collection of messages. Corpus is a dictionary that is keyed by the keys of the messages within it. It is iterable, *************** *** 71,75 **** o Suggestions? ! ''' # This module is part of the spambayes project, which is Copyright 2002-3 --- 71,75 ---- o Suggestions? ! ''' # This module is part of the spambayes project, which is Copyright 2002-3 *************** *** 151,155 **** # This method should probably not be overridden key = message.key() ! if options["globals", "verbose"]: print 'placing %s in corpus cache' % (key) --- 151,155 ---- # This method should probably not be overridden key = message.key() ! if options["globals", "verbose"]: print 'placing %s in corpus cache' % (key) From anadelonbrin at users.sourceforge.net Mon Dec 15 02:48:42 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 02:48:45 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionsClass.py,1.17,1.18 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv30198/spambayes Modified Files: OptionsClass.py Log Message: Don't override the built-in file. It wasn't really clear if "X-" or "x-" was the experimental option prefix, so accept either. (Personally I think either is as acceptable as picking one of them). Index: OptionsClass.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** OptionsClass.py 9 Dec 2003 16:31:39 -0000 1.17 --- OptionsClass.py 15 Dec 2003 07:48:40 -0000 1.18 *************** *** 508,513 **** def merge_files(self, file_list): ! for file in file_list: ! self.merge_file(file) def convert_and_set(self, section, option, value): --- 508,513 ---- def merge_files(self, file_list): ! for f in file_list: ! self.merge_file(f) def convert_and_set(self, section, option, value): *************** *** 534,547 **** # not an error if an X- option is missing else: ! option = 'x-'+option # going the other way, if the option has been # deprecated, set its x-prefixed version and # emit a warning ! if self._options.has_key((section, option)): ! self.convert_and_set(section, option, value) ! print >> sys.stderr, ( ! "warning: option %s in" ! " section %s is deprecated" % ! (opt, sect)) else: print >> sys.stderr, ( --- 534,548 ---- # not an error if an X- option is missing else: ! l_option = 'x-' + option ! u_option = 'X-' + option # going the other way, if the option has been # deprecated, set its x-prefixed version and # emit a warning ! if self._options.has_key((section, l_option)): ! self.convert_and_set(section, l_option, value) ! self._report_deprecated_error(section, option) ! elif self._options.has_key((section, u_option)): ! self.convert_and_set(section, u_option, value) ! self._report_deprecated_error(section, option) else: print >> sys.stderr, ( *************** *** 629,632 **** --- 630,638 ---- else: self.set(sect, opt, val) + + def _report_deprecated_error(self, sect, opt): + print >> sys.stderr, ( + "Warning: option %s in section %s is deprecated" % + (opt, sect)) def _report_option_error(self, sect, opt, val, stream, msg): From anadelonbrin at users.sourceforge.net Mon Dec 15 02:52:44 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 02:52:46 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.88, 1.89 tokenizer.py, 1.17, 1.18 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv30744/spambayes-old/spambayes Modified Files: Options.py tokenizer.py Log Message: I (some time ago) committed by accident options to control a local token generator for the habeas (http://habeas.com) headers, but didn't commit the tokenizing code. (Opps & sorry!) X'ify the options (for all I know someone has turned them on already, thinking they work as advertised) and commit the tokenizing code. If reports indicate that they make no difference, or we aren't able to get some sort of easy-to-use testing setup for release users, then I'll strip both of these out after the next release. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.88 retrieving revision 1.89 diff -C2 -d -r1.88 -r1.89 *** Options.py 15 Dec 2003 00:16:19 -0000 1.88 --- Options.py 15 Dec 2003 07:52:41 -0000 1.89 *************** *** 155,172 **** BOOLEAN, RESTORE), ! ("search_for_habeas_headers", "", False, """If true, search for the habeas headers (see http://www.habeas.com) ! If they are present and correct, this is a strong ham sign, if they ! are present and incorrect, this is a strong spam sign""", BOOLEAN, RESTORE), ! ("reduce_habeas_headers", "", False, ! """If search_for_habeas_headers is set, nine tokens are generated for ! messages with habeas headers. This should be fine, since messages ! with the headers should either be ham, or result in FN so that we can ! send them to habeas so they can be sued. However, to reduce the ! strength of habeas headers, we offer the ability to reduce the nine ! tokens to one. (This option has no effect if search_for_habeas_headers ! is False)""", BOOLEAN, RESTORE), ), --- 155,172 ---- BOOLEAN, RESTORE), ! ("X-search_for_habeas_headers", "Search for Habeas Headers", False, """If true, search for the habeas headers (see http://www.habeas.com) ! If they are present and correct, this should be a strong ham sign, if ! they are present and incorrect, this should be a strong spam sign.""", BOOLEAN, RESTORE), ! ("X-reduce_habeas_headers", "Reduce Habeas Header Tokens to Single", False, ! """If SpamBayes is set to search for the Habeas headers, nine tokens ! are generated for messages with habeas headers. This should be fine, ! since messages with the headers should either be ham, or result in FN ! so that we can send them to habeas so they can be sued. However, to ! reduce the strength of habeas headers, we offer the ability to reduce ! the nine tokens to one. (This option has no effect if ! search_for_habeas_headers is False)""", BOOLEAN, RESTORE), ), *************** *** 364,368 **** assigning certainty (0 or 1) to a word that has appeared in only ham or only spam. This is a disaster. ! As unknown_word_strength tends toward infintity, all probabilities tend toward unknown_word_prob. All reports were that a value near 0.4 --- 364,368 ---- assigning certainty (0 or 1) to a word that has appeared in only ham or only spam. This is a disaster. ! As unknown_word_strength tends toward infintity, all probabilities tend toward unknown_word_prob. All reports were that a value near 0.4 *************** *** 953,957 **** PORT, RESTORE), ), ! "globals" : ( ("verbose", "Verbose", False, --- 953,957 ---- PORT, RESTORE), ), ! "globals" : ( ("verbose", "Verbose", False, *************** *** 1015,1019 **** options.merge_files(alts) optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: optionsPathname = os.path.abspath('bayescustomize.ini') --- 1015,1019 ---- options.merge_files(alts) optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: optionsPathname = os.path.abspath('bayescustomize.ini') Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** tokenizer.py 9 Dec 2003 16:04:16 -0000 1.17 --- tokenizer.py 15 Dec 2003 07:52:41 -0000 1.18 *************** *** 659,692 **** def tokenize_word(word, _len=len, maxword=options["Tokenizer", "skip_max_word_size"]): ! n = _len(word) ! # Make sure this range matches in tokenize(). ! if 3 <= n <= maxword: ! yield word ! elif n >= 3: ! # A long word. ! # Don't want to skip embedded email addresses. ! # An earlier scheme also split up the y in x@y on '.'. Not splitting ! # improved the f-n rate; the f-p rate didn't care either way. ! if n < 40 and '.' in word and word.count('@') == 1: ! p1, p2 = word.split('@') ! yield 'email name:' + p1 ! yield 'email addr:' + p2 ! else: ! # There's value in generating a token indicating roughly how ! # many chars were skipped. This has real benefit for the f-n ! # rate, but is neutral for the f-p rate. I don't know why! ! # XXX Figure out why, and/or see if some other way of summarizing ! # XXX this info has greater benefit. ! if options["Tokenizer", "generate_long_skips"]: ! yield "skip:%c %d" % (word[0], n // 10 * 10) ! if has_highbit_char(word): ! hicount = 0 ! for i in map(ord, word): ! if i >= 128: ! hicount += 1 ! yield "8bit%%:%d" % round(hicount * 100.0 / len(word)) # Generate tokens for: --- 659,692 ---- def tokenize_word(word, _len=len, maxword=options["Tokenizer", "skip_max_word_size"]): ! n = _len(word) ! # Make sure this range matches in tokenize(). ! if 3 <= n <= maxword: ! yield word ! elif n >= 3: ! # A long word. ! # Don't want to skip embedded email addresses. ! # An earlier scheme also split up the y in x@y on '.'. Not splitting ! # improved the f-n rate; the f-p rate didn't care either way. ! if n < 40 and '.' in word and word.count('@') == 1: ! p1, p2 = word.split('@') ! yield 'email name:' + p1 ! yield 'email addr:' + p2 ! else: ! # There's value in generating a token indicating roughly how ! # many chars were skipped. This has real benefit for the f-n ! # rate, but is neutral for the f-p rate. I don't know why! ! # XXX Figure out why, and/or see if some other way of summarizing ! # XXX this info has greater benefit. ! if options["Tokenizer", "generate_long_skips"]: ! yield "skip:%c %d" % (word[0], n // 10 * 10) ! if has_highbit_char(word): ! hicount = 0 ! for i in map(ord, word): ! if i >= 128: ! hicount += 1 ! yield "8bit%%:%d" % round(hicount * 100.0 / len(word)) # Generate tokens for: *************** *** 1138,1141 **** --- 1138,1178 ---- return + # Habeas Headers - see http://www.habeas.com + if options["Tokenizer", "search_for_habeas_headers"]: + habeas_headers = [ + ("X-Habeas-SWE-1", "winter into spring"), + ("X-Habeas-SWE-2", "brightly anticipated"), + ("X-Habeas-SWE-3", "like Habeas SWE (tm)"), + ("X-Habeas-SWE-4", "Copyright 2002 Habeas (tm)"), + ("X-Habeas-SWE-5", "Sender Warranted Email (SWE) (tm). The sender of this"), + ("X-Habeas-SWE-6", "email in exchange for a license for this Habeas"), + ("X-Habeas-SWE-7", "warrant mark warrants that this is a Habeas Compliant"), + ("X-Habeas-SWE-8", "Message (HCM) and not spam. Please report use of this"), + ("X-Habeas-SWE-9", "mark in spam to .") + ] + valid_habeas = 0 + invalid_habeas = False + for opt, val in habeas_headers: + habeas = msg.get(opt) + if habeas is not None: + if options["Tokenizer", "reduce_habeas_headers"]: + if habeas == val: + valid_habeas += 1 + else: + invalid_habeas = True + else: + if habeas == val: + yield opt.lower() + ":valid" + else: + yield opt.lower() + ":invalid" + if options["Tokenizer", "reduce_habeas_headers"]: + # If there was any invalid line, we record as invalid. + # If all nine lines were correct, we record as valid. + # Otherwise we ignore. + if invalid_habeas == True: + yield "x-habeas-swe:invalid" + elif valid_habeas == 9: + yield "x-habeas-swe:valid" + # Subject: # Don't ignore case in Subject lines; e.g., 'free' versus 'FREE' is From anadelonbrin at users.sourceforge.net Mon Dec 15 03:10:07 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 03:10:12 2003 Subject: [Spambayes-checkins] spambayes/windows/py2exe .cvsignore,NONE,1.1 Message-ID: Update of /cvsroot/spambayes/spambayes/windows/py2exe In directory sc8-pr-cvs1:/tmp/cvs-serv1117/windows/py2exe Added Files: .cvsignore Log Message: Ignore autogenerated. --- NEW FILE: .cvsignore --- build dist From anadelonbrin at users.sourceforge.net Mon Dec 15 03:10:45 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 03:10:48 2003 Subject: [Spambayes-checkins] spambayes/windows .cvsignore,NONE,1.1 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv1229/windows Added Files: .cvsignore Log Message: Ignore autogenerated. --- NEW FILE: .cvsignore --- SpamBayes-Setup.exe From anadelonbrin at users.sourceforge.net Mon Dec 15 03:16:28 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 03:16:32 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py,1.18,1.19 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2167/spambayes Modified Files: tokenizer.py Log Message: I'm not sure how, but somehow I ended up dedenting a function in the last checkin. Undo that. Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** tokenizer.py 15 Dec 2003 07:52:41 -0000 1.18 --- tokenizer.py 15 Dec 2003 08:16:26 -0000 1.19 *************** *** 659,692 **** def tokenize_word(word, _len=len, maxword=options["Tokenizer", "skip_max_word_size"]): ! n = _len(word) ! # Make sure this range matches in tokenize(). ! if 3 <= n <= maxword: ! yield word ! ! elif n >= 3: ! # A long word. ! ! # Don't want to skip embedded email addresses. ! # An earlier scheme also split up the y in x@y on '.'. Not splitting ! # improved the f-n rate; the f-p rate didn't care either way. ! if n < 40 and '.' in word and word.count('@') == 1: ! p1, p2 = word.split('@') ! yield 'email name:' + p1 ! yield 'email addr:' + p2 ! else: ! # There's value in generating a token indicating roughly how ! # many chars were skipped. This has real benefit for the f-n ! # rate, but is neutral for the f-p rate. I don't know why! ! # XXX Figure out why, and/or see if some other way of summarizing ! # XXX this info has greater benefit. ! if options["Tokenizer", "generate_long_skips"]: ! yield "skip:%c %d" % (word[0], n // 10 * 10) ! if has_highbit_char(word): ! hicount = 0 ! for i in map(ord, word): ! if i >= 128: ! hicount += 1 ! yield "8bit%%:%d" % round(hicount * 100.0 / len(word)) # Generate tokens for: --- 659,692 ---- def tokenize_word(word, _len=len, maxword=options["Tokenizer", "skip_max_word_size"]): ! n = _len(word) ! # Make sure this range matches in tokenize(). ! if 3 <= n <= maxword: ! yield word ! ! elif n >= 3: ! # A long word. ! ! # Don't want to skip embedded email addresses. ! # An earlier scheme also split up the y in x@y on '.'. Not splitting ! # improved the f-n rate; the f-p rate didn't care either way. ! if n < 40 and '.' in word and word.count('@') == 1: ! p1, p2 = word.split('@') ! yield 'email name:' + p1 ! yield 'email addr:' + p2 ! else: ! # There's value in generating a token indicating roughly how ! # many chars were skipped. This has real benefit for the f-n ! # rate, but is neutral for the f-p rate. I don't know why! ! # XXX Figure out why, and/or see if some other way of summarizing ! # XXX this info has greater benefit. ! if options["Tokenizer", "generate_long_skips"]: ! yield "skip:%c %d" % (word[0], n // 10 * 10) ! if has_highbit_char(word): ! hicount = 0 ! for i in map(ord, word): ! if i >= 128: ! hicount += 1 ! yield "8bit%%:%d" % round(hicount * 100.0 / len(word)) # Generate tokens for: From anadelonbrin at users.sourceforge.net Mon Dec 15 03:33:36 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 03:33:40 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py,1.19,1.20 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv5699/spambayes Modified Files: tokenizer.py Log Message: An x'ified option can only be referred to without the 'x-' when reading from the file, not otherwise! Fix this wrt the habeas code. Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.19 retrieving revision 1.20 diff -C2 -d -r1.19 -r1.20 *** tokenizer.py 15 Dec 2003 08:16:26 -0000 1.19 --- tokenizer.py 15 Dec 2003 08:33:34 -0000 1.20 *************** *** 1139,1143 **** # Habeas Headers - see http://www.habeas.com ! if options["Tokenizer", "search_for_habeas_headers"]: habeas_headers = [ ("X-Habeas-SWE-1", "winter into spring"), --- 1139,1143 ---- # Habeas Headers - see http://www.habeas.com ! if options["Tokenizer", "X-search_for_habeas_headers"]: habeas_headers = [ ("X-Habeas-SWE-1", "winter into spring"), *************** *** 1156,1160 **** habeas = msg.get(opt) if habeas is not None: ! if options["Tokenizer", "reduce_habeas_headers"]: if habeas == val: valid_habeas += 1 --- 1156,1160 ---- habeas = msg.get(opt) if habeas is not None: ! if options["Tokenizer", "X-reduce_habeas_headers"]: if habeas == val: valid_habeas += 1 *************** *** 1166,1170 **** else: yield opt.lower() + ":invalid" ! if options["Tokenizer", "reduce_habeas_headers"]: # If there was any invalid line, we record as invalid. # If all nine lines were correct, we record as valid. --- 1166,1170 ---- else: yield opt.lower() + ":invalid" ! if options["Tokenizer", "X-reduce_habeas_headers"]: # If there was any invalid line, we record as invalid. # If all nine lines were correct, we record as valid. From anadelonbrin at users.sourceforge.net Mon Dec 15 04:13:42 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 04:13:50 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.92,1.93 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv12206/Outlook2000 Modified Files: manager.py Log Message: Fix a couple of comment typos. Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.92 retrieving revision 1.93 diff -C2 -d -r1.92 -r1.93 *** manager.py 14 Dec 2003 11:23:03 -0000 1.92 --- manager.py 15 Dec 2003 09:13:40 -0000 1.93 *************** *** 93,97 **** # No DB library at all! assert not hasattr(sys, "frozen"), \ ! "Don't build binary versions without bsbbd!" use_db = False --- 93,97 ---- # No DB library at all! assert not hasattr(sys, "frozen"), \ ! "Don't build binary versions without bsddb!" use_db = False *************** *** 388,392 **** # directory (version 0.8 and earlier, we copied the app one to the # user dir - that was a mistake - but supporting a version in that ! # directory wasn't. bayes_option_filenames = [] # data dir last so options there win. --- 388,392 ---- # directory (version 0.8 and earlier, we copied the app one to the # user dir - that was a mistake - but supporting a version in that ! # directory wasn't). bayes_option_filenames = [] # data dir last so options there win. From anadelonbrin at users.sourceforge.net Mon Dec 15 04:15:21 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 04:15:23 2003 Subject: [Spambayes-checkins] spambayes/scripts sb_imapfilter.py,1.15,1.16 Message-ID: Update of /cvsroot/spambayes/spambayes/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv12754/scripts Modified Files: sb_imapfilter.py Log Message: Add a warning as a temporary solution for Python bug #845560. Fix grammer in comment. Index: sb_imapfilter.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** sb_imapfilter.py 21 Oct 2003 21:46:31 -0000 1.15 --- sb_imapfilter.py 15 Dec 2003 09:15:19 -0000 1.16 *************** *** 238,246 **** # It is faster to do close() than a single # expunge when we log out (because expunge returns ! # a list of all the deleted messages, that we don't do # anything with) imap.close() # We *always* use SELECT and not EXAMINE, because this # speeds things up considerably. response = self.select(folder, None) if response[0] != "OK": --- 238,252 ---- # It is faster to do close() than a single # expunge when we log out (because expunge returns ! # a list of all the deleted messages which we don't do # anything with) imap.close() # We *always* use SELECT and not EXAMINE, because this # speeds things up considerably. + if folder == "": + # This is Python bug #845560 - if the empty string is + # passed, we get a traceback, not just an 'invalid folder' + # error, so print out a warning and exit. + print "Tried to select an invalid folder" + sys.exit(-1) response = self.select(folder, None) if response[0] != "OK": From anadelonbrin at users.sourceforge.net Mon Dec 15 04:20:35 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 04:20:38 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.89, 1.90 OptionsClass.py, 1.18, 1.19 tokenizer.py, 1.20, 1.21 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv14085/spambayes Modified Files: Options.py OptionsClass.py tokenizer.py Log Message: Bah. ConfigParser lower()s all option names, which is why X- didn't work, and no doubt the explanation behind Skip's cryptic windows & case-sensitivity checkin comment (should have figured that out earlier!). We could subclass ConfigParser to remove this, but I have no idea what other effects that might have at this point. So, back out the X-/x- changes, and instead correct the comments. "x-" is the correct prefix (although since config files are case-insensitive in option names, using "X-" there will work, but should be frowned on). Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.89 retrieving revision 1.90 diff -C2 -d -r1.89 -r1.90 *** Options.py 15 Dec 2003 07:52:41 -0000 1.89 --- Options.py 15 Dec 2003 09:20:33 -0000 1.90 *************** *** 155,159 **** BOOLEAN, RESTORE), ! ("X-search_for_habeas_headers", "Search for Habeas Headers", False, """If true, search for the habeas headers (see http://www.habeas.com) If they are present and correct, this should be a strong ham sign, if --- 155,159 ---- BOOLEAN, RESTORE), ! ("x-search_for_habeas_headers", "Search for Habeas Headers", False, """If true, search for the habeas headers (see http://www.habeas.com) If they are present and correct, this should be a strong ham sign, if *************** *** 161,165 **** BOOLEAN, RESTORE), ! ("X-reduce_habeas_headers", "Reduce Habeas Header Tokens to Single", False, """If SpamBayes is set to search for the Habeas headers, nine tokens are generated for messages with habeas headers. This should be fine, --- 161,165 ---- BOOLEAN, RESTORE), ! ("x-reduce_habeas_headers", "Reduce Habeas Header Tokens to Single", False, """If SpamBayes is set to search for the Habeas headers, nine tokens are generated for messages with habeas headers. This should be fine, Index: OptionsClass.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** OptionsClass.py 15 Dec 2003 07:48:40 -0000 1.18 --- OptionsClass.py 15 Dec 2003 09:20:33 -0000 1.19 *************** *** 22,32 **** via an instance of this class. ! Experimental or deprecated options are prefixed with 'X-', borrowing the practice from RFC-822 mail. If the user sets an option like: [Tokenizer] ! X-transmogrify: True ! and an 'X-transmogrify' or 'transmogrify' option exists, it is set silently to the value given by the user. If the user sets an option like: --- 22,32 ---- via an instance of this class. ! Experimental or deprecated options are prefixed with 'x-', borrowing the practice from RFC-822 mail. If the user sets an option like: [Tokenizer] ! x-transmogrify: True ! and an 'x-transmogrify' or 'transmogrify' option exists, it is set silently to the value given by the user. If the user sets an option like: *************** *** 34,38 **** transmogrify: True ! and no 'transmogrify' option exists, but an 'X-transmogrify' option does, the latter is set to the value given by the users and a deprecation message is printed to standard error. --- 34,38 ---- transmogrify: True ! and no 'transmogrify' option exists, but an 'x-transmogrify' option does, the latter is set to the value given by the users and a deprecation message is printed to standard error. *************** *** 528,532 **** if not self._options.has_key((section, option)): if option.startswith('x-'): ! # try setting option without the X- prefix option = option[2:] if self._options.has_key((section, option)): --- 528,532 ---- if not self._options.has_key((section, option)): if option.startswith('x-'): ! # try setting option without the x- prefix option = option[2:] if self._options.has_key((section, option)): *************** *** 534,548 **** # not an error if an X- option is missing else: ! l_option = 'x-' + option ! u_option = 'X-' + option # going the other way, if the option has been # deprecated, set its x-prefixed version and # emit a warning ! if self._options.has_key((section, l_option)): ! self.convert_and_set(section, l_option, value) ! self._report_deprecated_error(section, option) ! elif self._options.has_key((section, u_option)): ! self.convert_and_set(section, u_option, value) ! self._report_deprecated_error(section, option) else: print >> sys.stderr, ( --- 534,544 ---- # not an error if an X- option is missing else: ! option = 'x-' + option # going the other way, if the option has been # deprecated, set its x-prefixed version and # emit a warning ! if self._options.has_key((section, option)): ! self.convert_and_set(section, option, value) ! self._report_deprecated_error(section, opt) else: print >> sys.stderr, ( Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.20 retrieving revision 1.21 diff -C2 -d -r1.20 -r1.21 *** tokenizer.py 15 Dec 2003 08:33:34 -0000 1.20 --- tokenizer.py 15 Dec 2003 09:20:33 -0000 1.21 *************** *** 1139,1143 **** # Habeas Headers - see http://www.habeas.com ! if options["Tokenizer", "X-search_for_habeas_headers"]: habeas_headers = [ ("X-Habeas-SWE-1", "winter into spring"), --- 1139,1143 ---- # Habeas Headers - see http://www.habeas.com ! if options["Tokenizer", "x-search_for_habeas_headers"]: habeas_headers = [ ("X-Habeas-SWE-1", "winter into spring"), *************** *** 1156,1160 **** habeas = msg.get(opt) if habeas is not None: ! if options["Tokenizer", "X-reduce_habeas_headers"]: if habeas == val: valid_habeas += 1 --- 1156,1160 ---- habeas = msg.get(opt) if habeas is not None: ! if options["Tokenizer", "x-reduce_habeas_headers"]: if habeas == val: valid_habeas += 1 *************** *** 1166,1170 **** else: yield opt.lower() + ":invalid" ! if options["Tokenizer", "X-reduce_habeas_headers"]: # If there was any invalid line, we record as invalid. # If all nine lines were correct, we record as valid. --- 1166,1170 ---- else: yield opt.lower() + ":invalid" ! if options["Tokenizer", "x-reduce_habeas_headers"]: # If there was any invalid line, we record as invalid. # If all nine lines were correct, we record as valid. From montanaro at users.sourceforge.net Mon Dec 15 08:58:51 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Mon Dec 15 08:58:55 2003 Subject: [Spambayes-checkins] spambayes/spambayes mboxutils.py,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv30955 Modified Files: mboxutils.py Log Message: (nobody complained) This change generalizes the DirOfTxtFileMailbox class to assume all non-directory files contain a single message and to recursively descend into subdirectories of the argument directory. Index: mboxutils.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/mboxutils.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** mboxutils.py 19 Sep 2003 23:38:10 -0000 1.3 --- mboxutils.py 15 Dec 2003 13:58:49 -0000 1.4 *************** *** 31,39 **** class DirOfTxtFileMailbox: ! """Mailbox directory consisting of .txt and .lorien files.""" def __init__(self, dirname, factory): ! self.names = (glob.glob(os.path.join(dirname, "*.txt")) + ! glob.glob(os.path.join(dirname, "*.lorien"))) self.names.sort() self.factory = factory --- 31,41 ---- class DirOfTxtFileMailbox: ! """Directory of files each assumed to contain an RFC-822 message. ! ! Subdirectories are traversed recursively. ! """ def __init__(self, dirname, factory): ! self.names = glob.glob(os.path.join(dirname, "*")) self.names.sort() self.factory = factory *************** *** 41,50 **** def __iter__(self): for name in self.names: ! try: ! f = open(name) ! except IOError: ! continue ! yield self.factory(f) ! f.close() def _cat(seqs): --- 43,56 ---- def __iter__(self): for name in self.names: ! if os.path.isdir(name): ! for mbox in DirOfTxtFileMailbox(name, self.factory): ! yield mbox ! else: ! try: ! f = open(name) ! except IOError: ! continue ! yield self.factory(f) ! f.close() def _cat(seqs): From anadelonbrin at users.sourceforge.net Mon Dec 15 19:30:51 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 19:30:56 2003 Subject: [Spambayes-checkins] spambayes/spambayes ImapUI.py, 1.28, 1.29 ProxyUI.py, 1.32, 1.33 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv3741/spambayes Modified Files: ImapUI.py ProxyUI.py Log Message: Don't offer deprecated options on the config page of the web interface. Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.28 retrieving revision 1.29 diff -C2 -d -r1.28 -r1.29 *** ImapUI.py 15 Dec 2003 00:16:19 -0000 1.28 --- ImapUI.py 16 Dec 2003 00:30:49 -0000 1.29 *************** *** 66,71 **** ('html_ui', 'allow_remote_connections'), ('Header Options', None), ! ('Headers', 'notate_to'), ! ('Headers', 'notate_subject'), ('Storage Options', None), ('Storage', 'persistent_storage_file'), --- 66,71 ---- ('html_ui', 'allow_remote_connections'), ('Header Options', None), ! ('Headers', 'notate_to'), ! ('Headers', 'notate_subject'), ('Storage Options', None), ('Storage', 'persistent_storage_file'), *************** *** 94,99 **** ('Storage', 'persistent_use_database'), ('Tokenising Options', None), - ('Tokenizer', 'extract_dow'), - ('Tokenizer', 'generate_time_buckets'), ('Tokenizer', 'mine_received_headers'), ('Tokenizer', 'replace_nonascii_chars'), --- 94,97 ---- Index: ProxyUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v retrieving revision 1.32 retrieving revision 1.33 diff -C2 -d -r1.32 -r1.33 *** ProxyUI.py 15 Dec 2003 00:16:19 -0000 1.32 --- ProxyUI.py 16 Dec 2003 00:30:49 -0000 1.33 *************** *** 88,94 **** ('smtpproxy', 'use_cached_message'), ('Header Options', None), ! ('Headers', 'notate_to'), ! ('Headers', 'notate_subject'), ! ('Storage Options', None), ('Storage', 'persistent_storage_file'), ('Storage', 'messageinfo_storage_file'), --- 88,94 ---- ('smtpproxy', 'use_cached_message'), ('Header Options', None), ! ('Headers', 'notate_to'), ! ('Headers', 'notate_subject'), ! ('Storage Options', None), ('Storage', 'persistent_storage_file'), ('Storage', 'messageinfo_storage_file'), *************** *** 124,134 **** ('Storage', 'unknown_cache'), ('Tokenising Options', None), - ('Tokenizer', 'extract_dow'), - ('Tokenizer', 'generate_time_buckets'), ('Tokenizer', 'mine_received_headers'), ('Tokenizer', 'replace_nonascii_chars'), ('Tokenizer', 'summarize_email_prefixes'), ('Tokenizer', 'summarize_email_suffixes'), ! ('Training Options', None), ('Hammie', 'train_on_filter'), ('Interface Options', None), --- 124,132 ---- ('Storage', 'unknown_cache'), ('Tokenising Options', None), ('Tokenizer', 'mine_received_headers'), ('Tokenizer', 'replace_nonascii_chars'), ('Tokenizer', 'summarize_email_prefixes'), ('Tokenizer', 'summarize_email_suffixes'), ! ('Training Options', None), ('Hammie', 'train_on_filter'), ('Interface Options', None), From anadelonbrin at users.sourceforge.net Mon Dec 15 21:03:33 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 21:03:36 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.90, 1.91 UserInterface.py, 1.35, 1.36 classifier.py, 1.11, 1.12 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv18229/spambayes Modified Files: Options.py UserInterface.py classifier.py Log Message: New experimental option: x-use_bigrams. This is the mixed uni/bi-grams scheme outlined by Tim/Graham. Basically both uni and bi grams (of *tokens* not words) are generated and the strongest of the unigrams and bigram is used, rather than all three. To get more information: >>> from spambayes.Options import options >>> print options.display_full("Classifier", "x-use_bigrams") Also fix a bug in the web interface where the probability would be incorrectly calculated on 'show clues'. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.90 retrieving revision 1.91 diff -C2 -d -r1.90 -r1.91 *** Options.py 15 Dec 2003 09:20:33 -0000 1.90 --- Options.py 16 Dec 2003 02:03:30 -0000 1.91 *************** *** 427,430 **** --- 427,452 ---- to retrain your database if you change this option.""", BOOLEAN, RESTORE), + + ("x-use_bigrams", "(EXPERIMENTAL) Use mixed uni/bi-grams scheme", False, + """Enabling this option means that SpamBayes will generate both + unigrams (words) and bigrams (pairs of words). However, Graham's + scheme is also used, where, for each clue triplet of two unigrams and + the bigram they make, the clue that is strongest (i.e. has a + probability furtherest from 0.5) is used, and the other two are not. + + Note that to really test this option you need to retrain with it on, + so that your database includes the bigrams - if you subsequently turn + it off, these tokens will have no effect. + + Note also that you should probably also increase the max_discriminators + (Maximum number of extreme words) option if you enable this option; + this may need to be doubled or quadrupled to see the benefit from the + bigrams. + + This option is experimental, and may be removed in a future release. + We would appreciate feedback about it if you use it - email + spambayes@python.org with your comments and results. + """, + BOOLEAN, RESTORE), ), Index: UserInterface.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v retrieving revision 1.35 retrieving revision 1.36 diff -C2 -d -r1.35 -r1.36 *** UserInterface.py 11 Dec 2003 18:44:23 -0000 1.35 --- UserInterface.py 16 Dec 2003 02:03:31 -0000 1.36 *************** *** 306,309 **** --- 306,313 ---- for tok in tokens: clues.append((tok, None)) + # Need to regenerate the tokens (is there a way to + # 'rewind' or copy a generator? Would that be + # more effecient? + tokens = tokenizer.tokenize(message) probability = self.classifier.spamprob(tokens) cluesTable = self._fillCluesTable(clues) Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** classifier.py 14 Dec 2003 04:12:32 -0000 1.11 --- classifier.py 16 Dec 2003 02:03:31 -0000 1.12 *************** *** 1,3 **** --- 1,6 ---- #! /usr/bin/env python + + from __future__ import generators + # An implementation of a Bayes-like spam classifier. # *************** *** 36,39 **** --- 39,43 ---- import math + import types try: from sets import Set *************** *** 206,212 **** True, you're telling the classifier this message is definitely spam, else that it's definitely not spam. - """ ! self._add_msg(wordstream, is_spam) --- 210,217 ---- True, you're telling the classifier this message is definitely spam, else that it's definitely not spam. """ ! if options["Classifier", "x-use_bigrams"] and \ ! isinstance(wordstream, types.GeneratorType): ! wordstream = self._enhance_wordstream(wordstream) self._add_msg(wordstream, is_spam) *************** *** 216,219 **** --- 221,227 ---- Pass the same arguments you passed to learn(). """ + if options["Classifier", "x-use_bigrams"] and \ + isinstance(wordstream, types.GeneratorType): + wordstream = self._enhance_wordstream(wordstream) self._remove_msg(wordstream, is_spam) *************** *** 300,311 **** # to exploit it. def _add_msg(self, wordstream, is_spam): - # I think the string stuff is hiding the cause of the db ham/spam - # count problem, so remove it for now. self.probcache = {} # nuke the prob cache if is_spam: - #self.nspam = int(self.nspam) + 1 # account for string nspam self.nspam += 1 else: - #self.nham = int(self.nham) + 1 # account for string nham self.nham += 1 --- 308,315 ---- *************** *** 359,377 **** def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] - unknown = options["Classifier", "unknown_word_prob"] ! clues = [] # (distance, prob, word, record) tuples ! pushclue = clues.append ! for word in Set(wordstream): ! record = self._wordinfoget(word) ! if record is None: ! prob = unknown ! else: ! prob = self.probability(record) ! distance = abs(prob - 0.5) ! if distance >= mindist: ! pushclue((distance, prob, word, record)) clues.sort() if len(clues) > options["Classifier", "max_discriminators"]: --- 363,410 ---- def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] ! # clues is now a set so that that duplicates are removed. ! # Otherwise if a token is a stronger clue than the bigrams ! # it forms with the previous *and* next tokens, then it ! # would be used twice. As is, it is possible for a single ! # token to be present in two bigram tokens (but in different ! # positions). ! clues = Set() # (distance, prob, word, record) tuples ! pushclue = clues.add ! if options["Classifier", "x-use_bigrams"]: ! # The tokens list contains 3-tuples of a token, ! # the bigram it forms with the next token, and the ! # next token. This makes it easier to select which ! # one to use later on. ! tokens = [] ! p = None ! while True: ! try: ! q = wordstream.next() ! if p: ! tokens.append((p, ' '.join([p, q]), q)) ! p = q ! except StopIteration: ! break ! ! # Run through all the triplets and add the strongest ! # clue from each (note also the comment above explaining ! # how duplicates are treated). ! for grams in tokens: ! winner = (0, None, None, None) # distance, prob, word, record ! for gram in grams: ! contestant = self._worddistanceget(gram) ! if contestant[0] > winner[0]: ! winner = contestant ! if winner[0] >= mindist: ! pushclue(winner) ! else: ! for word in wordstream: # Set() not necessary; see above. ! contestant = self._worddistanceget(word) ! if contestant[0] >= mindist: ! pushclue(contestant) + clues = list(clues) clues.sort() if len(clues) > options["Classifier", "max_discriminators"]: *************** *** 380,383 **** --- 413,426 ---- return [t[1:] for t in clues] + def _worddistanceget(self, word): + unknown = options["Classifier", "unknown_word_prob"] + record = self._wordinfoget(word) + if record is None: + prob = unknown + else: + prob = self.probability(record) + distance = abs(prob - 0.5) + return distance, prob, word, record + def _wordinfoget(self, word): return self.wordinfo.get(word) *************** *** 389,392 **** --- 432,460 ---- del self.wordinfo[word] + def _enhance_wordstream(self, wordstream): + """Add bigrams to the wordstream. This wraps the last token + to the first one, so a small number of odd tokens might get + generated from that, but it shouldn't be significant. Note + that these are *token* bigrams, and not *word* bigrams - i.e. + 'synthetic' tokens get bigram'ed, too. + + The bigram token is simply "unigram1 unigram2" - a space should + be sufficient as a separator, since spaces aren't in any other + tokens, apart from 'synthetic' ones. + + If the experimental "Classifier":"x-use_bigrams" option is + removed, this function can be removed, too.""" + p = None + while True: + try: + if p: + yield p + q = wordstream.next() + if p: + yield "%s %s" % (p, q) + p = q + except StopIteration: + break + def _wordinfokeys(self): return self.wordinfo.keys() From anadelonbrin at users.sourceforge.net Mon Dec 15 21:46:44 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 21:46:48 2003 Subject: [Spambayes-checkins] spambayes/spambayes UserInterface.py, 1.36, 1.37 classifier.py, 1.12, 1.13 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv24768/spambayes Modified Files: UserInterface.py classifier.py Log Message: Efficiency improvements, thanks to Tim. (Use list comprehensions where appropriate). Index: UserInterface.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v retrieving revision 1.36 retrieving revision 1.37 diff -C2 -d -r1.36 -r1.37 *** UserInterface.py 16 Dec 2003 02:03:31 -0000 1.36 --- UserInterface.py 16 Dec 2003 02:46:42 -0000 1.37 *************** *** 301,313 **** def _buildCluesTable(self, message, subject=None, show_tokens=False): ! tokens = tokenizer.tokenize(message) if show_tokens: ! clues = [] ! for tok in tokens: ! clues.append((tok, None)) ! # Need to regenerate the tokens (is there a way to ! # 'rewind' or copy a generator? Would that be ! # more effecient? ! tokens = tokenizer.tokenize(message) probability = self.classifier.spamprob(tokens) cluesTable = self._fillCluesTable(clues) --- 301,307 ---- def _buildCluesTable(self, message, subject=None, show_tokens=False): ! tokens = list(tokenizer.tokenize(message)) if show_tokens: ! clues = [(tok, None) for tok in tokens] probability = self.classifier.spamprob(tokens) cluesTable = self._fillCluesTable(clues) Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** classifier.py 16 Dec 2003 02:03:31 -0000 1.12 --- classifier.py 16 Dec 2003 02:46:42 -0000 1.13 *************** *** 363,366 **** --- 363,368 ---- def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] + if not isinstance(wordstream, types.ListType): + wordstream = list(wordstream) # clues is now a set so that that duplicates are removed. *************** *** 378,391 **** # next token. This makes it easier to select which # one to use later on. ! tokens = [] ! p = None ! while True: ! try: ! q = wordstream.next() ! if p: ! tokens.append((p, ' '.join([p, q]), q)) ! p = q ! except StopIteration: ! break # Run through all the triplets and add the strongest --- 380,386 ---- # next token. This makes it easier to select which # one to use later on. ! l = wordstream ! tokens = [(l[i],"%s %s" % (l[i],l[(i+1)%len(l)]), ! l[(i+1)%len(l)]) for i in xrange(len(l))] # Run through all the triplets and add the strongest From anadelonbrin at users.sourceforge.net Mon Dec 15 23:48:31 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Dec 15 23:48:35 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionsClass.py,1.19,1.20 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv9453/spambayes Modified Files: OptionsClass.py Log Message: Option names are always case insensitive, no matter what. Index: OptionsClass.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v retrieving revision 1.19 retrieving revision 1.20 diff -C2 -d -r1.19 -r1.20 *** OptionsClass.py 15 Dec 2003 09:20:33 -0000 1.19 --- OptionsClass.py 16 Dec 2003 04:48:28 -0000 1.20 *************** *** 552,586 **** def display_name(self, sect, opt): '''A name for the option suitable for display to a user.''' ! return self._options[sect, opt].display_name() def default(self, sect, opt): '''The default value for the option.''' ! return self._options[sect, opt].default() def doc(self, sect, opt): '''Documentation for the option.''' ! return self._options[sect, opt].doc() def valid_input(self, sect, opt): '''Valid values for the option.''' ! return self._options[sect, opt].valid_input() def no_restore(self, sect, opt): '''Do not restore this option when restoring to defaults.''' ! return self._options[sect, opt].no_restore() def is_valid(self, sect, opt, value): '''Check if this is a valid value for this option.''' ! return self._options[sect, opt].is_valid(value) def multiple_values_allowed(self, sect, opt): '''Multiple values are allowed for this option.''' ! return self._options[sect, opt].multiple_values_allowed() def is_boolean(self, sect, opt): '''The option is a boolean value. (Support for Python 2.2).''' ! return self._options[sect, opt].is_boolean() def convert(self, sect, opt, value): '''Convert value from a string to the appropriate type.''' ! return self._options[sect, opt].convert(value) def unconvert(self, sect, opt): '''Convert value from the appropriate type to a string.''' ! return self._options[sect, opt].unconvert() def get_option(self, sect, opt): --- 552,586 ---- def display_name(self, sect, opt): '''A name for the option suitable for display to a user.''' ! return self._options[sect, opt.lower()].display_name() def default(self, sect, opt): '''The default value for the option.''' ! return self._options[sect, opt.lower()].default() def doc(self, sect, opt): '''Documentation for the option.''' ! return self._options[sect, opt.lower()].doc() def valid_input(self, sect, opt): '''Valid values for the option.''' ! return self._options[sect, opt.lower()].valid_input() def no_restore(self, sect, opt): '''Do not restore this option when restoring to defaults.''' ! return self._options[sect, opt.lower()].no_restore() def is_valid(self, sect, opt, value): '''Check if this is a valid value for this option.''' ! return self._options[sect, opt.lower()].is_valid(value) def multiple_values_allowed(self, sect, opt): '''Multiple values are allowed for this option.''' ! return self._options[sect, opt.lower()].multiple_values_allowed() def is_boolean(self, sect, opt): '''The option is a boolean value. (Support for Python 2.2).''' ! return self._options[sect, opt.lower()].is_boolean() def convert(self, sect, opt, value): '''Convert value from a string to the appropriate type.''' ! return self._options[sect, opt.lower()].convert(value) def unconvert(self, sect, opt): '''Convert value from the appropriate type to a string.''' ! return self._options[sect, opt.lower()].unconvert() def get_option(self, sect, opt): *************** *** 588,598 **** if self.conversion_table.has_key((sect, opt)): sect, opt = self.conversion_table[sect, opt] ! return self._options[sect, opt] def get(self, sect, opt): '''Get an option value.''' ! if self.conversion_table.has_key((sect, opt)): ! sect, opt = self.conversion_table[sect, opt] ! return self.get_option(sect, opt).get() def __getitem__(self, key): --- 588,598 ---- if self.conversion_table.has_key((sect, opt)): sect, opt = self.conversion_table[sect, opt] ! return self._options[sect, opt.lower()] def get(self, sect, opt): '''Get an option value.''' ! if self.conversion_table.has_key((sect, opt.lower())): ! sect, opt = self.conversion_table[sect, opt.lower()] ! return self.get_option(sect, opt.lower()).get() def __getitem__(self, key): *************** *** 601,612 **** def set(self, sect, opt, val=None): '''Set an option.''' ! if self.conversion_table.has_key((sect, opt)): ! sect, opt = self.conversion_table[sect, opt] if self.is_valid(sect, opt, val): ! self._options[sect, opt].set(val) else: print >> sys.stderr, ("Attempted to set [%s] %s with invalid" " value %s (%s)" % ! (sect, opt, val, type(val))) def set_from_cmdline(self, arg, stream=None): --- 601,612 ---- def set(self, sect, opt, val=None): '''Set an option.''' ! if self.conversion_table.has_key((sect, opt.lower())): ! sect, opt = self.conversion_table[sect, opt.lower()] if self.is_valid(sect, opt, val): ! self._options[sect, opt.lower()].set(val) else: print >> sys.stderr, ("Attempted to set [%s] %s with invalid" " value %s (%s)" % ! (sect, opt.lower(), val, type(val))) def set_from_cmdline(self, arg, stream=None): *************** *** 617,620 **** --- 617,621 ---- """ sect, opt, val = arg.split(':', 2) + opt = opt.lower() try: val = self.convert(sect, opt, val) *************** *** 716,720 **** if section is not None and option is not None: output.write(self._options[section, ! option].as_nice_string(section)) return output.getvalue() --- 717,721 ---- if section is not None and option is not None: output.write(self._options[section, ! option.lower()].as_nice_string(section)) return output.getvalue() *************** *** 724,728 **** if section is not None and sect != section: continue ! output.write(self._options[sect, opt].as_nice_string(sect)) return output.getvalue() --- 725,729 ---- if section is not None and sect != section: continue ! output.write(self._options[sect, opt.lower()].as_nice_string(sect)) return output.getvalue() From tim at fourstonesExpressions.com Mon Dec 15 23:54:14 2003 From: tim at fourstonesExpressions.com (Tim Stone) Date: Mon Dec 15 23:54:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionsClass.py, 1.19, 1.20 In-Reply-To: References: Message-ID: Watch the world reel now On Mon, 15 Dec 2003 20:48:31 -0800, Tony Meyer wrote: > Update of /cvsroot/spambayes/spambayes/spambayes > In directory sc8-pr-cvs1:/tmp/cvs-serv9453/spambayes > > Modified Files: > OptionsClass.py > Log Message: > Option names are always case insensitive, no matter what. > > Index: OptionsClass.py > =================================================================== > RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v > retrieving revision 1.19 > retrieving revision 1.20 > diff -C2 -d -r1.19 -r1.20 > *** OptionsClass.py 15 Dec 2003 09:20:33 -0000 1.19 > --- OptionsClass.py 16 Dec 2003 04:48:28 -0000 1.20 > *************** > *** 552,586 **** > def display_name(self, sect, opt): > '''A name for the option suitable for display to a user.''' > ! return self._options[sect, opt].display_name() > def default(self, sect, opt): > '''The default value for the option.''' > ! return self._options[sect, opt].default() > def doc(self, sect, opt): > '''Documentation for the option.''' > ! return self._options[sect, opt].doc() > def valid_input(self, sect, opt): > '''Valid values for the option.''' > ! return self._options[sect, opt].valid_input() > def no_restore(self, sect, opt): > '''Do not restore this option when restoring to defaults.''' > ! return self._options[sect, opt].no_restore() > def is_valid(self, sect, opt, value): > '''Check if this is a valid value for this option.''' > ! return self._options[sect, opt].is_valid(value) > def multiple_values_allowed(self, sect, opt): > '''Multiple values are allowed for this option.''' > ! return self._options[sect, opt].multiple_values_allowed() > > def is_boolean(self, sect, opt): > '''The option is a boolean value. (Support for Python 2.2).''' > ! return self._options[sect, opt].is_boolean() > > def convert(self, sect, opt, value): > '''Convert value from a string to the appropriate type.''' > ! return self._options[sect, opt].convert(value) > > def unconvert(self, sect, opt): > '''Convert value from the appropriate type to a string.''' > ! return self._options[sect, opt].unconvert() > > def get_option(self, sect, opt): > --- 552,586 ---- > def display_name(self, sect, opt): > '''A name for the option suitable for display to a user.''' > ! return self._options[sect, opt.lower()].display_name() > def default(self, sect, opt): > '''The default value for the option.''' > ! return self._options[sect, opt.lower()].default() > def doc(self, sect, opt): > '''Documentation for the option.''' > ! return self._options[sect, opt.lower()].doc() > def valid_input(self, sect, opt): > '''Valid values for the option.''' > ! return self._options[sect, opt.lower()].valid_input() > def no_restore(self, sect, opt): > '''Do not restore this option when restoring to defaults.''' > ! return self._options[sect, opt.lower()].no_restore() > def is_valid(self, sect, opt, value): > '''Check if this is a valid value for this option.''' > ! return self._options[sect, opt.lower()].is_valid(value) > def multiple_values_allowed(self, sect, opt): > '''Multiple values are allowed for this option.''' > ! return self._options[sect, > opt.lower()].multiple_values_allowed() > > def is_boolean(self, sect, opt): > '''The option is a boolean value. (Support for Python 2.2).''' > ! return self._options[sect, opt.lower()].is_boolean() > > def convert(self, sect, opt, value): > '''Convert value from a string to the appropriate type.''' > ! return self._options[sect, opt.lower()].convert(value) > > def unconvert(self, sect, opt): > '''Convert value from the appropriate type to a string.''' > ! return self._options[sect, opt.lower()].unconvert() > > def get_option(self, sect, opt): > *************** > *** 588,598 **** > if self.conversion_table.has_key((sect, opt)): > sect, opt = self.conversion_table[sect, opt] > ! return self._options[sect, opt] > > def get(self, sect, opt): > '''Get an option value.''' > ! if self.conversion_table.has_key((sect, opt)): > ! sect, opt = self.conversion_table[sect, opt] > ! return self.get_option(sect, opt).get() > > def __getitem__(self, key): > --- 588,598 ---- > if self.conversion_table.has_key((sect, opt)): > sect, opt = self.conversion_table[sect, opt] > ! return self._options[sect, opt.lower()] > > def get(self, sect, opt): > '''Get an option value.''' > ! if self.conversion_table.has_key((sect, opt.lower())): > ! sect, opt = self.conversion_table[sect, opt.lower()] > ! return self.get_option(sect, opt.lower()).get() > > def __getitem__(self, key): > *************** > *** 601,612 **** > def set(self, sect, opt, val=None): > '''Set an option.''' > ! if self.conversion_table.has_key((sect, opt)): > ! sect, opt = self.conversion_table[sect, opt] > if self.is_valid(sect, opt, val): > ! self._options[sect, opt].set(val) > else: > print >> sys.stderr, ("Attempted to set [%s] %s with > invalid" > " value %s (%s)" % > ! (sect, opt, val, type(val))) > > def set_from_cmdline(self, arg, stream=None): > --- 601,612 ---- > def set(self, sect, opt, val=None): > '''Set an option.''' > ! if self.conversion_table.has_key((sect, opt.lower())): > ! sect, opt = self.conversion_table[sect, opt.lower()] > if self.is_valid(sect, opt, val): > ! self._options[sect, opt.lower()].set(val) > else: > print >> sys.stderr, ("Attempted to set [%s] %s with > invalid" > " value %s (%s)" % > ! (sect, opt.lower(), val, type(val))) > > def set_from_cmdline(self, arg, stream=None): > *************** > *** 617,620 **** > --- 617,621 ---- > """ > sect, opt, val = arg.split(':', 2) > + opt = opt.lower() > try: > val = self.convert(sect, opt, val) > *************** > *** 716,720 **** > if section is not None and option is not None: > output.write(self._options[section, > ! option].as_nice_string(section)) > return output.getvalue() > > --- 717,721 ---- > if section is not None and option is not None: > output.write(self._options[section, > ! > option.lower()].as_nice_string(section)) > return output.getvalue() > > *************** > *** 724,728 **** > if section is not None and sect != section: > continue > ! output.write(self._options[sect, opt].as_nice_string(sect)) > return output.getvalue() > > --- 725,729 ---- > if section is not None and sect != section: > continue > ! output.write(self._options[sect, > opt.lower()].as_nice_string(sect)) > return output.getvalue() > > > > > _______________________________________________ > Spambayes-checkins mailing list > Spambayes-checkins@python.org > http://mail.python.org/mailman/listinfo/spambayes-checkins > -- Vous exprimer; Expr?sese; Te stesso esprimere; Express yourself! Tim Stone See my photography at www.fourstonesExpressions.com See my writing at www.xanga.com/obj3kshun From tim_one at users.sourceforge.net Tue Dec 16 00:00:00 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:00:04 2003 Subject: [Spambayes-checkins] spambayes/spambayes classifier.py,1.13,1.14 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv10943/spambayes Modified Files: classifier.py Log Message: _worddistanceget(): Don't do the expensive lookup of unknown_word_prob unless it's actually needed. Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.13 retrieving revision 1.14 diff -C2 -d -r1.13 -r1.14 *** classifier.py 16 Dec 2003 02:46:42 -0000 1.13 --- classifier.py 16 Dec 2003 04:59:58 -0000 1.14 *************** *** 383,387 **** tokens = [(l[i],"%s %s" % (l[i],l[(i+1)%len(l)]), l[(i+1)%len(l)]) for i in xrange(len(l))] ! # Run through all the triplets and add the strongest # clue from each (note also the comment above explaining --- 383,387 ---- tokens = [(l[i],"%s %s" % (l[i],l[(i+1)%len(l)]), l[(i+1)%len(l)]) for i in xrange(len(l))] ! # Run through all the triplets and add the strongest # clue from each (note also the comment above explaining *************** *** 409,416 **** def _worddistanceget(self, word): - unknown = options["Classifier", "unknown_word_prob"] record = self._wordinfoget(word) if record is None: ! prob = unknown else: prob = self.probability(record) --- 409,415 ---- def _worddistanceget(self, word): record = self._wordinfoget(word) if record is None: ! prob = options["Classifier", "unknown_word_prob"] else: prob = self.probability(record) *************** *** 451,455 **** except StopIteration: break ! def _wordinfokeys(self): return self.wordinfo.keys() --- 450,454 ---- except StopIteration: break ! def _wordinfokeys(self): return self.wordinfo.keys() From tim_one at users.sourceforge.net Tue Dec 16 00:06:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:39 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs/resources rclabels2text.py, 1.1, 1.2 rcparser.py, 1.10, 1.11 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources In directory sc8-pr-cvs1:/tmp/cvs-serv12349/Outlook2000/dialogs/resources Modified Files: rclabels2text.py rcparser.py Log Message: Whitespace normalization. Index: rclabels2text.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/rclabels2text.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** rclabels2text.py 20 Aug 2003 02:12:48 -0000 1.1 --- rclabels2text.py 16 Dec 2003 05:06:33 -0000 1.2 *************** *** 37,41 **** out.write(s) out.write("\n\n") ! out.close() os.startfile(outputFilename); --- 37,41 ---- out.write(s) out.write("\n\n") ! out.close() os.startfile(outputFilename); Index: rcparser.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/resources/rcparser.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** rcparser.py 26 Aug 2003 07:23:04 -0000 1.10 --- rcparser.py 16 Dec 2003 05:06:33 -0000 1.11 *************** *** 90,94 **** #print t return t ! class RCParser: --- 90,94 ---- #print t return t ! class RCParser: *************** *** 99,103 **** token = "" ! def __init__(self): self.ids = {"IDOK":1, "IDCANCEL":2, "IDC_STATIC": -1} self.names = {1:"IDOK", 2:"IDCANCEL", -1:"IDC_STATIC"} --- 99,103 ---- token = "" ! def __init__(self): self.ids = {"IDOK":1, "IDCANCEL":2, "IDC_STATIC": -1} self.names = {1:"IDOK", 2:"IDCANCEL", -1:"IDC_STATIC"} *************** *** 114,122 **** self.token = None return self.token ! def getCommaToken(self): tok = self.getToken() assert tok == ",", "Token '%s' should be a comma!" % tok ! def loadDialogs(self, rcFileName): """ --- 114,122 ---- self.token = None return self.token ! def getCommaToken(self): tok = self.getToken() assert tok == ",", "Token '%s' should be a comma!" % tok ! def loadDialogs(self, rcFileName): """ *************** *** 206,214 **** self.names[id] = id_name return id ! def lang(self): while self.token[0:4]=="LANG" or self.token[0:7]=="SUBLANG" or self.token==',': self.getToken(); ! def dialog(self, name): dlg = DialogDef(name,self.addId(name)) --- 206,214 ---- self.names[id] = id_name return id ! def lang(self): while self.token[0:4]=="LANG" or self.token[0:7]=="SUBLANG" or self.token==',': self.getToken(); ! def dialog(self, name): dlg = DialogDef(name,self.addId(name)) *************** *** 246,250 **** break self.dialogs[name] = dlg.createDialogTemplate() ! def dialogStyle(self, dlg): dlg.style, dlg.styles = self.styles( [], win32con.WS_VISIBLE | win32con.DS_SETFONT) --- 246,250 ---- break self.dialogs[name] = dlg.createDialogTemplate() ! def dialogStyle(self, dlg): dlg.style, dlg.styles = self.styles( [], win32con.WS_VISIBLE | win32con.DS_SETFONT) *************** *** 252,260 **** self.getToken() dlg.styleEx, dlg.stylesEx = self.styles( [], 0) ! def styles(self, defaults, defaultStyle): list = defaults style = defaultStyle ! if "STYLE"==self.token: self.getToken() --- 252,260 ---- self.getToken() dlg.styleEx, dlg.stylesEx = self.styles( [], 0) ! def styles(self, defaults, defaultStyle): list = defaults style = defaultStyle ! if "STYLE"==self.token: self.getToken() *************** *** 285,291 **** self.getToken() self.debug("style is ",style) ! return style, list ! def dialogCaption(self, dlg): if "CAPTION"==self.token: --- 285,291 ---- self.getToken() self.debug("style is ",style) ! return style, list ! def dialogCaption(self, dlg): if "CAPTION"==self.token: From tim_one at users.sourceforge.net Tue Dec 16 00:06:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:42 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs FolderSelector.py, 1.31, 1.32 __init__.py, 1.11, 1.12 async_processor.py, 1.8, 1.9 dialog_map.py, 1.35, 1.36 dlgcore.py, 1.12, 1.13 opt_processors.py, 1.14, 1.15 processors.py, 1.12, 1.13 test_dialogs.py, 1.9, 1.10 wizard_processors.py, 1.9, 1.10 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs In directory sc8-pr-cvs1:/tmp/cvs-serv12349/Outlook2000/dialogs Modified Files: FolderSelector.py __init__.py async_processor.py dialog_map.py dlgcore.py opt_processors.py processors.py test_dialogs.py wizard_processors.py Log Message: Whitespace normalization. Index: FolderSelector.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/FolderSelector.py,v retrieving revision 1.31 retrieving revision 1.32 diff -C2 -d -r1.31 -r1.32 *** FolderSelector.py 15 Oct 2003 04:12:25 -0000 1.31 --- FolderSelector.py 16 Dec 2003 05:06:33 -0000 1.32 *************** *** 218,222 **** 0, 0) return array.array("c", buf), extra ! def UnpackTVItem(buffer): item_mask, item_hItem, item_state, item_stateMask, \ --- 218,222 ---- 0, 0) return array.array("c", buf), extra ! def UnpackTVItem(buffer): item_mask, item_hItem, item_state, item_stateMask, \ *************** *** 232,236 **** if not (item_mask & commctrl.TVIF_SELECTEDIMAGE): item_selimage = None if not (item_mask & commctrl.TVIF_STATE): item_state = item_stateMask = None ! if item_textptr: text = win32gui.PyGetString(item_textptr) --- 232,236 ---- if not (item_mask & commctrl.TVIF_SELECTEDIMAGE): item_selimage = None if not (item_mask & commctrl.TVIF_STATE): item_state = item_stateMask = None ! if item_textptr: text = win32gui.PyGetString(item_textptr) *************** *** 448,452 **** # Message processing # def GetMessageMap(self): ! def OnInitDialog (self, hwnd, msg, wparam, lparam): FolderSelector_Parent.OnInitDialog(self, hwnd, msg, wparam, lparam) --- 448,452 ---- # Message processing # def GetMessageMap(self): ! def OnInitDialog (self, hwnd, msg, wparam, lparam): FolderSelector_Parent.OnInitDialog(self, hwnd, msg, wparam, lparam) *************** *** 512,516 **** id_name = self._GetIDName(id) code = win32api.HIWORD(wparam) ! if code == win32con.BN_CLICKED: if id in (win32con.IDOK, win32con.IDCANCEL) and self.in_label_edit: --- 512,516 ---- id_name = self._GetIDName(id) code = win32api.HIWORD(wparam) ! if code == win32con.BN_CLICKED: if id in (win32con.IDOK, win32con.IDCANCEL) and self.in_label_edit: Index: __init__.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/__init__.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** __init__.py 5 Sep 2003 02:09:42 -0000 1.11 --- __init__.py 16 Dec 2003 05:06:33 -0000 1.12 *************** *** 55,59 **** except win32gui.error: pass ! import dlgcore dlg = dlgcore.ProcessorDialog(parent, manager, config, idd, commands) --- 55,59 ---- except win32gui.error: pass ! import dlgcore dlg = dlgcore.ProcessorDialog(parent, manager, config, idd, commands) *************** *** 69,73 **** print "Cancelling wizard" config_wizard.CancelWizardConfig(manager, config) ! def MakePropertyPage(parent, manager, config, idd, yoffset=24): """Creates a child dialog box to use as property page in a tab control""" --- 69,73 ---- print "Cancelling wizard" config_wizard.CancelWizardConfig(manager, config) ! def MakePropertyPage(parent, manager, config, idd, yoffset=24): """Creates a child dialog box to use as property page in a tab control""" *************** *** 78,82 **** if not parent: raise "Parent must be the tab control" ! import dlgcore dlg = dlgcore.ProcessorPage(parent, manager, config, idd, commands, yoffset) --- 78,82 ---- if not parent: raise "Parent must be the tab control" ! import dlgcore dlg = dlgcore.ProcessorPage(parent, manager, config, idd, commands, yoffset) Index: async_processor.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/async_processor.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** async_processor.py 7 Sep 2003 03:53:13 -0000 1.8 --- async_processor.py 16 Dec 2003 05:06:33 -0000 1.9 *************** *** 160,164 **** else: raise RuntimeError, "Not one of my messages??" ! def OnFinished(self, wparam, lparam): self.seen_finished = True --- 160,164 ---- else: raise RuntimeError, "Not one of my messages??" ! def OnFinished(self, wparam, lparam): self.seen_finished = True *************** *** 177,181 **** win32con.WM_SETTEXT, 0, text) ! def OnProgressStatus(self, wparam, lparam): self.SetStatusText(self.progress_status) --- 177,181 ---- win32con.WM_SETTEXT, 0, text) ! def OnProgressStatus(self, wparam, lparam): self.SetStatusText(self.progress_status) *************** *** 191,195 **** def OnProgressWarning(self, wparam, lparam): pass ! def OnClicked(self, id): self.StartProcess() --- 191,195 ---- def OnProgressWarning(self, wparam, lparam): pass ! def OnClicked(self, id): self.StartProcess() *************** *** 289,292 **** for i in range(2): p.tick() ! print "Done!" --- 289,292 ---- for i in range(2): p.tick() ! print "Done!" Index: dialog_map.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dialog_map.py,v retrieving revision 1.35 retrieving revision 1.36 diff -C2 -d -r1.35 -r1.36 *** dialog_map.py 29 Sep 2003 02:14:26 -0000 1.35 --- dialog_map.py 16 Dec 2003 05:06:33 -0000 1.36 *************** *** 53,57 **** big = "spam" small = "ham" ! if big is not None: db_status = "%s\nWarning: you have much more %s than %s - " \ "SpamBayes works best with approximately even " \ --- 53,57 ---- big = "spam" small = "ham" ! if big is not None: db_status = "%s\nWarning: you have much more %s than %s - " \ "SpamBayes works best with approximately even " \ *************** *** 151,155 **** else: unsure_text = "unsure messages untouched" ! watch_names = manager.FormatFolderNames( config.watch_folder_ids, config.watch_include_sub) --- 151,155 ---- else: unsure_text = "unsure messages untouched" ! watch_names = manager.FormatFolderNames( config.watch_folder_ids, config.watch_include_sub) *************** *** 165,174 **** ControlProcessor.__init__(self, window, control_ids) self.page_ids = page_ids.split() ! def Init(self): self.pages = {} self.currentPage = None self.currentPageIndex = -1 ! self.currentPageHwnd = None for index, page_id in enumerate(self.page_ids): template = self.window.manager.dialog_parser.dialogs[page_id] --- 165,174 ---- ControlProcessor.__init__(self, window, control_ids) self.page_ids = page_ids.split() ! def Init(self): self.pages = {} self.currentPage = None self.currentPageIndex = -1 ! self.currentPageHwnd = None for index, page_id in enumerate(self.page_ids): template = self.window.manager.dialog_parser.dialogs[page_id] *************** *** 183,187 **** return False return True ! def OnNotify(self, nmhdr, wparam, lparam): # this does not appear to be in commctrl module --- 183,187 ---- return False return True ! def OnNotify(self, nmhdr, wparam, lparam): # this does not appear to be in commctrl module *************** *** 203,207 **** self.currentPageIndex = index return 0 ! def addPage(self, item, idName, label): format = "iiiiiii" --- 203,207 ---- self.currentPageIndex = index return 0 ! def addPage(self, item, idName, label): format = "iiiiiii" *************** *** 209,213 **** address,l = win32gui.PyGetBufferAddressAndLen(lbuf) win32gui.PySetString(address, label) ! buf = struct.pack(format, commctrl.TCIF_TEXT, # mask --- 209,213 ---- address,l = win32gui.PyGetBufferAddressAndLen(lbuf) win32gui.PySetString(address, label) ! buf = struct.pack(format, commctrl.TCIF_TEXT, # mask *************** *** 301,305 **** ShowDialog(parent, self.window.manager, self.window.config, self.idd) self.window.LoadAllControls() ! def GetPopupHelpText(self, id): dd = self.window.manager.dialog_parser.dialogs[self.idd] --- 301,305 ---- ShowDialog(parent, self.window.manager, self.window.config, self.idd) self.window.LoadAllControls() ! def GetPopupHelpText(self, id): dd = self.window.manager.dialog_parser.dialogs[self.idd] *************** *** 342,346 **** # And show the wizard. ShowWizard(parent, manager, self.idd, use_existing_config = True) ! def WizardFinish(mgr, window): print "Wizard Done!" --- 342,346 ---- # And show the wizard. ShowWizard(parent, manager, self.idd, use_existing_config = True) ! def WizardFinish(mgr, window): print "Wizard Done!" *************** *** 377,381 **** try: progress.tick() ! if rescore: # Setup the "filter now" config to what we want. --- 377,381 ---- try: progress.tick() ! if rescore: # Setup the "filter now" config to what we want. *************** *** 390,394 **** import filter filter.filterer(mgr, config, progress) ! bayes = classifier_data.bayes progress.set_status("Completed training with %d spam and %d good messages" \ --- 390,394 ---- import filter filter.filterer(mgr, config, progress) ! bayes = classifier_data.bayes progress.set_status("Completed training with %d spam and %d good messages" \ *************** *** 405,409 **** (CloseButtonProcessor, "IDOK IDCANCEL"), (TabProcessor, "IDC_TAB", ! """IDD_GENERAL IDD_FILTER IDD_TRAINING IDD_ADVANCED"""), (CommandButtonProcessor, "IDC_ABOUT_BTN", ShowAbout, ()), --- 405,409 ---- (CloseButtonProcessor, "IDOK IDCANCEL"), (TabProcessor, "IDC_TAB", ! """IDD_GENERAL IDD_FILTER IDD_TRAINING IDD_ADVANCED"""), (CommandButtonProcessor, "IDC_ABOUT_BTN", ShowAbout, ()), *************** *** 448,452 **** (EditNumberProcessor, "IDC_EDIT_UNSURE IDC_SLIDER_UNSURE", "Filter.unsure_threshold"), ! (ComboProcessor, "IDC_ACTION_UNSURE", "Filter.unsure_action"), (BoolButtonProcessor, "IDC_MARK_UNSURE_AS_READ", "Filter.unsure_mark_as_read"), --- 448,452 ---- (EditNumberProcessor, "IDC_EDIT_UNSURE IDC_SLIDER_UNSURE", "Filter.unsure_threshold"), ! (ComboProcessor, "IDC_ACTION_UNSURE", "Filter.unsure_action"), (BoolButtonProcessor, "IDC_MARK_UNSURE_AS_READ", "Filter.unsure_mark_as_read"), *************** *** 473,477 **** (ComboProcessor, "IDC_RECOVER_RS", "General.recover_from_spam_message_state", "not change the message,mark the message as read,mark the message as unread"), ! ), "IDD_ADVANCED" : ( --- 473,477 ---- (ComboProcessor, "IDC_RECOVER_RS", "General.recover_from_spam_message_state", "not change the message,mark the message as read,mark the message as unread"), ! ), "IDD_ADVANCED" : ( *************** *** 531,535 **** "Training.spam_folder_ids"), (BoolButtonProcessor, "IDC_BUT_RESCORE", "Training.rescore"), ! ), "IDD_WIZARD_TRAIN" : ( --- 531,535 ---- "Training.spam_folder_ids"), (BoolButtonProcessor, "IDC_BUT_RESCORE", "Training.rescore"), ! ), "IDD_WIZARD_TRAIN" : ( Index: dlgcore.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/dlgcore.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** dlgcore.py 27 Aug 2003 02:11:38 -0000 1.12 --- dlgcore.py 16 Dec 2003 05:06:33 -0000 1.13 *************** *** 22,26 **** if self.hwnd_tooltip is not None: win32gui.SendMessage(self.hwnd_tooltip, commctrl.TTM_TRACKACTIVATE, 0, 0) ! def ShowTooltipForControl(self, control_id, text): # Note sure this tooltip stuff is quite right! --- 22,26 ---- if self.hwnd_tooltip is not None: win32gui.SendMessage(self.hwnd_tooltip, commctrl.TTM_TRACKACTIVATE, 0, 0) ! def ShowTooltipForControl(self, control_id, text): # Note sure this tooltip stuff is quite right! *************** *** 42,46 **** commctrl.TTM_SETMAXTIPWIDTH, 0, 300) ! format = "iiiiiiiiiii" tt_size = struct.calcsize(format) --- 42,46 ---- commctrl.TTM_SETMAXTIPWIDTH, 0, 300) ! format = "iiiiiiiiiii" tt_size = struct.calcsize(format) *************** *** 50,54 **** flags = commctrl.TTF_TRACK | commctrl.TTF_ABSOLUTE data = struct.pack(format, tt_size, flags, hwnd_dialog, uID, 0,0,0,0, 0, text_address, 0) ! # Add a tool for this control only if we haven't already if control_id not in self.tooltip_tools: --- 50,54 ---- flags = commctrl.TTF_TRACK | commctrl.TTF_ABSOLUTE data = struct.pack(format, tt_size, flags, hwnd_dialog, uID, 0,0,0,0, 0, text_address, 0) ! # Add a tool for this control only if we haven't already if control_id not in self.tooltip_tools: *************** *** 109,113 **** centre_x, centre_y = win32gui.ClientToScreen( desktop, ( (dt_r-dt_l)/2, (dt_b-dt_t)/2) ) win32gui.MoveWindow(self.hwnd, centre_x-(w/2), centre_y-(h/2), w, h, 0) ! def OnInitDialog(self, hwnd, msg, wparam, lparam): self.hwnd = hwnd --- 109,113 ---- centre_x, centre_y = win32gui.ClientToScreen( desktop, ( (dt_r-dt_l)/2, (dt_b-dt_t)/2) ) win32gui.MoveWindow(self.hwnd, centre_x-(w/2), centre_y-(h/2), w, h, 0) ! def OnInitDialog(self, hwnd, msg, wparam, lparam): self.hwnd = hwnd *************** *** 160,164 **** def OnDestroy(self, hwnd, msg, wparam, lparam): self.tt.HideTooltip() ! def OnHelp(self, hwnd, msg, wparam, lparam): format = "iiiiiii" --- 160,164 ---- def OnDestroy(self, hwnd, msg, wparam, lparam): self.tt.HideTooltip() ! def OnHelp(self, hwnd, msg, wparam, lparam): format = "iiiiiii" *************** *** 226,230 **** if cp is not None: return cp.GetPopupHelpText(iCtrlId) ! print "Can not get command processor for", self._GetIDName(iCtrlId) return None --- 226,230 ---- if cp is not None: return cp.GetPopupHelpText(iCtrlId) ! print "Can not get command processor for", self._GetIDName(iCtrlId) return None *************** *** 232,236 **** for cp in self.command_processors.values(): cp.OnRButtonUp(wparam,lparam) ! def OnCommandProcessorMessage(self, hwnd, msg, wparam, lparam): for p in self.processor_message_map[msg]: --- 232,236 ---- for cp in self.command_processors.values(): cp.OnRButtonUp(wparam,lparam) ! def OnCommandProcessorMessage(self, hwnd, msg, wparam, lparam): for p in self.processor_message_map[msg]: *************** *** 265,269 **** win32gui.MessageBox(self.hwnd, str(why), "SpamBayes", mb_flags) return False ! def SaveAllControls(self): for p in self.all_processors: --- 265,269 ---- win32gui.MessageBox(self.hwnd, str(why), "SpamBayes", mb_flags) return False ! def SaveAllControls(self): for p in self.all_processors: *************** *** 324,326 **** self.template[0][2] = self.template[0][2] | win32con.WS_CHILD return win32gui.CreateDialogIndirect(self.hinst, self.template, self.parent, message_map) - --- 324,325 ---- Index: opt_processors.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/opt_processors.py,v retrieving revision 1.14 retrieving revision 1.15 diff -C2 -d -r1.14 -r1.15 *** opt_processors.py 9 Sep 2003 01:13:07 -0000 1.14 --- opt_processors.py 16 Dec 2003 05:06:33 -0000 1.15 *************** *** 1,4 **** # Option Control Processors for our dialog. ! # These are extensions to basic Control Processors that are linked with # SpamBayes options. --- 1,4 ---- # Option Control Processors for our dialog. ! # These are extensions to basic Control Processors that are linked with # SpamBayes options. *************** *** 128,132 **** else: assert 0, "Couldn't find a checked button" ! # A "Combo" processor, that loads valid strings from the option. class ComboProcessor(OptionControlProcessor): --- 128,132 ---- else: assert 0, "Couldn't find a checked button" ! # A "Combo" processor, that loads valid strings from the option. class ComboProcessor(OptionControlProcessor): *************** *** 140,144 **** self.option_to_text = zip(self.option.valid_input(),self.option.valid_input()) self.text_to_option = dict(self.option_to_text) ! def OnCommand(self, wparam, lparam): code = win32api.HIWORD(wparam) --- 140,144 ---- self.option_to_text = zip(self.option.valid_input(),self.option.valid_input()) self.text_to_option = dict(self.option_to_text) ! def OnCommand(self, wparam, lparam): code = win32api.HIWORD(wparam) *************** *** 180,184 **** "automatically adjust" return OptionControlProcessor.GetPopupHelpText(self, id) ! def GetMessages(self): return [win32con.WM_HSCROLL] --- 180,184 ---- "automatically adjust" return OptionControlProcessor.GetPopupHelpText(self, id) ! def GetMessages(self): return [win32con.WM_HSCROLL] *************** *** 202,206 **** # They are typing - value may be currently invalid pass ! def Init(self): OptionControlProcessor.Init(self) --- 202,206 ---- # They are typing - value may be currently invalid pass ! def Init(self): OptionControlProcessor.Init(self) *************** *** 249,253 **** raise ValueError, "Value must be between %d and %d" % (self.min_val, self.max_val) self.SetOptionValue(val) ! # Folder IDs, and the "include_sub" option, if applicable. class FolderIDProcessor(OptionControlProcessor): --- 249,253 ---- raise ValueError, "Value must be between %d and %d" % (self.min_val, self.max_val) self.SetOptionValue(val) ! # Folder IDs, and the "include_sub" option, if applicable. class FolderIDProcessor(OptionControlProcessor): Index: processors.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/processors.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** processors.py 5 Sep 2003 06:54:23 -0000 1.12 --- processors.py 16 Dec 2003 05:06:33 -0000 1.13 *************** *** 68,72 **** def GetPopupHelpText(self, cid): return None ! class ButtonProcessor(ControlProcessor): def OnCommand(self, wparam, lparam): --- 68,72 ---- def GetPopupHelpText(self, cid): return None ! class ButtonProcessor(ControlProcessor): def OnCommand(self, wparam, lparam): *************** *** 93,97 **** args = (self.window,) + self.args self.func(*args) ! def GetPopupHelpText(self, ctrlid): assert ctrlid == self.control_id --- 93,97 ---- args = (self.window,) + self.args self.func(*args) ! def GetPopupHelpText(self, ctrlid): assert ctrlid == self.control_id Index: test_dialogs.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/test_dialogs.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** test_dialogs.py 26 Aug 2003 02:27:26 -0000 1.9 --- test_dialogs.py 16 Dec 2003 05:06:33 -0000 1.10 *************** *** 11,15 **** except ImportError: sys.path.append(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))) ! import manager, win32con mgr = manager.GetManager() --- 11,15 ---- except ImportError: sys.path.append(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))) ! import manager, win32con mgr = manager.GetManager() Index: wizard_processors.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/wizard_processors.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** wizard_processors.py 26 Aug 2003 04:52:06 -0000 1.9 --- wizard_processors.py 16 Dec 2003 05:06:33 -0000 1.10 *************** *** 88,92 **** except: pass ! self.finish_fn(self.window.manager, self.window) win32gui.EndDialog(self.window.hwnd, win32con.IDOK) --- 88,92 ---- except: pass ! self.finish_fn(self.window.manager, self.window) win32gui.EndDialog(self.window.hwnd, win32con.IDOK) *************** *** 103,107 **** print "Back button switching to page", pageNo self.switchToPage(pageNo) ! def switchToPage(self, index): if self.currentPageHwnd is not None: --- 103,107 ---- print "Back button switching to page", pageNo self.switchToPage(pageNo) ! def switchToPage(self, index): if self.currentPageHwnd is not None: *************** *** 129,133 **** return index assert 0, "No page '%s'" % next ! # methods to be overridden. default implementation is simple sequential def getNextPage(self): --- 129,133 ---- return index assert 0, "No page '%s'" % next ! # methods to be overridden. default implementation is simple sequential def getNextPage(self): *************** *** 168,172 **** ok = not self.window.config.wizard.need_train return ok ! def getNextPage(self): index = self.currentPageIndex --- 168,172 ---- ok = not self.window.config.wizard.need_train return ok ! def getNextPage(self): index = self.currentPageIndex *************** *** 221,227 **** self.name_joiner = name_joiner self.in_setting_name = False ! name_sect_name, name_sub_option_name = option_folder_name.split(".") ! self.option_folder_name = window.config.get_option(name_sect_name, name_sub_option_name) --- 221,227 ---- self.name_joiner = name_joiner self.in_setting_name = False ! name_sect_name, name_sub_option_name = option_folder_name.split(".") ! self.option_folder_name = window.config.get_option(name_sect_name, name_sub_option_name) From tim_one at users.sourceforge.net Tue Dec 16 00:06:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:43 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py, 1.116, 1.117 config.py, 1.27, 1.28 config_wizard.py, 1.8, 1.9 manager.py, 1.93, 1.94 msgstore.py, 1.79, 1.80 oastats.py, 1.2, 1.3 tester.py, 1.20, 1.21 train.py, 1.34, 1.35 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv12349/Outlook2000 Modified Files: addin.py config.py config_wizard.py manager.py msgstore.py oastats.py tester.py train.py Log Message: Whitespace normalization. Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.116 retrieving revision 1.117 diff -C2 -d -r1.116 -r1.117 *** addin.py 10 Dec 2003 05:28:10 -0000 1.116 --- addin.py 16 Dec 2003 05:06:32 -0000 1.117 *************** *** 1444,1448 **** _DoRegister(klass, _winreg.HKEY_LOCAL_MACHINE) print "Registration (in HKEY_LOCAL_MACHINE) complete." ! def DllRegisterServer(): klass = OutlookAddin --- 1444,1448 ---- _DoRegister(klass, _winreg.HKEY_LOCAL_MACHINE) print "Registration (in HKEY_LOCAL_MACHINE) complete." ! def DllRegisterServer(): klass = OutlookAddin Index: config.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/config.py,v retrieving revision 1.27 retrieving revision 1.28 diff -C2 -d -r1.27 -r1.28 *** config.py 26 Aug 2003 02:35:45 -0000 1.27 --- config.py 16 Dec 2003 05:06:33 -0000 1.28 *************** *** 77,81 **** return "" return str(self.value) ! def multiple_values_allowed(self): return type(self.value)==types.ListType --- 77,81 ---- return "" return str(self.value) ! def multiple_values_allowed(self): return type(self.value)==types.ListType *************** *** 240,244 **** "Should the timer only be used for 'Inbox' type folders?", True, """The point of using a timer is to prevent the SpamBayes filter ! getting in the way the builtin Outlook rules. Therefore, is it generally only necessary to use a timer for folders that have new items being delivered directly to them. Folders that are not inbox --- 240,244 ---- "Should the timer only be used for 'Inbox' type folders?", True, """The point of using a timer is to prevent the SpamBayes filter ! getting in the way the builtin Outlook rules. Therefore, is it generally only necessary to use a timer for folders that have new items being delivered directly to them. Folders that are not inbox *************** *** 386,390 **** c.filter.unsure_folder_id = None if c.filter.unsure_folder_id is not None: raise ValueError, "unsure_id wrong (%r)" % (c.filter.unsure_folder_id,) ! options.set("Filter", "filter_now", True) print "Filter_now from container is", c.filter.filter_now --- 386,390 ---- c.filter.unsure_folder_id = None if c.filter.unsure_folder_id is not None: raise ValueError, "unsure_id wrong (%r)" % (c.filter.unsure_folder_id,) ! options.set("Filter", "filter_now", True) print "Filter_now from container is", c.filter.filter_now *************** *** 405,407 **** options.update_file("delme.cfg") print "Created 'delme.cfg'" - --- 405,406 ---- Index: config_wizard.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/config_wizard.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** config_wizard.py 15 Oct 2003 04:11:12 -0000 1.8 --- config_wizard.py 16 Dec 2003 05:06:33 -0000 1.9 *************** *** 38,42 **** new_config.filter.watch_folder_ids = [] new_config.filter.watch_include_sub = False ! wc = new_config.wizard if from_existing: --- 38,42 ---- new_config.filter.watch_folder_ids = [] new_config.filter.watch_include_sub = False ! wc = new_config.wizard if from_existing: *************** *** 87,91 **** manager.ReportError(msg) return None ! def CommitWizardConfig(manager, wc): # If the user want to manually configure, then don't do anything --- 87,91 ---- manager.ReportError(msg) return None ! def CommitWizardConfig(manager, wc): # If the user want to manually configure, then don't do anything *************** *** 150,152 **** InitWizardConfig(manager, cfg, from_existing) return cfg - \ No newline at end of file --- 150,151 ---- Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.93 retrieving revision 1.94 diff -C2 -d -r1.93 -r1.94 *** manager.py 15 Dec 2003 09:13:40 -0000 1.93 --- manager.py 16 Dec 2003 05:06:33 -0000 1.94 *************** *** 504,508 **** # Can't make the directory. return self.application_directory ! def MigrateDataDirectory(self): # A bit of a nod to save people doing a full retrain. --- 504,508 ---- # Can't make the directory. return self.application_directory ! def MigrateDataDirectory(self): # A bit of a nod to save people doing a full retrain. *************** *** 511,515 **** # Note that this is migrating data for very old versions of the # plugin (before the first decent binary!). The next time it is ! # touched it can die :) self._MigrateFile("default_bayes_database.pck") self._MigrateFile("default_bayes_database.db") --- 511,515 ---- # Note that this is migrating data for very old versions of the # plugin (before the first decent binary!). The next time it is ! # touched it can die :) self._MigrateFile("default_bayes_database.pck") self._MigrateFile("default_bayes_database.db") *************** *** 552,556 **** # Using MAPI to set them directly on the folder also has no effect. # Later: We have since discovered that Outlook stores user property ! # information in the 'associated contents' folder - see # msgstore.MAPIMsgStoreFolder.DoesFolderHaveOutlookField() for more # details. We can reverse engineer this well enough to determine --- 552,556 ---- # Using MAPI to set them directly on the folder also has no effect. # Later: We have since discovered that Outlook stores user property ! # information in the 'associated contents' folder - see # msgstore.MAPIMsgStoreFolder.DoesFolderHaveOutlookField() for more # details. We can reverse engineer this well enough to determine Index: msgstore.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v retrieving revision 1.79 retrieving revision 1.80 diff -C2 -d -r1.79 -r1.80 *** msgstore.py 10 Dec 2003 07:18:53 -0000 1.79 --- msgstore.py 16 Dec 2003 05:06:33 -0000 1.80 *************** *** 136,140 **** exc_val = NormalizeCOMException(exc_val) return exc_val[0] in known_failure_codes ! def ReportMAPIError(manager, what, exc_val): hr, exc_msg, exc, arg_err = exc_val --- 136,140 ---- exc_val = NormalizeCOMException(exc_val) return exc_val[0] in known_failure_codes ! def ReportMAPIError(manager, what, exc_val): hr, exc_msg, exc, arg_err = exc_val *************** *** 160,164 **** ReadOnlyException = ReadOnlyException ObjectChangedException = ObjectChangedException ! def __init__(self, outlook = None): self.outlook = outlook --- 160,164 ---- ReadOnlyException = ReadOnlyException ObjectChangedException = ObjectChangedException ! def __init__(self, outlook = None): self.outlook = outlook *************** *** 686,690 **** except pythoncom.com_error, details: raise MsgStoreExceptionFromCOMException(details) ! def CreateTemporaryMessage(self, msg_flags = None): # Create a message designed to be used temporarily. It is your --- 686,690 ---- except pythoncom.com_error, details: raise MsgStoreExceptionFromCOMException(details) ! def CreateTemporaryMessage(self, msg_flags = None): # Create a message designed to be used temporarily. It is your *************** *** 720,724 **** PR_SUBJECT_A, PR_TRANSPORT_MESSAGE_HEADERS_A, ! ) def __init__(self, msgstore, prop_row): --- 720,724 ---- PR_SUBJECT_A, PR_TRANSPORT_MESSAGE_HEADERS_A, ! ) def __init__(self, msgstore, prop_row): *************** *** 1195,1199 **** prop_ids = PROP_TAG( PT_BINARY, PROP_ID(resolve_ids[0])), \ PROP_TAG( PT_BINARY, PROP_ID(resolve_ids[1])) ! prop_tuples = (prop_ids[0],folder.id[0]), (prop_ids[1],folder.id[1]) self.mapi_object.SetProps(prop_tuples) --- 1195,1199 ---- prop_ids = PROP_TAG( PT_BINARY, PROP_ID(resolve_ids[0])), \ PROP_TAG( PT_BINARY, PROP_ID(resolve_ids[1])) ! prop_tuples = (prop_ids[0],folder.id[0]), (prop_ids[1],folder.id[1]) self.mapi_object.SetProps(prop_tuples) Index: oastats.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/oastats.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** oastats.py 30 Sep 2003 14:37:44 -0000 1.2 --- oastats.py 16 Dec 2003 05:06:33 -0000 1.3 *************** *** 74,78 **** s.RecordClassification(.2) print "\n".join(s.GetStats()) ! s = Stats(Config()) s.RecordClassification(.2) --- 74,78 ---- s.RecordClassification(.2) print "\n".join(s.GetStats()) ! s = Stats(Config()) s.RecordClassification(.2) Index: tester.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/tester.py,v retrieving revision 1.20 retrieving revision 1.21 diff -C2 -d -r1.20 -r1.21 *** tester.py 8 Sep 2003 07:51:51 -0000 1.20 --- tester.py 16 Dec 2003 05:06:33 -0000 1.21 *************** *** 167,171 **** ms_folder = self.manager.message_store.GetFolder(folder) TestFailed("The test message remained in folder '%s'" % ms_folder.GetFQName()) ! def _CleanTestMessageFromFolder(self, folder): subject = TEST_SUBJECT --- 167,171 ---- ms_folder = self.manager.message_store.GetFolder(folder) TestFailed("The test message remained in folder '%s'" % ms_folder.GetFQName()) ! def _CleanTestMessageFromFolder(self, folder): subject = TEST_SUBJECT *************** *** 253,257 **** TestFailed("Something caused a new ham message to appear") check_words(words, bayes, 0, 0) ! # Now move the message back to the inbox - it should get trained. store_msg = driver.manager.message_store.GetMessage(spam_msg) --- 253,257 ---- TestFailed("Something caused a new ham message to appear") check_words(words, bayes, 0, 0) ! # Now move the message back to the inbox - it should get trained. store_msg = driver.manager.message_store.GetMessage(spam_msg) *************** *** 308,315 **** if not train.been_trained_as_spam(store_msg, driver.manager.classifier_data): TestFailed("Message was not identified as Spam after moving") ! # word infos still be 'spam' check_words(words, bayes, 1, 0) ! # Now undo the damage we did. was_spam = train.untrain_message(store_msg, driver.manager.classifier_data) --- 308,315 ---- if not train.been_trained_as_spam(store_msg, driver.manager.classifier_data): TestFailed("Message was not identified as Spam after moving") ! # word infos still be 'spam' check_words(words, bayes, 1, 0) ! # Now undo the damage we did. was_spam = train.untrain_message(store_msg, driver.manager.classifier_data) *************** *** 323,327 **** if need_untrain: train.untrain_message(store_msg, driver.manager.classifier_data) ! # Check all the counts are back where we started. if nspam != bayes.nspam: --- 323,327 ---- if need_untrain: train.untrain_message(store_msg, driver.manager.classifier_data) ! # Check all the counts are back where we started. if nspam != bayes.nspam: *************** *** 330,339 **** TestFailed("Ham count didn't get back to the same") check_words(words, bayes, 0, 0) ! if bayes.wordinfo != original_bayes.wordinfo: TestFailed("The bayes object's 'wordinfo' did not compare the same at the end of all this!") if bayes.probcache != original_bayes.probcache: TestFailed("The bayes object's 'probcache' did not compare the same at the end of all this!") ! spam_msg.Delete() print "Created a Spam message, and saw it get filtered and trained." --- 330,339 ---- TestFailed("Ham count didn't get back to the same") check_words(words, bayes, 0, 0) ! if bayes.wordinfo != original_bayes.wordinfo: TestFailed("The bayes object's 'wordinfo' did not compare the same at the end of all this!") if bayes.probcache != original_bayes.probcache: TestFailed("The bayes object's 'probcache' did not compare the same at the end of all this!") ! spam_msg.Delete() print "Created a Spam message, and saw it get filtered and trained." *************** *** 441,445 **** }, run_tests, manager) ! apply_with_new_config(manager, {"Filter.timer_enabled": True, --- 441,445 ---- }, run_tests, manager) ! apply_with_new_config(manager, {"Filter.timer_enabled": True, *************** *** 452,456 **** }, run_tests, manager) ! apply_with_new_config(manager, {"Filter.timer_enabled": True, --- 452,456 ---- }, run_tests, manager) ! apply_with_new_config(manager, {"Filter.timer_enabled": True, *************** *** 476,480 **** for (sect_name, opt_name), val in old_config.items(): manager.options.set(sect_name, opt_name, val) ! ############################################################################### # "Non-filter" tests are those that don't require us to create messages and --- 476,480 ---- for (sect_name, opt_name), val in old_config.items(): manager.options.set(sect_name, opt_name, val) ! ############################################################################### # "Non-filter" tests are those that don't require us to create messages and *************** *** 547,551 **** raise TestFailed("Couldn't find unknown folder in names '%s'" % names) print "Finished 'invalid ID' tests" ! ############################################################################### # "Failure" tests - execute some tests while provoking the msgstore to simulate --- 547,551 ---- raise TestFailed("Couldn't find unknown folder in names '%s'" % names) print "Finished 'invalid ID' tests" ! ############################################################################### # "Failure" tests - execute some tests while provoking the msgstore to simulate *************** *** 555,559 **** msgstore.test_suite_failure = None msgstore.test_suite_failure_request = None ! def _setup_for_mapi_failure(checkpoint, hr): assert msgstore.test_suite_running, "msgstore should already know its running" --- 555,559 ---- msgstore.test_suite_failure = None msgstore.test_suite_failure_request = None ! def _setup_for_mapi_failure(checkpoint, hr): assert msgstore.test_suite_running, "msgstore should already know its running" *************** *** 612,616 **** # SetReadState??? _do_single_failure_spam_test(driver, "MAPIMsgStoreMsg._DoCopyMove", mapi.MAPI_E_TABLE_TOO_BIG) ! finally: manager.verbose = old_verbose --- 612,616 ---- # SetReadState??? _do_single_failure_spam_test(driver, "MAPIMsgStoreMsg._DoCopyMove", mapi.MAPI_E_TABLE_TOO_BIG) ! finally: manager.verbose = old_verbose *************** *** 626,630 **** }, do_failure_tests, manager) ! def filter_message_with_event(msg, mgr, all_actions=True): import filter --- 626,630 ---- }, do_failure_tests, manager) ! def filter_message_with_event(msg, mgr, all_actions=True): import filter *************** *** 642,646 **** filter._original_filter_message = filter.filter_message filter.filter_message = filter_message_with_event ! try: # restore the plugin config at exit. assert not msgstore.test_suite_running, "already running??" --- 642,646 ---- filter._original_filter_message = filter.filter_message filter.filter_message = filter_message_with_event ! try: # restore the plugin config at exit. assert not msgstore.test_suite_running, "already running??" Index: train.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/train.py,v retrieving revision 1.34 retrieving revision 1.35 diff -C2 -d -r1.34 -r1.35 *** train.py 19 Sep 2003 04:03:38 -0000 1.34 --- train.py 16 Dec 2003 05:06:33 -0000 1.35 *************** *** 161,168 **** real_trainer(classifier_data, config, mgr.message_store, progress) ! if progress.stop_requested(): return ! if rebuild: assert mgr.classifier_data is not classifier_data --- 161,168 ---- real_trainer(classifier_data, config, mgr.message_store, progress) ! if progress.stop_requested(): return ! if rebuild: assert mgr.classifier_data is not classifier_data From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:45 2003 Subject: [Spambayes-checkins] spambayes/utilities which_database.py,1.6,1.7 Message-ID: Update of /cvsroot/spambayes/spambayes/utilities In directory sc8-pr-cvs1:/tmp/cvs-serv12349/utilities Modified Files: which_database.py Log Message: Whitespace normalization. Index: which_database.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/utilities/which_database.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** which_database.py 14 Oct 2003 19:24:06 -0000 1.6 --- which_database.py 16 Dec 2003 05:06:34 -0000 1.7 *************** *** 76,81 **** use_dbm = options["Storage", "persistent_use_database"] if not use_dbm: ! print "Your storage %s is a: pickle" % (hammie,) ! return if not os.path.exists(hammie): --- 76,81 ---- use_dbm = options["Storage", "persistent_use_database"] if not use_dbm: ! print "Your storage %s is a: pickle" % (hammie,) ! return if not os.path.exists(hammie): From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:46 2003 Subject: [Spambayes-checkins] spambayes/testtools incremental.py, 1.5, 1.6 mkgraph.py, 1.2, 1.3 regimes.py, 1.2, 1.3 urlslurper.py, 1.5, 1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/testtools In directory sc8-pr-cvs1:/tmp/cvs-serv12349/testtools Modified Files: incremental.py mkgraph.py regimes.py urlslurper.py Log Message: Whitespace normalization. Index: incremental.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/incremental.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** incremental.py 5 Sep 2003 01:15:29 -0000 1.5 --- incremental.py 16 Dec 2003 05:06:34 -0000 1.6 *************** *** 333,339 **** set = int(set[3:]) - 1 isspam = (dir.find('Spam') >= 0) ! msg = msgs.Msg(dir, base) ! for j in range(0, nsets): if which is not None and j != which: --- 333,339 ---- set = int(set[3:]) - 1 isspam = (dir.find('Spam') >= 0) ! msg = msgs.Msg(dir, base) ! for j in range(0, nsets): if which is not None and j != which: *************** *** 342,346 **** sys.stderr.write("%-78s\r" % ("%s : %d" % (base, set))) sys.stderr.flush() ! nham_tested[j].append(tests[j].nham_tested) nham_trained[j].append(tests[j].nham_trained) --- 342,346 ---- sys.stderr.write("%-78s\r" % ("%s : %d" % (base, set))) sys.stderr.flush() ! nham_tested[j].append(tests[j].nham_tested) nham_trained[j].append(tests[j].nham_trained) *************** *** 355,359 **** # tests[j].reset_test_results() rules[j].group_action(j, tests[j]) ! if j != set: guess = tests[j].predict([msg], isspam) --- 355,359 ---- # tests[j].reset_test_results() rules[j].group_action(j, tests[j]) ! if j != set: guess = tests[j].predict([msg], isspam) *************** *** 367,371 **** elif todo == 1: tests[j].train([msg], None) ! oldgroup = group --- 367,371 ---- elif todo == 1: tests[j].train([msg], None) ! oldgroup = group Index: mkgraph.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/mkgraph.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** mkgraph.py 1 Mar 2003 05:13:08 -0000 1.2 --- mkgraph.py 16 Dec 2003 05:06:34 -0000 1.3 *************** *** 72,77 **** print '% linetype=1 linelabel="spam_unsure" markertype=0 linecolor=9' line(nspam_unsure) ! ! if report == "error": print '$ Data=Curve2d' print '% toplabel="%s Error Rates"' % (title) --- 72,77 ---- print '% linetype=1 linelabel="spam_unsure" markertype=0 linecolor=9' line(nspam_unsure) ! ! if report == "error": print '$ Data=Curve2d' print '% toplabel="%s Error Rates"' % (title) *************** *** 171,173 **** if __name__ == "__main__": main() - --- 171,172 ---- Index: regimes.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/regimes.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** regimes.py 8 Mar 2003 00:33:22 -0000 1.2 --- regimes.py 16 Dec 2003 05:06:34 -0000 1.3 *************** *** 109,111 **** self.ham[0].append(msg) return actual - --- 109,110 ---- Index: urlslurper.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/urlslurper.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** urlslurper.py 15 May 2003 00:35:38 -0000 1.5 --- urlslurper.py 16 Dec 2003 05:06:34 -0000 1.6 *************** *** 225,233 **** # Build a new opener without any proxy information. opener = urllib2.build_opener(urllib2.HTTPHandler) ! # install it urllib2.install_opener(opener) ! # read any url cache if os.path.exists(filename): f = file(filename, "r") --- 225,233 ---- # Build a new opener without any proxy information. opener = urllib2.build_opener(urllib2.HTTPHandler) ! # install it urllib2.install_opener(opener) ! # read any url cache if os.path.exists(filename): f = file(filename, "r") From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:54 2003 Subject: [Spambayes-checkins] website/scripts/ht2html LinkFixer.py, 1.3, 1.4 PDOGenerator.py, 1.1.1.1, 1.2 Sidebar.py, 1.3, 1.4 SpamBayesGenerator.py, 1.7, 1.8 Message-ID: Update of /cvsroot/spambayes/website/scripts/ht2html In directory sc8-pr-cvs1:/tmp/cvs-serv12349/website/scripts/ht2html Modified Files: LinkFixer.py PDOGenerator.py Sidebar.py SpamBayesGenerator.py Log Message: Whitespace normalization. Index: LinkFixer.py =================================================================== RCS file: /cvsroot/spambayes/website/scripts/ht2html/LinkFixer.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** LinkFixer.py 11 Nov 2003 22:17:51 -0000 1.3 --- LinkFixer.py 16 Dec 2003 05:06:34 -0000 1.4 *************** *** 40,44 **** elif url[-1] == '/': url = url + 'index.html' ! # normalize the path, kind of the way os.path.normpath() does. # urlparse ought to have something like this built in... --- 40,44 ---- elif url[-1] == '/': url = url + 'index.html' ! # normalize the path, kind of the way os.path.normpath() does. # urlparse ought to have something like this built in... Index: PDOGenerator.py =================================================================== RCS file: /cvsroot/spambayes/website/scripts/ht2html/PDOGenerator.py,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -C2 -d -r1.1.1.1 -r1.2 *** PDOGenerator.py 19 Sep 2002 08:40:55 -0000 1.1.1.1 --- PDOGenerator.py 16 Dec 2003 05:06:34 -0000 1.2 *************** *** 108,112 **** ''' % \ ! self.__d def get_corner_bgcolor(self): --- 108,112 ---- ''' % \ ! self.__d def get_corner_bgcolor(self): *************** *** 115,130 **** # value. Some images may be `bizarre'. See .../pics/backgrounds.py return [ ! '#3399ff', '#6699cc', '#3399ff', '#0066cc', '#3399ff', ! '#0066cc', '#0066cc', '#3399ff', '#3399ff', '#3399ff', ! '#3399ff', '#6699cc', '#3399ff', '#3399ff', '#ffffff', ! '#6699cc', '#0066cc', '#3399ff', '#0066cc', '#3399ff', ! '#6699cc', '#0066cc', '#6699cc', '#3399ff', '#3399ff', ! '#6699cc', '#3399ff', '#3399ff', '#6699cc', '#6699cc', ! '#0066cc', '#6699cc', '#0066cc', '#6699cc', '#0066cc', ! '#0066cc', '#6699cc', '#3399ff', '#0066cc', '#bbd6f1', ! '#0066cc', '#6699cc', '#3399ff', '#3399ff', '#0066cc', ! '#0066cc', '#0066cc', '#6699cc', '#6699cc', '#3399ff', ! '#3399ff', '#6699cc', '#0066cc', '#0066cc', '#6699cc', ! '#0066cc', '#6699cc', '#3399ff', '#6699cc', '#3399ff', '#d6ebff', '#6699cc', '#3399ff', '#0066cc', ][self.__whichbanner] --- 115,130 ---- # value. Some images may be `bizarre'. See .../pics/backgrounds.py return [ ! '#3399ff', '#6699cc', '#3399ff', '#0066cc', '#3399ff', ! '#0066cc', '#0066cc', '#3399ff', '#3399ff', '#3399ff', ! '#3399ff', '#6699cc', '#3399ff', '#3399ff', '#ffffff', ! '#6699cc', '#0066cc', '#3399ff', '#0066cc', '#3399ff', ! '#6699cc', '#0066cc', '#6699cc', '#3399ff', '#3399ff', ! '#6699cc', '#3399ff', '#3399ff', '#6699cc', '#6699cc', ! '#0066cc', '#6699cc', '#0066cc', '#6699cc', '#0066cc', ! '#0066cc', '#6699cc', '#3399ff', '#0066cc', '#bbd6f1', ! '#0066cc', '#6699cc', '#3399ff', '#3399ff', '#0066cc', ! '#0066cc', '#0066cc', '#6699cc', '#6699cc', '#3399ff', ! '#3399ff', '#6699cc', '#0066cc', '#0066cc', '#6699cc', ! '#0066cc', '#6699cc', '#3399ff', '#6699cc', '#3399ff', '#d6ebff', '#6699cc', '#3399ff', '#0066cc', ][self.__whichbanner] Index: Sidebar.py =================================================================== RCS file: /cvsroot/spambayes/website/scripts/ht2html/Sidebar.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** Sidebar.py 5 Jun 2003 15:13:54 -0000 1.3 --- Sidebar.py 16 Dec 2003 05:06:34 -0000 1.4 *************** *** 70,74 **** else: done_one = 1 ! print ''% ( self.getSidebarHeaderAttrs()) print item print '' --- 70,74 ---- else: done_one = 1 ! print ''% ( self.getSidebarHeaderAttrs()) print item print '' Index: SpamBayesGenerator.py =================================================================== RCS file: /cvsroot/spambayes/website/scripts/ht2html/SpamBayesGenerator.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** SpamBayesGenerator.py 26 Aug 2003 07:37:35 -0000 1.7 --- SpamBayesGenerator.py 16 Dec 2003 05:06:34 -0000 1.8 *************** *** 96,100 **** ''' % \ ! self.__d def get_corner_bgcolor(self): --- 96,100 ---- ''' % \ ! self.__d def get_corner_bgcolor(self): From tim_one at users.sourceforge.net Tue Dec 16 00:06:37 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:56 2003 Subject: [Spambayes-checkins] spambayes/windows autoconfigure.py, 1.7, 1.8 pop3proxy_service.py, 1.16, 1.17 pop3proxy_tray.py, 1.17, 1.18 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv12349/windows Modified Files: autoconfigure.py pop3proxy_service.py pop3proxy_tray.py Log Message: Whitespace normalization. Index: autoconfigure.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/autoconfigure.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** autoconfigure.py 6 Oct 2003 03:01:01 -0000 1.7 --- autoconfigure.py 16 Dec 2003 05:06:34 -0000 1.8 *************** *** 90,99 **** 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 --- 90,99 ---- 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 *************** *** 131,135 **** except ConfigParser.NoOptionError: port = None ! if us_name.lower()[:4] == "pop3": if port is None: --- 131,135 ---- except ConfigParser.NoOptionError: port = None ! if us_name.lower()[:4] == "pop3": if port is None: *************** *** 207,211 **** pop_accounts = {} smtp_accounts = {} ! r = re.compile(r"user_pref\(\"mail.server.server(\d+).(real)?hostname\", \"([^\"]*)\"\);") current_pos = 0 --- 207,211 ---- pop_accounts = {} smtp_accounts = {} ! r = re.compile(r"user_pref\(\"mail.server.server(\d+).(real)?hostname\", \"([^\"]*)\"\);") current_pos = 0 *************** *** 244,248 **** account_type = prefs[type_loc : \ type_loc + prefs[type_loc:].index('"')] ! if account_type == "pop3": new_pref = 'user_pref("mail.server.server%s.%shostname", ' \ --- 244,248 ---- account_type = prefs[type_loc : \ type_loc + prefs[type_loc:].index('"')] ! if account_type == "pop3": new_pref = 'user_pref("mail.server.server%s.%shostname", ' \ *************** *** 291,295 **** '"127.0.0.1");' % (server_num,) ! # Find the port port_string = 'user_pref("mail.smtpserver.smtp1.port", ' port_loc = prefs.find(port_string) --- 291,295 ---- '"127.0.0.1");' % (server_num,) ! # Find the port port_string = 'user_pref("mail.smtpserver.smtp1.port", ' port_loc = prefs.find(port_string) *************** *** 387,391 **** except ConfigParser.NoOptionError: port = None ! if us_name.lower()[:4] == "pop3": if port is None: --- 387,391 ---- except ConfigParser.NoOptionError: port = None ! if us_name.lower()[:4] == "pop3": if port is None: *************** *** 425,429 **** # OE stores its configuration in the registry, not a file. key = "Software\\Microsoft\\Internet Account Manager\\Accounts" ! import win32api import win32con --- 425,429 ---- # OE stores its configuration in the registry, not a file. key = "Software\\Microsoft\\Internet Account Manager\\Accounts" ! import win32api import win32con *************** *** 554,665 **** def configure_pocomail(unused): ! import win32api ! import win32con ! ! key = "Software\\Poco Systems Inc" ! ! pop_proxy = pop_proxy_port ! smtp_proxy = smtp_proxy_port ! ! reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, key) ! 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] ! ! pocomail_accounts_file = os.path.join(pocomail_path, "accounts.ini") ! ! 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() --- 554,665 ---- def configure_pocomail(unused): ! import win32api ! import win32con ! ! key = "Software\\Poco Systems Inc" ! ! pop_proxy = pop_proxy_port ! smtp_proxy = smtp_proxy_port ! ! reg = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER, key) ! 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] ! ! pocomail_accounts_file = os.path.join(pocomail_path, "accounts.ini") ! ! 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() Index: pop3proxy_service.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_service.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** pop3proxy_service.py 14 Dec 2003 01:30:49 -0000 1.16 --- pop3proxy_service.py 16 Dec 2003 05:06:34 -0000 1.17 *************** *** 67,71 **** sb_dir = os.path.dirname(os.path.dirname(this_filename)) sb_scripts_dir = os.path.join(sb_dir,"scripts") ! sys.path.insert(0, sb_dir) sys.path.insert(-1, sb_scripts_dir) --- 67,71 ---- sb_dir = os.path.dirname(os.path.dirname(this_filename)) sb_scripts_dir = os.path.join(sb_dir,"scripts") ! sys.path.insert(0, sb_dir) sys.path.insert(-1, sb_scripts_dir) *************** *** 102,106 **** self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stopping.set() ! sb_server.stop() def SvcDoRun(self): --- 102,106 ---- self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stopping.set() ! sb_server.stop() def SvcDoRun(self): *************** *** 113,118 **** "another SpamBayes server is already running on this machine" servicemanager.LogErrorMsg(msg) ! errCode = winerror.ERROR_SERVICE_SPECIFIC_ERROR ! self.ReportServiceStatus(win32service.SERVICE_STOPPED, win32ExitCode=errCode, svcExitCode = 1) return --- 113,118 ---- "another SpamBayes server is already running on this machine" servicemanager.LogErrorMsg(msg) ! errCode = winerror.ERROR_SERVICE_SPECIFIC_ERROR ! self.ReportServiceStatus(win32service.SERVICE_STOPPED, win32ExitCode=errCode, svcExitCode = 1) return *************** *** 123,127 **** thread.start() ! # Write an event log record - in debug mode we will also # see this message printed. from spambayes.Options import optionsPathname --- 123,127 ---- thread.start() ! # Write an event log record - in debug mode we will also # see this message printed. from spambayes.Options import optionsPathname *************** *** 154,158 **** except KeyboardInterrupt: pass ! # Write another event log record. s = sb_server.state --- 154,158 ---- except KeyboardInterrupt: pass ! # Write another event log record. s = sb_server.state Index: pop3proxy_tray.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_tray.py,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** pop3proxy_tray.py 14 Dec 2003 01:30:49 -0000 1.17 --- pop3proxy_tray.py 16 Dec 2003 05:06:34 -0000 1.18 *************** *** 184,188 **** flags = NIF_ICON | NIF_MESSAGE | NIF_TIP ! nid = (self.hwnd, 0, flags, WM_TASKBAR_NOTIFY, self.hstartedicon, "SpamBayes") Shell_NotifyIcon(NIM_ADD, nid) --- 184,188 ---- flags = NIF_ICON | NIF_MESSAGE | NIF_TIP ! nid = (self.hwnd, 0, flags, WM_TASKBAR_NOTIFY, self.hstartedicon, "SpamBayes") Shell_NotifyIcon(NIM_ADD, nid) *************** *** 192,196 **** print "Service not availible. Using thread." self.use_service = False ! # Start up sb_server if not self.started: --- 192,196 ---- print "Service not availible. Using thread." self.use_service = False ! # Start up sb_server if not self.started: *************** *** 213,222 **** tip = "SpamBayes is not running" return tip ! def UpdateIcon(self): ! flags = NIF_TIP | NIF_ICON if self.started: ! hicon = self.hstartedicon else: hicon = self.hstoppedicon --- 213,222 ---- tip = "SpamBayes is not running" return tip ! def UpdateIcon(self): ! flags = NIF_TIP | NIF_ICON if self.started: ! hicon = self.hstartedicon else: hicon = self.hstoppedicon *************** *** 277,284 **** elif dwWaitTime > 10000: dwWaitTime = 10000 ! Sleep(dwWaitTime); ssStatus = QueryServiceStatus(schService) ! if ssStatus[5] > dwOldCheckPoint: dwStartTickCount = GetTickCount() --- 277,284 ---- elif dwWaitTime > 10000: dwWaitTime = 10000 ! Sleep(dwWaitTime); ssStatus = QueryServiceStatus(schService) ! if ssStatus[5] > dwOldCheckPoint: dwStartTickCount = GetTickCount() *************** *** 302,306 **** CloseServiceHandle(schService) return ! ControlService(schService, SERVICE_CONTROL_STOP) --- 302,306 ---- CloseServiceHandle(schService) return ! ControlService(schService, SERVICE_CONTROL_STOP) *************** *** 316,323 **** elif dwWaitTime > 10000: dwWaitTime = 10000 ! Sleep(dwWaitTime); ssStatus = QueryServiceStatus(schService) ! if ssStatus[5] > dwOldCheckPoint: dwStartTickCount = GetTickCount() --- 316,323 ---- elif dwWaitTime > 10000: dwWaitTime = 10000 ! Sleep(dwWaitTime); ssStatus = QueryServiceStatus(schService) ! if ssStatus[5] > dwOldCheckPoint: dwStartTickCount = GetTickCount() *************** *** 329,333 **** CloseServiceHandle(schService) self.started = False ! def OnDestroy(self, hwnd, msg, wparam, lparam): nid = (self.hwnd, 0) --- 329,333 ---- CloseServiceHandle(schService) self.started = False ! def OnDestroy(self, hwnd, msg, wparam, lparam): nid = (self.hwnd, 0) *************** *** 401,405 **** DestroyWindow(self.hwnd) PostQuitMessage(0) ! def _ProxyThread(self): self.started = True --- 401,405 ---- DestroyWindow(self.hwnd) PostQuitMessage(0) ! def _ProxyThread(self): self.started = True *************** *** 440,444 **** self.started = True self.UpdateUIState() ! def Stop(self): self.CheckCurrentState() --- 440,444 ---- self.started = True self.UpdateUIState() ! def Stop(self): self.CheckCurrentState() *************** *** 472,476 **** self.started = IsServerRunningAnywhere() self.UpdateUIState() ! def UpdateUIState(self): if self.started != self.last_started_state: --- 472,476 ---- self.started = IsServerRunningAnywhere() self.UpdateUIState() ! def UpdateUIState(self): if self.started != self.last_started_state: *************** *** 491,495 **** self.ShowMessage("SpamBayes is not running.") ! def OpenConfig(self): if self.started: webbrowser.open_new("http://localhost:%d/config" % \ --- 491,495 ---- self.ShowMessage("SpamBayes is not running.") ! def OpenConfig(self): if self.started: webbrowser.open_new("http://localhost:%d/config" % \ *************** *** 497,502 **** else: self.ShowMessage("SpamBayes is not running.") ! ! def OpenReview(self): if self.started: webbrowser.open_new("http://localhost:%d/review" % \ --- 497,502 ---- else: self.ShowMessage("SpamBayes is not running.") ! ! def OpenReview(self): if self.started: webbrowser.open_new("http://localhost:%d/review" % \ *************** *** 504,508 **** else: self.ShowMessage("SpamBayes is not running.") ! def CheckVersion(self): # Stolen, with few modifications, from addin.py --- 504,508 ---- else: self.ShowMessage("SpamBayes is not running.") ! def CheckVersion(self): # Stolen, with few modifications, from addin.py *************** *** 538,542 **** # Offer to open up the url ## os.startfile(url) ! def ShowMessage(self, msg): MessageBox(self.hwnd, msg, "SpamBayes", win32con.MB_OK) --- 538,542 ---- # Offer to open up the url ## os.startfile(url) ! def ShowMessage(self, msg): MessageBox(self.hwnd, msg, "SpamBayes", win32con.MB_OK) *************** *** 544,550 **** def main(): ! w = MainWindow() ! PumpMessages() if __name__=='__main__': ! main() --- 544,550 ---- def main(): ! w = MainWindow() ! PumpMessages() if __name__=='__main__': ! main() From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:06:58 2003 Subject: [Spambayes-checkins] spambayes/spambayes Corpus.py, 1.11, 1.12 Dibbler.py, 1.10, 1.11 ImapUI.py, 1.29, 1.30 Options.py, 1.91, 1.92 OptionsClass.py, 1.20, 1.21 ProxyUI.py, 1.33, 1.34 ServerUI.py, 1.4, 1.5 UserInterface.py, 1.37, 1.38 dbmstorage.py, 1.10, 1.11 message.py, 1.44, 1.45 oe_mailbox.py, 1.2, 1.3 smtpproxy.py, 1.4, 1.5 storage.py, 1.36, 1.37 tokenizer.py, 1.21, 1.22 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv12349/spambayes Modified Files: Corpus.py Dibbler.py ImapUI.py Options.py OptionsClass.py ProxyUI.py ServerUI.py UserInterface.py dbmstorage.py message.py oe_mailbox.py smtpproxy.py storage.py tokenizer.py Log Message: Whitespace normalization. Index: Corpus.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Corpus.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** Corpus.py 15 Dec 2003 07:06:23 -0000 1.11 --- Corpus.py 16 Dec 2003 05:06:34 -0000 1.12 *************** *** 151,155 **** # This method should probably not be overridden key = message.key() ! if options["globals", "verbose"]: print 'placing %s in corpus cache' % (key) --- 151,155 ---- # This method should probably not be overridden key = message.key() ! if options["globals", "verbose"]: print 'placing %s in corpus cache' % (key) Index: Dibbler.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Dibbler.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** Dibbler.py 11 Dec 2003 18:40:54 -0000 1.10 --- Dibbler.py 16 Dec 2003 05:06:34 -0000 1.11 *************** *** 422,426 **** for line in headers.split('\r\n') if headersRegex.match(line)]) ! # HTTP Basic/Digest Authentication support. serverAuthMode = self._server.requestAuthenticationMode() --- 422,426 ---- for line in headers.split('\r\n') if headersRegex.match(line)]) ! # HTTP Basic/Digest Authentication support. serverAuthMode = self._server.requestAuthenticationMode() *************** *** 474,478 **** if isinstance(dispatcher, Listener): dispatcher.close() ! # Let any existing connections close down first. This # has happened when all we have left are _HTTPHandlers --- 474,478 ---- if isinstance(dispatcher, Listener): dispatcher.close() ! # Let any existing connections close down first. This # has happened when all we have left are _HTTPHandlers *************** *** 482,491 **** def isProtected(dispatcher): return not isinstance(dispatcher, _HTTPHandler) ! while len(filter(isProtected, contextMap.values())) > 0: asyncore.poll(timeout=1, map=contextMap) ! raise SystemExit ! message = """

    500 Server error

    %s
    """ details = traceback.format_exception(eType, eValue, eTrace) --- 482,491 ---- def isProtected(dispatcher): return not isinstance(dispatcher, _HTTPHandler) ! while len(filter(isProtected, contextMap.values())) > 0: asyncore.poll(timeout=1, map=contextMap) ! raise SystemExit ! message = """

    500 Server error

    %s
    """ details = traceback.format_exception(eType, eValue, eTrace) *************** *** 560,564 **** self.writeError(500, "Inconsistent authentication mode.") return ! headers = [] headers.append('HTTP/1.0 401 Unauthorized') --- 560,564 ---- self.writeError(500, "Inconsistent authentication mode.") return ! headers = [] headers.append('HTTP/1.0 401 Unauthorized') *************** *** 586,590 **** def _getCurrentNonce(self): ! """Returns the current nonce value. This value is a Base64 encoding of current time plus 20 minutes. This means the nonce will expire 20 minutes from now.""" --- 586,590 ---- def _getCurrentNonce(self): ! """Returns the current nonce value. This value is a Base64 encoding of current time plus 20 minutes. This means the nonce will expire 20 minutes from now.""" *************** *** 613,617 **** def stripQuotes(s): return (s[0] == '"' and s[-1] == '"') and s[1:-1] or s ! options = dict([s.split('=') for s in login.split(", ")]) userName = stripQuotes(options["username"]) --- 613,617 ---- def stripQuotes(s): return (s[0] == '"' and s[-1] == '"') and s[1:-1] or s ! options = dict([s.split('=') for s in login.split(", ")]) userName = stripQuotes(options["username"]) Index: ImapUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ImapUI.py,v retrieving revision 1.29 retrieving revision 1.30 diff -C2 -d -r1.29 -r1.30 *** ImapUI.py 16 Dec 2003 00:30:49 -0000 1.29 --- ImapUI.py 16 Dec 2003 05:06:34 -0000 1.30 *************** *** 255,259 **** UserInterface.UserInterface.onChangeopts(self, **parms) self.parm_ini_map = backup ! def _buildFolderBox(self, section, option, available_folders): folderTable = self.html.configTable.clone() --- 255,259 ---- UserInterface.UserInterface.onChangeopts(self, **parms) self.parm_ini_map = backup ! def _buildFolderBox(self, section, option, available_folders): folderTable = self.html.configTable.clone() Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.91 retrieving revision 1.92 diff -C2 -d -r1.91 -r1.92 *** Options.py 16 Dec 2003 02:03:30 -0000 1.91 --- Options.py 16 Dec 2003 05:06:34 -0000 1.92 *************** *** 165,171 **** are generated for messages with habeas headers. This should be fine, since messages with the headers should either be ham, or result in FN ! so that we can send them to habeas so they can be sued. However, to ! reduce the strength of habeas headers, we offer the ability to reduce ! the nine tokens to one. (This option has no effect if search_for_habeas_headers is False)""", BOOLEAN, RESTORE), --- 165,171 ---- are generated for messages with habeas headers. This should be fine, since messages with the headers should either be ham, or result in FN ! so that we can send them to habeas so they can be sued. However, to ! reduce the strength of habeas headers, we offer the ability to reduce ! the nine tokens to one. (This option has no effect if search_for_habeas_headers is False)""", BOOLEAN, RESTORE), *************** *** 364,368 **** assigning certainty (0 or 1) to a word that has appeared in only ham or only spam. This is a disaster. ! As unknown_word_strength tends toward infintity, all probabilities tend toward unknown_word_prob. All reports were that a value near 0.4 --- 364,368 ---- assigning certainty (0 or 1) to a word that has appeared in only ham or only spam. This is a disaster. ! As unknown_word_strength tends toward infintity, all probabilities tend toward unknown_word_prob. All reports were that a value near 0.4 *************** *** 975,979 **** PORT, RESTORE), ), ! "globals" : ( ("verbose", "Verbose", False, --- 975,979 ---- PORT, RESTORE), ), ! "globals" : ( ("verbose", "Verbose", False, *************** *** 1037,1041 **** options.merge_files(alts) optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: optionsPathname = os.path.abspath('bayescustomize.ini') --- 1037,1041 ---- options.merge_files(alts) optionsPathname = os.path.abspath(alts[-1]) ! if not optionsPathname: optionsPathname = os.path.abspath('bayescustomize.ini') Index: OptionsClass.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v retrieving revision 1.20 retrieving revision 1.21 diff -C2 -d -r1.20 -r1.21 *** OptionsClass.py 16 Dec 2003 04:48:28 -0000 1.20 --- OptionsClass.py 16 Dec 2003 05:06:34 -0000 1.21 *************** *** 146,150 **** '''Multiple values are allowed for this option.''' return type(self.value) in MultiContainerTypes ! def is_valid(self, value): '''Check if this is a valid value for this option.''' --- 146,150 ---- '''Multiple values are allowed for this option.''' return type(self.value) in MultiContainerTypes ! def is_valid(self, value): '''Check if this is a valid value for this option.''' *************** *** 193,197 **** # either no match or too many matches return False ! def _split_values(self, value): # do the regex mojo here --- 193,197 ---- # either no match or too many matches return False ! def _split_values(self, value): # do the regex mojo here *************** *** 470,474 **** out.close() f.close() ! def _add_missing(self, out, written, sect, vi, label=True): # add any missing ones, where the value does not equal the default --- 470,474 ---- out.close() f.close() ! def _add_missing(self, out, written, sect, vi, label=True): # add any missing ones, where the value does not equal the default *************** *** 503,507 **** except TypeError: # opt[0] not a class pass ! o = klass(*args) self._options[section, o.name] = o --- 503,507 ---- except TypeError: # opt[0] not a class pass ! o = klass(*args) self._options[section, o.name] = o *************** *** 607,611 **** else: print >> sys.stderr, ("Attempted to set [%s] %s with invalid" ! " value %s (%s)" % (sect, opt.lower(), val, type(val))) --- 607,611 ---- else: print >> sys.stderr, ("Attempted to set [%s] %s with invalid" ! " value %s (%s)" % (sect, opt.lower(), val, type(val))) *************** *** 667,671 **** all.sort() return all ! def options_in_section(self, section): '''Return an alphabetical list of all the options in this section.''' --- 667,671 ---- all.sort() return all ! def options_in_section(self, section): '''Return an alphabetical list of all the options in this section.''' *************** *** 707,730 **** def display_full(self, section=None, option=None): ! '''Display options including all information.''' ! # Given that the Options class is no longer as nice looking ! # as it once was, this returns all the information, i.e. ! # the doc, default values, and so on ! output = StringIO.StringIO() ! # when section and option are both specified, this ! # is nothing more than a call to as_nice_string ! if section is not None and option is not None: ! output.write(self._options[section, ! option.lower()].as_nice_string(section)) ! return output.getvalue() ! ! all = self._options.keys() ! all.sort() ! for sect, opt in all: ! if section is not None and sect != section: ! continue ! output.write(self._options[sect, opt.lower()].as_nice_string(sect)) ! return output.getvalue() # These are handy references to commonly used regex/tuples defining --- 707,730 ---- def display_full(self, section=None, option=None): ! '''Display options including all information.''' ! # Given that the Options class is no longer as nice looking ! # as it once was, this returns all the information, i.e. ! # the doc, default values, and so on ! output = StringIO.StringIO() ! # when section and option are both specified, this ! # is nothing more than a call to as_nice_string ! if section is not None and option is not None: ! output.write(self._options[section, ! option.lower()].as_nice_string(section)) ! return output.getvalue() ! ! all = self._options.keys() ! all.sort() ! for sect, opt in all: ! if section is not None and sect != section: ! continue ! output.write(self._options[sect, opt.lower()].as_nice_string(sect)) ! return output.getvalue() # These are handy references to commonly used regex/tuples defining Index: ProxyUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ProxyUI.py,v retrieving revision 1.33 retrieving revision 1.34 diff -C2 -d -r1.33 -r1.34 *** ProxyUI.py 16 Dec 2003 00:30:49 -0000 1.33 --- ProxyUI.py 16 Dec 2003 05:06:34 -0000 1.34 *************** *** 700,704 **** if pmap == adv_map: ! return errmsg # check for equal number of pop3servers and ports --- 700,704 ---- if pmap == adv_map: ! return errmsg # check for equal number of pop3servers and ports Index: ServerUI.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/ServerUI.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** ServerUI.py 15 Dec 2003 00:16:20 -0000 1.4 --- ServerUI.py 16 Dec 2003 05:06:34 -0000 1.5 *************** *** 105,109 **** # we have a few extra checks errmsg = UserInterface.UserInterface.verifyInput(self, parms) ! # check for equal number of pop3servers and ports slist = list(parms['pop3proxy_remote_servers']) --- 105,109 ---- # we have a few extra checks errmsg = UserInterface.UserInterface.verifyInput(self, parms) ! # check for equal number of pop3servers and ports slist = list(parms['pop3proxy_remote_servers']) Index: UserInterface.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/UserInterface.py,v retrieving revision 1.37 retrieving revision 1.38 diff -C2 -d -r1.37 -r1.38 *** UserInterface.py 16 Dec 2003 02:46:42 -0000 1.37 --- UserInterface.py 16 Dec 2003 05:06:34 -0000 1.38 *************** *** 130,139 **** if trustedIPs == "*" or remoteIP == clientSocket.getsockname()[0]: ! return True trustedIPs = trustedIPs.replace('.', '\.').replace('*', '([01]?\d\d?|2[04]\d|25[0-5])') for trusted in trustedIPs.split(','): ! if re.search("^" + trusted + "$", remoteIP): ! return True return False --- 130,139 ---- if trustedIPs == "*" or remoteIP == clientSocket.getsockname()[0]: ! return True trustedIPs = trustedIPs.replace('.', '\.').replace('*', '([01]?\d\d?|2[04]\d|25[0-5])') for trusted in trustedIPs.split(','): ! if re.search("^" + trusted + "$", remoteIP): ! return True return False *************** *** 299,303 **** nham, nspam) return cluesTable ! def _buildCluesTable(self, message, subject=None, show_tokens=False): tokens = list(tokenizer.tokenize(message)) --- 299,303 ---- nham, nspam) return cluesTable ! def _buildCluesTable(self, message, subject=None, show_tokens=False): tokens = list(tokenizer.tokenize(message)) *************** *** 477,481 **** # Attempt to convert the content from a DBX file to a standard mbox if file: ! content = self._convertToMbox(content) # Convert platform-specific line endings into unix-style. --- 477,481 ---- # Attempt to convert the content from a DBX file to a standard mbox if file: ! content = self._convertToMbox(content) # Convert platform-specific line endings into unix-style. *************** *** 513,517 **** """Check if the given buffer is in a non-mbox format, and convert it into mbox format if so. If it's already an mbox, return it unchanged. ! Currently, the only supported non-mbox format is Outlook Express DBX. In such a case we use the module oe_mailbox to convert the DBX --- 513,517 ---- """Check if the given buffer is in a non-mbox format, and convert it into mbox format if so. If it's already an mbox, return it unchanged. ! Currently, the only supported non-mbox format is Outlook Express DBX. In such a case we use the module oe_mailbox to convert the DBX *************** *** 527,536 **** fh_entries = oe_mailbox.dbxFileHeader.FH_ENTRIES fh_ptr = oe_mailbox.dbxFileHeader.FH_TREE_ROOT_NODE_PTR ! info = oe_mailbox.dbxFileInfo(dbxStream, header.getEntry(file_info_len)) entries = header.getEntry(fh_entries) address = header.getEntry(fh_ptr) ! if address and entries: tree = oe_mailbox.dbxTree(dbxStream, address, entries) --- 527,536 ---- fh_entries = oe_mailbox.dbxFileHeader.FH_ENTRIES fh_ptr = oe_mailbox.dbxFileHeader.FH_TREE_ROOT_NODE_PTR ! info = oe_mailbox.dbxFileInfo(dbxStream, header.getEntry(file_info_len)) entries = header.getEntry(fh_entries) address = header.getEntry(fh_ptr) ! if address and entries: tree = oe_mailbox.dbxTree(dbxStream, address, entries) *************** *** 863,867 **** if options.multiple_values_allowed(sect, opt) and \ value == "": ! value = () value = options.convert(sect, opt, value) if not options.is_valid(sect, opt, value): --- 863,867 ---- if options.multiple_values_allowed(sect, opt) and \ value == "": ! value = () value = options.convert(sect, opt, value) if not options.is_valid(sect, opt, value): Index: dbmstorage.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/dbmstorage.py,v retrieving revision 1.10 retrieving revision 1.11 diff -C2 -d -r1.10 -r1.11 *** dbmstorage.py 26 Nov 2003 01:13:02 -0000 1.10 --- dbmstorage.py 16 Dec 2003 05:06:34 -0000 1.11 *************** *** 15,19 **** def open_dbhash(*args): ! """Open a bsddb hash. Don't use this on Windows, unless Python 2.3 or greater is used, in which case bsddb3 is actually named bsddb.""" import bsddb --- 15,19 ---- def open_dbhash(*args): ! """Open a bsddb hash. Don't use this on Windows, unless Python 2.3 or greater is used, in which case bsddb3 is actually named bsddb.""" import bsddb Index: message.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/message.py,v retrieving revision 1.44 retrieving revision 1.45 diff -C2 -d -r1.44 -r1.45 *** message.py 11 Dec 2003 18:45:31 -0000 1.44 --- message.py 16 Dec 2003 05:06:34 -0000 1.45 *************** *** 164,168 **** self.load() ! def load(self): try: self.dbm = dbmstorage.open(self.db_name, self.mode) --- 164,168 ---- self.load() ! def load(self): try: self.dbm = dbmstorage.open(self.db_name, self.mode) *************** *** 179,183 **** self.close() ! def close(self): # Close our underlying database. Better not assume all databases # have close functions! --- 179,183 ---- self.close() ! def close(self): # Close our underlying database. Better not assume all databases # have close functions! Index: oe_mailbox.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/oe_mailbox.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** oe_mailbox.py 11 Dec 2003 18:45:46 -0000 1.2 --- oe_mailbox.py 16 Dec 2003 05:06:34 -0000 1.3 *************** *** 16,118 **** ########################################################################### class dbxFileHeader: ! """ ! Each Outlook Express DBX file has a file header. ! This header defines many properties, only a few of which interest us. ! The only properties which are required are defined by indexes. The ! indexes are static attributes of the class and their names begin with ! "fh". You can access their values through the method getEntry(). ! """ ! HEADER_SIZE = 0x24bc # total header size ! HEADER_ENTRIES = HEADER_SIZE >> 2 # total of entries in the header ! MAGIC_NUMBER = 0xfe12adcfL # specific to DBX files ! OFFLINE = 0x26fe9d30L # specific to offline.dbx ! FOLDERS = 0x6f74fdc6L # specific to folders.dbx ! POP3UIDL = 0x6f74fdc7L # specific to pop3uidl.dbx ! # various entries indexes ! FH_FILE_INFO_LENGTH = 0x07 # file info length ! FH_FIRST_FOLDER_LIST_NODE = 0x1b # pointer to the first folder list node ! FH_LAST_FOLDER_LIST_NODE = 0x1c # pointer to the last folder list node ! FH_MESSAGE_CONDITIONS_PTR = 0x22 # pointer to the message conditions object ! FH_FOLDER_CONDITIONS_PTR = 0x23 # pointer to the folder conditions object ! FH_ENTRIES = 0x31 # entries in tree ! FH_TREE_ROOT_NODE_PTR = 0x39 # pointer to the root node of a tree ! FILE_HEADER_ENTRIES = \ ! [ ( 0x07, "file info length" ), ! ( 0x09, "pointer to the last variable segment" ), ! ( 0x0a, "length of a variable segment" ), ! ( 0x0b, "used space of the last variable segment" ), ! ( 0x0c, "pointer to the last tree segment" ), ! ( 0x0d, "length of a tree segment" ), ! ( 0x0e, "used space of the last tree segment" ), ! ( 0x0f, "pointer to the last message segment" ), ! ( 0x10, "length of a message segment" ), ! ( 0x11, "used space of the last message segment" ), ! ( 0x12, "root pointer to the deleted message list" ), ! ( 0x13, "root pointer to the deleted tree list" ), ! ( 0x15, "used space in the middle sector of the file" ), ! ( 0x16, "reusable space in the middle sector of the file" ), ! ( 0x17, "index of the last entry in the tree" ), ! ( 0x1b, "pointer to the first folder list node" ), ! ( 0x1c, "pointer to the last folder list node" ), ! ( 0x1f, "used space of the file" ), ! ( 0x22, "pointer to the message conditions object" ), ! ( 0x23, "pointer to the folder conditions object" ), ! ( 0x31, "entries in the tree" ), ! ( 0x32, "entries in the 2.nd tree" ), ! ( 0x33, "entries in the 3.rd tree" ), ! ( 0x39, "pointer to the root node of the tree" ), ! ( 0x3a, "pointer to the root node of the 2.nd tree" ), ! ( 0x3b, "pointer to the root node of the 3.rd tree" ), ! ( 0x9f, "used space for indexed info objects" ), ! ( 0xa0, "used space for conditions objects" ), ! ( 0xa2, "used space for folder list objects" ), ! ( 0xa3, "used space for tree objects" ), ! ( 0xa4, "used space for message objects" )] ! def __init__(self, dbxStream): ! """Initialize the DBX header by reading it directly from the passed ! stream.""" ! dbxStream.seek(0) ! self.dbxBuffer = dbxStream.read(dbxFileHeader.HEADER_SIZE) ! def isMessages(self): ! """Return true iff the DBX is a messages DBX.""" ! return not (self.isFolders() or self.isPOP3UIDL() or self.isOffline()) ! def isFolders(self): ! """Return true if the DBX is the folders DBX.""" ! return self.getEntry(1) == dbxFileHeader.FOLDERS ! def isPOP3UIDL(self): ! """Return true if the DBX is the POP3UIDL DBX.""" ! return self.getEntry(1) == dbxFileHeader.POP3UIDL ! def isOffline(self): ! """Return true if the DBX is the offline DBX.""" ! return self.getEntry(1) == dbxFileHeader.OFFLINE ! def isValid(self): ! """Return true if the DBX is a valid DBX file.""" ! return self.getEntry(0) == dbxFileHeader.MAGIC_NUMBER ! def getHeaderBuffer(self): ! """Return the bytes buffer containing the whole header.""" ! return self.dbxBuffer ! def getEntry(self, dbxEntry): ! """Return the n-th entry as a long integer.""" ! return struct.unpack("L", ! self.dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def getEntryAsHexStr(self, dbxEntry): ! """Return the n-th entry as an hexadecimal string. ! (Little endian encoding!)""" ! return '0x' + \ ! binascii.hexlify(self.dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4]) ########################################################################### --- 16,118 ---- ########################################################################### class dbxFileHeader: ! """ ! Each Outlook Express DBX file has a file header. ! This header defines many properties, only a few of which interest us. ! The only properties which are required are defined by indexes. The ! indexes are static attributes of the class and their names begin with ! "fh". You can access their values through the method getEntry(). ! """ ! HEADER_SIZE = 0x24bc # total header size ! HEADER_ENTRIES = HEADER_SIZE >> 2 # total of entries in the header ! MAGIC_NUMBER = 0xfe12adcfL # specific to DBX files ! OFFLINE = 0x26fe9d30L # specific to offline.dbx ! FOLDERS = 0x6f74fdc6L # specific to folders.dbx ! POP3UIDL = 0x6f74fdc7L # specific to pop3uidl.dbx ! # various entries indexes ! FH_FILE_INFO_LENGTH = 0x07 # file info length ! FH_FIRST_FOLDER_LIST_NODE = 0x1b # pointer to the first folder list node ! FH_LAST_FOLDER_LIST_NODE = 0x1c # pointer to the last folder list node ! FH_MESSAGE_CONDITIONS_PTR = 0x22 # pointer to the message conditions object ! FH_FOLDER_CONDITIONS_PTR = 0x23 # pointer to the folder conditions object ! FH_ENTRIES = 0x31 # entries in tree ! FH_TREE_ROOT_NODE_PTR = 0x39 # pointer to the root node of a tree ! FILE_HEADER_ENTRIES = \ ! [ ( 0x07, "file info length" ), ! ( 0x09, "pointer to the last variable segment" ), ! ( 0x0a, "length of a variable segment" ), ! ( 0x0b, "used space of the last variable segment" ), ! ( 0x0c, "pointer to the last tree segment" ), ! ( 0x0d, "length of a tree segment" ), ! ( 0x0e, "used space of the last tree segment" ), ! ( 0x0f, "pointer to the last message segment" ), ! ( 0x10, "length of a message segment" ), ! ( 0x11, "used space of the last message segment" ), ! ( 0x12, "root pointer to the deleted message list" ), ! ( 0x13, "root pointer to the deleted tree list" ), ! ( 0x15, "used space in the middle sector of the file" ), ! ( 0x16, "reusable space in the middle sector of the file" ), ! ( 0x17, "index of the last entry in the tree" ), ! ( 0x1b, "pointer to the first folder list node" ), ! ( 0x1c, "pointer to the last folder list node" ), ! ( 0x1f, "used space of the file" ), ! ( 0x22, "pointer to the message conditions object" ), ! ( 0x23, "pointer to the folder conditions object" ), ! ( 0x31, "entries in the tree" ), ! ( 0x32, "entries in the 2.nd tree" ), ! ( 0x33, "entries in the 3.rd tree" ), ! ( 0x39, "pointer to the root node of the tree" ), ! ( 0x3a, "pointer to the root node of the 2.nd tree" ), ! ( 0x3b, "pointer to the root node of the 3.rd tree" ), ! ( 0x9f, "used space for indexed info objects" ), ! ( 0xa0, "used space for conditions objects" ), ! ( 0xa2, "used space for folder list objects" ), ! ( 0xa3, "used space for tree objects" ), ! ( 0xa4, "used space for message objects" )] ! def __init__(self, dbxStream): ! """Initialize the DBX header by reading it directly from the passed ! stream.""" ! dbxStream.seek(0) ! self.dbxBuffer = dbxStream.read(dbxFileHeader.HEADER_SIZE) ! def isMessages(self): ! """Return true iff the DBX is a messages DBX.""" ! return not (self.isFolders() or self.isPOP3UIDL() or self.isOffline()) ! def isFolders(self): ! """Return true if the DBX is the folders DBX.""" ! return self.getEntry(1) == dbxFileHeader.FOLDERS ! def isPOP3UIDL(self): ! """Return true if the DBX is the POP3UIDL DBX.""" ! return self.getEntry(1) == dbxFileHeader.POP3UIDL ! def isOffline(self): ! """Return true if the DBX is the offline DBX.""" ! return self.getEntry(1) == dbxFileHeader.OFFLINE ! def isValid(self): ! """Return true if the DBX is a valid DBX file.""" ! return self.getEntry(0) == dbxFileHeader.MAGIC_NUMBER ! def getHeaderBuffer(self): ! """Return the bytes buffer containing the whole header.""" ! return self.dbxBuffer ! def getEntry(self, dbxEntry): ! """Return the n-th entry as a long integer.""" ! return struct.unpack("L", ! self.dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def getEntryAsHexStr(self, dbxEntry): ! """Return the n-th entry as an hexadecimal string. ! (Little endian encoding!)""" ! return '0x' + \ ! binascii.hexlify(self.dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4]) ########################################################################### *************** *** 121,155 **** class dbxFileInfo: ! """ ! Following the DBX header there is DBX info. This part gives the name of ! the folder described by the current DBX. ! """ ! MESSAGE_FILE_INFO = 0x618 ! def __init__(self, dbxStream, dbxLength): ! """Reads the DBX info part from a DBX stream.""" ! dbxStream.seek(dbxFileHeader.HEADER_SIZE) ! self.dbxLength = dbxLength ! self.dbxBuffer = dbxStream.read(dbxLength) ! def isFoldersInfo(self): ! """Return true if the info belongs to folders.dbx.""" ! return self.dbxLength != dbxFileInfo.MESSAGE_FILE_INFO ! def getFolderName(self): ! """Returns the folder name.""" ! if not self.isFoldersInfo(): ! name = [c for c in self.dbxBuffer[0x105:0x210] if ord(c) != 0] ! return "".join(name) ! else: ! return None ! def getCreationTime(self): ! """Not implemented yet.""" ! if self.isFoldersInfo(): ! return "Not implemented yet" ! else: ! return None ########################################################################### --- 121,155 ---- class dbxFileInfo: ! """ ! Following the DBX header there is DBX info. This part gives the name of ! the folder described by the current DBX. ! """ ! MESSAGE_FILE_INFO = 0x618 ! def __init__(self, dbxStream, dbxLength): ! """Reads the DBX info part from a DBX stream.""" ! dbxStream.seek(dbxFileHeader.HEADER_SIZE) ! self.dbxLength = dbxLength ! self.dbxBuffer = dbxStream.read(dbxLength) ! def isFoldersInfo(self): ! """Return true if the info belongs to folders.dbx.""" ! return self.dbxLength != dbxFileInfo.MESSAGE_FILE_INFO ! def getFolderName(self): ! """Returns the folder name.""" ! if not self.isFoldersInfo(): ! name = [c for c in self.dbxBuffer[0x105:0x210] if ord(c) != 0] ! return "".join(name) ! else: ! return None ! def getCreationTime(self): ! """Not implemented yet.""" ! if self.isFoldersInfo(): ! return "Not implemented yet" ! else: ! return None ########################################################################### *************** *** 158,204 **** class dbxTree: ! """Stands for the tree which stores the messages in a given folder.""" ! TREE_NODE_SIZE = 0x27c # size of a tree node ! def __init__(self, dbxStream, dbxAddress, dbxValues): ! """Reads the addresses of the stored messages.""" ! self.dbxValues = [i for i in range(dbxValues)] ! # XXX : silly fix ! ! if dbxAddress > 0: ! self.__readValues(dbxStream, 0, dbxAddress, 0, dbxValues) ! def __readValues(self, dbxStream, dbxParent, dbxAddress, dbxPosition, dbxValues): ! dbxStream.seek(dbxAddress) ! dbxBuffer = dbxStream.read(dbxTree.TREE_NODE_SIZE) ! count = 0 ! entries = ((self.getEntry(dbxBuffer, 4) >> 8) & 0xff) ! if self.getEntry(dbxBuffer, 2) != 0: ! self.__readValues(dbxStream, dbxAddress, self.getEntry(dbxBuffer, 2), ! dbxPosition, self.getEntry(dbxBuffer, 5)) ! count += self.getEntry(dbxBuffer, 5) ! for i in range(entries): ! pos = 6 + i * 3 ! if self.getEntry(dbxBuffer, pos) != 0: ! count += 1 ! value = dbxPosition + count ! self.dbxValues[value - 1] = self.getEntry(dbxBuffer, pos) ! if self.getEntry(dbxBuffer, pos + 1) != 0: ! self.__readValues(dbxStream, dbxAddress, self.getEntry(dbxBuffer, pos + 1), ! dbxPosition + count, self.getEntry(dbxBuffer, pos + 2)) ! count += self.getEntry(dbxBuffer, pos + 2) ! def getEntry(self, dbxBuffer, dbxEntry): ! """Return the n-th entry as a long integer.""" ! return struct.unpack("L", dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def getValue(self, dbxIndex): ! """Return the address of the n-th message.""" ! return self.dbxValues[dbxIndex] ########################################################################### --- 158,204 ---- class dbxTree: ! """Stands for the tree which stores the messages in a given folder.""" ! TREE_NODE_SIZE = 0x27c # size of a tree node ! def __init__(self, dbxStream, dbxAddress, dbxValues): ! """Reads the addresses of the stored messages.""" ! self.dbxValues = [i for i in range(dbxValues)] ! # XXX : silly fix ! ! if dbxAddress > 0: ! self.__readValues(dbxStream, 0, dbxAddress, 0, dbxValues) ! def __readValues(self, dbxStream, dbxParent, dbxAddress, dbxPosition, dbxValues): ! dbxStream.seek(dbxAddress) ! dbxBuffer = dbxStream.read(dbxTree.TREE_NODE_SIZE) ! count = 0 ! entries = ((self.getEntry(dbxBuffer, 4) >> 8) & 0xff) ! if self.getEntry(dbxBuffer, 2) != 0: ! self.__readValues(dbxStream, dbxAddress, self.getEntry(dbxBuffer, 2), ! dbxPosition, self.getEntry(dbxBuffer, 5)) ! count += self.getEntry(dbxBuffer, 5) ! for i in range(entries): ! pos = 6 + i * 3 ! if self.getEntry(dbxBuffer, pos) != 0: ! count += 1 ! value = dbxPosition + count ! self.dbxValues[value - 1] = self.getEntry(dbxBuffer, pos) ! if self.getEntry(dbxBuffer, pos + 1) != 0: ! self.__readValues(dbxStream, dbxAddress, self.getEntry(dbxBuffer, pos + 1), ! dbxPosition + count, self.getEntry(dbxBuffer, pos + 2)) ! count += self.getEntry(dbxBuffer, pos + 2) ! def getEntry(self, dbxBuffer, dbxEntry): ! """Return the n-th entry as a long integer.""" ! return struct.unpack("L", dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def getValue(self, dbxIndex): ! """Return the address of the n-th message.""" ! return self.dbxValues[dbxIndex] ########################################################################### *************** *** 207,324 **** class dbxIndexedInfo: ! """ ! Messages and folders mailboxes contain the "message info" and "folders ! info" entities. ! These entities are indexed info sequences. This is their base class. ! """ ! MAX_INDEX = 0x20 # max index ! DT_NONE = 0 # data type none ! def __init__(self, dbxStream, dbxAddress): ! """Reads the indexed infos from the passed stream.""" ! self.dbxBodyLength = 0L ! self.dbxObjectLength = 0L ! self.dbxEntries = 0L ! self.dbxCounter = 0L ! self.dbxBuffer = [] ! self.dbxIndexes = 0L ! self.dbxBegin = [0L for i in range(dbxIndexedInfo.MAX_INDEX)] ! self.dbxLength = [i for i in self.dbxBegin] ! self.dbxAddress = dbxAddress ! self.__readIndexedInfo(dbxStream) ! def __readIndexedInfo(self, dbxStream): ! dbxStream.seek(self.dbxAddress) ! temp = dbxStream.read(12) ! self.dbxBodyLength = self.__getEntry(temp, 1) ! self.dbxObjectLength = self.__getEntry(temp, 2) & 0xffff ! self.dbxEntries = (self.__getEntry(temp, 2) >> 16) & 0xff ! self.dbxCounter = (self.__getEntry(temp, 1) >> 24) & 0xff ! self.dbxBuffer = dbxStream.read(self.dbxBodyLength) # bytes array ! isIndirect = bool(0) # boolean ! lastIndirect = 0 ! data = self.dbxEntries << 2 # index within dbxBuffer ! for i in range(self.dbxEntries): ! value = self.__getEntry(self.dbxBuffer, i) ! isDirect = value & 0x80 ! index = value & 0x7f ! value >>= 8 ! if isDirect: ! self.__setIndex(index, (i << 2) + 1, 3) ! else: ! self.__setIndex(index, data + value) ! if isIndirect: ! self.__setEnd(lastIndirect, data + value) ! isIndirect = bool(1) ! lastIndirect = index ! self.dbxIndexes |= 1 << index ! if isIndirect: ! self.__setEnd(lastIndirect, self.dbxBodyLength) ! def __setIndex(self, dbxIndex, dbxBegin, dbxLength = 0): ! if dbxIndex < dbxIndexedInfo.MAX_INDEX: ! self.dbxBegin[dbxIndex] = dbxBegin ! self.dbxLength[dbxIndex] = dbxLength ! def __setEnd(self, dbxIndex, dbxEnd): ! if dbxIndex < dbxIndexedInfo.MAX_INDEX: ! self.dbxLength[dbxIndex] = dbxEnd - self.dbxBegin[dbxIndex] ! def __getEntry(self, dbxBuffer, dbxEntry): ! return struct.unpack("L", dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def getIndexText(self, dbxIndex): ! """Returns the description of the given indexed field.""" ! return "" ! def getIndexDataType(self, dbxIndex): ! """Returns the data type of the given index.""" ! return DT_NONE ! def getValue(self, dbxIndex): ! """Returns a tuple : (index in buffer of the info, length of the info).""" ! return (self.dbxBegin[dbxIndex], self.dbxLength[dbxIndex]) ! def getValueAsLong(self, dbxIndex): ! """Returns the indexed info as a long value.""" ! data, length = self.getValue(dbxIndex) ! value = 0 ! if data: ! value = struct.unpack("L", self.dbxBuffer[data:data + 4])[0] ! if length < 4: ! value &= (1 << (length << 3)) - 1 ! return value ! def getString(self, dbxIndex): ! """Returns the indexed info as a string value.""" ! index = self.dbxBegin[dbxIndex] ! end = index ! for c in self.dbxBuffer[index:]: ! if ord(c) == 0: break ! end += 1 ! return self.dbxBuffer[index:end] ! def getAddress(self): ! return self.dbxAddress ! def getBodyLength(self): ! return self.dbxBodyLength ! def getEntries(self): ! return self.dbxEntries ! def getCounter(self): ! return self.dbxCounter ! def getIndexes(self): ! return self.dbxIndexes ! def isIndexed(self, dbxIndex): ! return self.dbxIndexes & (1 << dbxIndex) ########################################################################### --- 207,324 ---- class dbxIndexedInfo: ! """ ! Messages and folders mailboxes contain the "message info" and "folders ! info" entities. ! These entities are indexed info sequences. This is their base class. ! """ ! MAX_INDEX = 0x20 # max index ! DT_NONE = 0 # data type none ! def __init__(self, dbxStream, dbxAddress): ! """Reads the indexed infos from the passed stream.""" ! self.dbxBodyLength = 0L ! self.dbxObjectLength = 0L ! self.dbxEntries = 0L ! self.dbxCounter = 0L ! self.dbxBuffer = [] ! self.dbxIndexes = 0L ! self.dbxBegin = [0L for i in range(dbxIndexedInfo.MAX_INDEX)] ! self.dbxLength = [i for i in self.dbxBegin] ! self.dbxAddress = dbxAddress ! self.__readIndexedInfo(dbxStream) ! def __readIndexedInfo(self, dbxStream): ! dbxStream.seek(self.dbxAddress) ! temp = dbxStream.read(12) ! self.dbxBodyLength = self.__getEntry(temp, 1) ! self.dbxObjectLength = self.__getEntry(temp, 2) & 0xffff ! self.dbxEntries = (self.__getEntry(temp, 2) >> 16) & 0xff ! self.dbxCounter = (self.__getEntry(temp, 1) >> 24) & 0xff ! self.dbxBuffer = dbxStream.read(self.dbxBodyLength) # bytes array ! isIndirect = bool(0) # boolean ! lastIndirect = 0 ! data = self.dbxEntries << 2 # index within dbxBuffer ! for i in range(self.dbxEntries): ! value = self.__getEntry(self.dbxBuffer, i) ! isDirect = value & 0x80 ! index = value & 0x7f ! value >>= 8 ! if isDirect: ! self.__setIndex(index, (i << 2) + 1, 3) ! else: ! self.__setIndex(index, data + value) ! if isIndirect: ! self.__setEnd(lastIndirect, data + value) ! isIndirect = bool(1) ! lastIndirect = index ! self.dbxIndexes |= 1 << index ! if isIndirect: ! self.__setEnd(lastIndirect, self.dbxBodyLength) ! def __setIndex(self, dbxIndex, dbxBegin, dbxLength = 0): ! if dbxIndex < dbxIndexedInfo.MAX_INDEX: ! self.dbxBegin[dbxIndex] = dbxBegin ! self.dbxLength[dbxIndex] = dbxLength ! def __setEnd(self, dbxIndex, dbxEnd): ! if dbxIndex < dbxIndexedInfo.MAX_INDEX: ! self.dbxLength[dbxIndex] = dbxEnd - self.dbxBegin[dbxIndex] ! def __getEntry(self, dbxBuffer, dbxEntry): ! return struct.unpack("L", dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def getIndexText(self, dbxIndex): ! """Returns the description of the given indexed field.""" ! return "" ! def getIndexDataType(self, dbxIndex): ! """Returns the data type of the given index.""" ! return DT_NONE ! def getValue(self, dbxIndex): ! """Returns a tuple : (index in buffer of the info, length of the info).""" ! return (self.dbxBegin[dbxIndex], self.dbxLength[dbxIndex]) ! def getValueAsLong(self, dbxIndex): ! """Returns the indexed info as a long value.""" ! data, length = self.getValue(dbxIndex) ! value = 0 ! if data: ! value = struct.unpack("L", self.dbxBuffer[data:data + 4])[0] ! if length < 4: ! value &= (1 << (length << 3)) - 1 ! return value ! def getString(self, dbxIndex): ! """Returns the indexed info as a string value.""" ! index = self.dbxBegin[dbxIndex] ! end = index ! for c in self.dbxBuffer[index:]: ! if ord(c) == 0: break ! end += 1 ! return self.dbxBuffer[index:end] ! def getAddress(self): ! return self.dbxAddress ! def getBodyLength(self): ! return self.dbxBodyLength ! def getEntries(self): ! return self.dbxEntries ! def getCounter(self): ! return self.dbxCounter ! def getIndexes(self): ! return self.dbxIndexes ! def isIndexed(self, dbxIndex): ! return self.dbxIndexes & (1 << dbxIndex) ########################################################################### *************** *** 327,377 **** class dbxMessageInfo(dbxIndexedInfo): ! """ ! The message info structure inherits from the index info one. It just ! defines extra constants which allow to access pertinent info. ! """ ! MI_INDEX = 0x0 # index of the message ! MI_FLAGS = 0x1 # the message flags ! MI_MESSAGE_ADDRESS = 0x4 # the address of the message ! MI_SUBJECT = 0x8 # the subject of the message ! # label of each indexed info ! INDEX_LABEL = \ ! [ "message index" , "flags" , ! "time message created/send" , "body lines" , ! "message address" , "original subject" , ! "time message saved" , "message id" , ! "subject" , "sender eMail address and name" , ! "answered to message id" , "server/newsgroup/message number", ! "server" , "sender name" , ! "sender eMail address" , "id 0f" , ! "message priority" , "message text length" , ! "time message created/received", "receiver name" , ! "receiver eMail address" , "id 15" , ! "id 16" , "id 17" , ! "id 18" , "id 19" , ! "OE account name" , "OE account registry key" , ! "message text structure" , "id 1d" , ! "id 1e" , "id 1f" ] ! DT_NONE = 0 # index is none ! DT_INT4 = 1 # index is a long integer (32 bits) ! DT_STRING = 2 # index is a string ! DT_DATE_TIME = 3 # index is date/time ! DT_DATA = 4 # index is data ! # the data type of each index ! INDEX_DATA_TYPE = \ ! [ DT_INT4 , DT_INT4 , DT_DATE_TIME, DT_INT4 , DT_INT4 , DT_STRING, DT_DATE_TIME, DT_STRING, ! DT_STRING, DT_STRING, DT_STRING , DT_STRING, DT_STRING, DT_STRING, DT_STRING , DT_NONE , ! DT_INT4 , DT_INT4 , DT_DATE_TIME, DT_STRING, DT_STRING, DT_NONE , DT_INT4 , DT_NONE , ! DT_INT4 , DT_INT4 , DT_STRING , DT_STRING, DT_DATA , DT_NONE , DT_NONE , DT_NONE ] ! def getIndexText(self, dbxIndex): ! return dbxMessageInfo.INDEX_LABEL[dbxIndex] ! def getIndexDataType(self, dbxIndex): ! return dbxMessageInfo.INDEX_DATA_TYPE[dbxIndex] ########################################################################### --- 327,377 ---- class dbxMessageInfo(dbxIndexedInfo): ! """ ! The message info structure inherits from the index info one. It just ! defines extra constants which allow to access pertinent info. ! """ ! MI_INDEX = 0x0 # index of the message ! MI_FLAGS = 0x1 # the message flags ! MI_MESSAGE_ADDRESS = 0x4 # the address of the message ! MI_SUBJECT = 0x8 # the subject of the message ! # label of each indexed info ! INDEX_LABEL = \ ! [ "message index" , "flags" , ! "time message created/send" , "body lines" , ! "message address" , "original subject" , ! "time message saved" , "message id" , ! "subject" , "sender eMail address and name" , ! "answered to message id" , "server/newsgroup/message number", ! "server" , "sender name" , ! "sender eMail address" , "id 0f" , ! "message priority" , "message text length" , ! "time message created/received", "receiver name" , ! "receiver eMail address" , "id 15" , ! "id 16" , "id 17" , ! "id 18" , "id 19" , ! "OE account name" , "OE account registry key" , ! "message text structure" , "id 1d" , ! "id 1e" , "id 1f" ] ! DT_NONE = 0 # index is none ! DT_INT4 = 1 # index is a long integer (32 bits) ! DT_STRING = 2 # index is a string ! DT_DATE_TIME = 3 # index is date/time ! DT_DATA = 4 # index is data ! # the data type of each index ! INDEX_DATA_TYPE = \ ! [ DT_INT4 , DT_INT4 , DT_DATE_TIME, DT_INT4 , DT_INT4 , DT_STRING, DT_DATE_TIME, DT_STRING, ! DT_STRING, DT_STRING, DT_STRING , DT_STRING, DT_STRING, DT_STRING, DT_STRING , DT_NONE , ! DT_INT4 , DT_INT4 , DT_DATE_TIME, DT_STRING, DT_STRING, DT_NONE , DT_INT4 , DT_NONE , ! DT_INT4 , DT_INT4 , DT_STRING , DT_STRING, DT_DATA , DT_NONE , DT_NONE , DT_NONE ] ! def getIndexText(self, dbxIndex): ! return dbxMessageInfo.INDEX_LABEL[dbxIndex] ! def getIndexDataType(self, dbxIndex): ! return dbxMessageInfo.INDEX_DATA_TYPE[dbxIndex] ########################################################################### *************** *** 380,418 **** class dbxMessage: ! def __init__(self, dbxStream, dbxAddress): ! self.dbxAddress = dbxAddress ! self.dbxText = "" ! self.dbxLength = 0L ! self.__readMessageText(dbxStream) ! def __getEntry(self, dbxBuffer, dbxEntry): ! if len(dbxBuffer) < (dbxEntry * 4) + 4: ! return None ! return struct.unpack("L", dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def __readMessageText(self, dbxStream): ! address = self.dbxAddress ! header = "" ! while (address): ! dbxStream.seek(address) ! header = dbxStream.read(16) ! self.dbxLength += self.__getEntry(header, 2) ! address = self.__getEntry(header, 3) ! pos = "" ! address = self.dbxAddress ! while (address): ! dbxStream.seek(address) ! header = dbxStream.read(16) ! pos += dbxStream.read(self.__getEntry(header, 2)) ! address = self.__getEntry(header, 3) ! self.dbxText = pos ! def getText(self): ! return self.dbxText ########################################################################### --- 380,418 ---- class dbxMessage: ! def __init__(self, dbxStream, dbxAddress): ! self.dbxAddress = dbxAddress ! self.dbxText = "" ! self.dbxLength = 0L ! self.__readMessageText(dbxStream) ! def __getEntry(self, dbxBuffer, dbxEntry): ! if len(dbxBuffer) < (dbxEntry * 4) + 4: ! return None ! return struct.unpack("L", dbxBuffer[dbxEntry * 4:(dbxEntry * 4) + 4])[0] ! def __readMessageText(self, dbxStream): ! address = self.dbxAddress ! header = "" ! while (address): ! dbxStream.seek(address) ! header = dbxStream.read(16) ! self.dbxLength += self.__getEntry(header, 2) ! address = self.__getEntry(header, 3) ! pos = "" ! address = self.dbxAddress ! while (address): ! dbxStream.seek(address) ! header = dbxStream.read(16) ! pos += dbxStream.read(self.__getEntry(header, 2)) ! address = self.__getEntry(header, 3) ! self.dbxText = pos ! def getText(self): ! return self.dbxText ########################################################################### *************** *** 421,488 **** if __name__ == '__main__': ! import sys ! import getopt ! try: ! opts, args = getopt.getopt(sys.argv[1:], 'hp') ! except getopt.error, msg: ! print >>sys.stderr, str(msg) + '\n\n' + __doc__ ! sys.exit() ! print_message = False ! for opt, arg in opts: ! if opt == '-h': ! print >>sys.stderr, __doc__ ! sys.exit() ! elif opt == '-p': ! print_message = True ! if not args: ! print "Please enter a directory with dbx files." ! sys.exit() ! MAILBOX_DIR = args[0] ! files = [os.path.join(MAILBOX_DIR, file) for file in \ ! os.listdir(MAILBOX_DIR) if os.path.splitext(file)[1] == '.dbx'] ! for file in files: ! try: ! print ! print file ! dbx = open(file, "rb", 0) ! header = dbxFileHeader(dbx) ! print "IS VALID DBX :", header.isValid() ! if header.isMessages(): ! info = dbxFileInfo(dbx, header.getEntry(dbxFileHeader.FH_FILE_INFO_LENGTH)) ! print "MAILBOX NAME :", info.getFolderName() ! print "CREATION TIME :", info.getCreationTime() ! entries = header.getEntry(dbxFileHeader.FH_ENTRIES) ! address = header.getEntry(dbxFileHeader.FH_TREE_ROOT_NODE_PTR) ! if address and entries: ! tree = dbxTree(dbx, address, entries) ! for i in range(entries): ! address = tree.getValue(i) ! messageInfo = dbxMessageInfo(dbx, address) ! if messageInfo.isIndexed(dbxMessageInfo.MI_MESSAGE_ADDRESS): ! messageAddress = messageInfo.getValueAsLong(dbxMessageInfo.MI_MESSAGE_ADDRESS) ! message = dbxMessage(dbx, messageAddress) ! if print_message: ! print ! print "Message :", messageInfo.getString(dbxMessageInfo.MI_SUBJECT) ! print "=" * (len(messageInfo.getString(dbxMessageInfo.MI_SUBJECT)) + 9) ! print ! print message.getText() ! except Exception, (strerror): ! print strerror ! dbx.close() --- 421,488 ---- if __name__ == '__main__': ! import sys ! import getopt ! try: ! opts, args = getopt.getopt(sys.argv[1:], 'hp') ! except getopt.error, msg: ! print >>sys.stderr, str(msg) + '\n\n' + __doc__ ! sys.exit() ! print_message = False ! for opt, arg in opts: ! if opt == '-h': ! print >>sys.stderr, __doc__ ! sys.exit() ! elif opt == '-p': ! print_message = True ! if not args: ! print "Please enter a directory with dbx files." ! sys.exit() ! MAILBOX_DIR = args[0] ! files = [os.path.join(MAILBOX_DIR, file) for file in \ ! os.listdir(MAILBOX_DIR) if os.path.splitext(file)[1] == '.dbx'] ! for file in files: ! try: ! print ! print file ! dbx = open(file, "rb", 0) ! header = dbxFileHeader(dbx) ! print "IS VALID DBX :", header.isValid() ! if header.isMessages(): ! info = dbxFileInfo(dbx, header.getEntry(dbxFileHeader.FH_FILE_INFO_LENGTH)) ! print "MAILBOX NAME :", info.getFolderName() ! print "CREATION TIME :", info.getCreationTime() ! entries = header.getEntry(dbxFileHeader.FH_ENTRIES) ! address = header.getEntry(dbxFileHeader.FH_TREE_ROOT_NODE_PTR) ! if address and entries: ! tree = dbxTree(dbx, address, entries) ! for i in range(entries): ! address = tree.getValue(i) ! messageInfo = dbxMessageInfo(dbx, address) ! if messageInfo.isIndexed(dbxMessageInfo.MI_MESSAGE_ADDRESS): ! messageAddress = messageInfo.getValueAsLong(dbxMessageInfo.MI_MESSAGE_ADDRESS) ! message = dbxMessage(dbx, messageAddress) ! if print_message: ! print ! print "Message :", messageInfo.getString(dbxMessageInfo.MI_SUBJECT) ! print "=" * (len(messageInfo.getString(dbxMessageInfo.MI_SUBJECT)) + 9) ! print ! print message.getText() ! except Exception, (strerror): ! print strerror ! dbx.close() Index: smtpproxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/smtpproxy.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** smtpproxy.py 3 Oct 2003 05:17:43 -0000 1.4 --- smtpproxy.py 16 Dec 2003 05:06:34 -0000 1.5 *************** *** 61,65 **** work, but I don't really know. Richie Hindle suggested something along these lines back in September '02. ! o Suggestions? --- 61,65 ---- work, but I don't really know. Richie Hindle suggested something along these lines back in September '02. ! o Suggestions? *************** *** 74,88 **** the smtpproxy can extract the id from the body of the message, if it is there. ! Header Body *** Windows 2000 MUAs *** Eudora 5.2 Forward * * Eudora 5.2 Redirect * Netscape Messenger (4.7) Forward (inline) * * ! Netscape Messenger (4.7) Forward (quoted) Plain * ! Netscape Messenger (4.7) Forward (quoted) HTML * ! Netscape Messenger (4.7) Forward (quoted) Plain & HTML * ! Netscape Messenger (4.7) Forward (attachment) Plain * * ! Netscape Messenger (4.7) Forward (attachment) HTML * * ! Netscape Messenger (4.7) Forward (attachment) Plain & HTML * * Outlook Express 6 Forward HTML (Base64) * Outlook Express 6 Forward HTML (None) * --- 74,88 ---- the smtpproxy can extract the id from the body of the message, if it is there. ! Header Body *** Windows 2000 MUAs *** Eudora 5.2 Forward * * Eudora 5.2 Redirect * Netscape Messenger (4.7) Forward (inline) * * ! Netscape Messenger (4.7) Forward (quoted) Plain * ! Netscape Messenger (4.7) Forward (quoted) HTML * ! Netscape Messenger (4.7) Forward (quoted) Plain & HTML * ! Netscape Messenger (4.7) Forward (attachment) Plain * * ! Netscape Messenger (4.7) Forward (attachment) HTML * * ! Netscape Messenger (4.7) Forward (attachment) Plain & HTML * * Outlook Express 6 Forward HTML (Base64) * Outlook Express 6 Forward HTML (None) * *************** *** 92,96 **** Outlook Express 6 Forward Plain (QP) * Outlook Express 6 Forward Plain (uuencoded) * ! http://www.endymion.com/products/mailman Forward * M2 (Opera Mailer 7.01) Forward * M2 (Opera Mailer 7.01) Redirect * * --- 92,96 ---- Outlook Express 6 Forward Plain (QP) * Outlook Express 6 Forward Plain (uuencoded) * ! http://www.endymion.com/products/mailman Forward * M2 (Opera Mailer 7.01) Forward * M2 (Opera Mailer 7.01) Redirect * * *************** *** 263,267 **** then do not forward the command on. Otherwise forward verbatim. ! Any other commands are merely passed on verbatim to the server. """ --- 263,267 ---- then do not forward the command on. Otherwise forward verbatim. ! Any other commands are merely passed on verbatim to the server. """ *************** *** 334,338 **** self.blockData = False return "%s:%s" % (command, ' '.join(args)) ! def onData(self, command, args): self.inData = True --- 334,338 ---- self.blockData = False return "%s:%s" % (command, ' '.join(args)) ! def onData(self, command, args): self.inData = True *************** *** 357,361 **** self.state = state self.imap = imap ! def extractSpambayesID(self, data): msg = message_from_string(data) --- 357,361 ---- self.state = state self.imap = imap ! def extractSpambayesID(self, data): msg = message_from_string(data) *************** *** 493,497 **** "different lengths!" sys.exit() ! return servers, proxyPorts def CreateProxies(servers, proxyPorts, trainer): --- 493,497 ---- "different lengths!" sys.exit() ! return servers, proxyPorts def CreateProxies(servers, proxyPorts, trainer): Index: storage.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/storage.py,v retrieving revision 1.36 retrieving revision 1.37 diff -C2 -d -r1.36 -r1.37 *** storage.py 8 Oct 2003 04:04:35 -0000 1.36 --- storage.py 16 Dec 2003 05:06:34 -0000 1.37 *************** *** 238,242 **** self._write_state_key() self.db.sync() ! def _write_state_key(self): self.db[self.statekey] = (classifier.PICKLE_VERSION, --- 238,242 ---- self._write_state_key() self.db.sync() ! def _write_state_key(self): self.db[self.statekey] = (classifier.PICKLE_VERSION, *************** *** 248,252 **** key.""" self._write_state_key() ! def _wordinfoget(self, word): if isinstance(word, unicode): --- 248,252 ---- key.""" self._write_state_key() ! def _wordinfoget(self, word): if isinstance(word, unicode): *************** *** 340,344 **** '''Commit the current transaction - may commit at db or cursor''' raise NotImplementedError, "must be implemented in subclass" ! def create_bayes(self): '''Create a new bayes table''' --- 340,344 ---- '''Commit the current transaction - may commit at db or cursor''' raise NotImplementedError, "must be implemented in subclass" ! def create_bayes(self): '''Create a new bayes table''' *************** *** 451,455 **** import psycopg ! if options["globals", "verbose"]: print >> sys.stderr, 'Loading state from',self.db_name,'database' --- 451,455 ---- import psycopg ! if options["globals", "verbose"]: print >> sys.stderr, 'Loading state from',self.db_name,'database' *************** *** 463,467 **** self.db.rollback() self.create_bayes() ! if self._has_key(self.statekey): row = self._get_row(self.statekey) --- 463,467 ---- self.db.rollback() self.create_bayes() ! if self._has_key(self.statekey): row = self._get_row(self.statekey) *************** *** 485,489 **** It is assumed that the database already exists, and that the mySQL server is currently running.''' ! def __init__(self, data_source_name): self.table_definition = ("create table bayes (" --- 485,489 ---- It is assumed that the database already exists, and that the mySQL server is currently running.''' ! def __init__(self, data_source_name): self.table_definition = ("create table bayes (" *************** *** 522,526 **** import MySQLdb ! if options["globals", "verbose"]: print >> sys.stderr, 'Loading state from',self.db_name,'database' --- 522,526 ---- import MySQLdb ! if options["globals", "verbose"]: print >> sys.stderr, 'Loading state from',self.db_name,'database' *************** *** 535,539 **** self.db.rollback() self.create_bayes() ! if self._has_key(self.statekey): row = self._get_row(self.statekey) --- 535,539 ---- self.db.rollback() self.create_bayes() ! if self._has_key(self.statekey): row = self._get_row(self.statekey) Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.21 retrieving revision 1.22 diff -C2 -d -r1.21 -r1.22 *** tokenizer.py 15 Dec 2003 09:20:33 -0000 1.21 --- tokenizer.py 16 Dec 2003 05:06:34 -0000 1.22 *************** *** 663,670 **** if 3 <= n <= maxword: yield word ! elif n >= 3: # A long word. ! # Don't want to skip embedded email addresses. # An earlier scheme also split up the y in x@y on '.'. Not splitting --- 663,670 ---- if 3 <= n <= maxword: yield word ! elif n >= 3: # A long word. ! # Don't want to skip embedded email addresses. # An earlier scheme also split up the y in x@y on '.'. Not splitting *************** *** 816,820 **** # http://mail.python.org/pipermail/spambayes-dev/2003-September/001177.html yield "filename:" ! if 0: # disabled; see comment before function x = msg.get('content-transfer-encoding') --- 816,820 ---- # http://mail.python.org/pipermail/spambayes-dev/2003-September/001177.html yield "filename:" ! if 0: # disabled; see comment before function x = msg.get('content-transfer-encoding') *************** *** 1174,1178 **** elif valid_habeas == 9: yield "x-habeas-swe:valid" ! # Subject: # Don't ignore case in Subject lines; e.g., 'free' versus 'FREE' is --- 1174,1178 ---- elif valid_habeas == 9: yield "x-habeas-swe:valid" ! # Subject: # Don't ignore case in Subject lines; e.g., 'free' versus 'FREE' is From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:00 2003 Subject: [Spambayes-checkins] spambayes/spambayes/test sb_test_support.py, 1.1, 1.2 test_programs.py, 1.2, 1.3 test_smtpproxy.py, 1.1, 1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/test In directory sc8-pr-cvs1:/tmp/cvs-serv12349/spambayes/test Modified Files: sb_test_support.py test_programs.py test_smtpproxy.py Log Message: Whitespace normalization. Index: sb_test_support.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/test/sb_test_support.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** sb_test_support.py 3 Dec 2003 00:34:17 -0000 1.1 --- sb_test_support.py 16 Dec 2003 05:06:34 -0000 1.2 *************** *** 29,35 **** import sb_server except ImportError: ! # Scripts are usually in "spambayes/scripts" (for an # installed SpamBayes, they appear to be in ! # os.path.join(sys.prefix(), "scripts"), which we may like to # leverage - however, these test scripts are not currently # installed). --- 29,35 ---- import sb_server except ImportError: ! # Scripts are usually in "spambayes/scripts" (for an # installed SpamBayes, they appear to be in ! # os.path.join(sys.prefix(), "scripts"), which we may like to # leverage - however, these test scripts are not currently # installed). Index: test_programs.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_programs.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** test_programs.py 14 Dec 2003 01:12:08 -0000 1.2 --- test_programs.py 16 Dec 2003 05:06:34 -0000 1.3 *************** *** 132,136 **** except sb_server.AlreadyRunningException: return True ! class TestServer(unittest.TestCase): def setUp(self): --- 132,136 ---- except sb_server.AlreadyRunningException: return True ! class TestServer(unittest.TestCase): def setUp(self): *************** *** 242,246 **** self.failUnless(not is_any_sb_server_running(), "Should be no platform mutex held after stopping") ! if __name__=='__main__': sb_test_support.unittest_main() --- 242,246 ---- self.failUnless(not is_any_sb_server_running(), "Should be no platform mutex held after stopping") ! if __name__=='__main__': sb_test_support.unittest_main() Index: test_smtpproxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/test/test_smtpproxy.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** test_smtpproxy.py 24 Sep 2003 06:53:37 -0000 1.1 --- test_smtpproxy.py 16 Dec 2003 05:06:34 -0000 1.2 *************** *** 136,140 **** print "pushing", repr(data) Dibbler.BrighterAsyncChat.push(self, data) ! def recv(self, buffer_size): """Asynchat override.""" --- 136,140 ---- print "pushing", repr(data) Dibbler.BrighterAsyncChat.push(self, data) ! def recv(self, buffer_size): """Asynchat override.""" From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:01 2003 Subject: [Spambayes-checkins] website/scripts striphtml.py,1.1.1.1,1.2 Message-ID: Update of /cvsroot/spambayes/website/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv12349/website/scripts Modified Files: striphtml.py Log Message: Whitespace normalization. Index: striphtml.py =================================================================== RCS file: /cvsroot/spambayes/website/scripts/striphtml.py,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -C2 -d -r1.1.1.1 -r1.2 *** striphtml.py 19 Sep 2002 08:40:55 -0000 1.1.1.1 --- striphtml.py 16 Dec 2003 05:06:34 -0000 1.2 *************** *** 30,60 **** data = fi.read() if eheadsearch.search(data) >= 0: ! i = eheadsearch.regs[0][1] ! head, data = data[:i], data[i:] else: ! head = "" bstart = bodysearch.search(data) if bstart < 0: ! bstart = 0 else: ! head = head + data[:bstart] ! bstart = bstart + len(bodysearch.group(0)) if bannersearch.search(data) >= 0: ! i, j = bannersearch.regs[0] ! print "banner", i, j, `data[i:j]` ! data = data[:i] + data[j:] end = ebodyearch.search(data, bstart) if end < 0: ! end = len(data) body = string.strip(data[bstart:end]) if titlesearch.search(head) >= 0: ! title = titlesearch.group(1) elif h1search.search(body) >= 0: ! title = h1search.group(1) else: ! title = "" if title: ! title = string.join(string.split(title)) ! fo.write("Title: %s\n" % title) fo.write("\n") fo.write(body) --- 30,60 ---- data = fi.read() if eheadsearch.search(data) >= 0: ! i = eheadsearch.regs[0][1] ! head, data = data[:i], data[i:] else: ! head = "" bstart = bodysearch.search(data) if bstart < 0: ! bstart = 0 else: ! head = head + data[:bstart] ! bstart = bstart + len(bodysearch.group(0)) if bannersearch.search(data) >= 0: ! i, j = bannersearch.regs[0] ! print "banner", i, j, `data[i:j]` ! data = data[:i] + data[j:] end = ebodyearch.search(data, bstart) if end < 0: ! end = len(data) body = string.strip(data[bstart:end]) if titlesearch.search(head) >= 0: ! title = titlesearch.group(1) elif h1search.search(body) >= 0: ! title = h1search.group(1) else: ! title = "" if title: ! title = string.join(string.split(title)) ! fo.write("Title: %s\n" % title) fo.write("\n") fo.write(body) *************** *** 66,79 **** def makedirs(dirname): if os.path.exists(dirname): ! return 1 head, tail = os.path.split(dirname) if head: ! if not makedirs(head): ! return 0 try: ! os.mkdir(dirname, 0777) ! return 1 except os.error: ! return 0 def main(): --- 66,79 ---- def makedirs(dirname): if os.path.exists(dirname): ! return 1 head, tail = os.path.split(dirname) if head: ! if not makedirs(head): ! return 0 try: ! os.mkdir(dirname, 0777) ! return 1 except os.error: ! return 0 def main(): *************** *** 81,105 **** prefix = "" for o, a in opts: ! if o == "-p": prefix = a if not args: ! strip(sys.stdin, sys.stdout) else: ! for file in args: ! name, ext = os.path.splitext(file) ! if ext == ".htp": ! error("file %s is already an HTML prototype" % name) ! continue ! sys.stderr.write("Processing %s ...\n" % file) ! htpname = prefix + name + ".htp" ! dirname = os.path.dirname(htpname) ! if dirname: ! if not makedirs(dirname): ! error("can't create directory %s" % dirname) ! continue ! fi = open(file, "r") ! fo = open(htpname, "w") ! strip(fi, fo) ! fi.close() ! fo.close() if __name__ == '__main__': --- 81,105 ---- prefix = "" for o, a in opts: ! if o == "-p": prefix = a if not args: ! strip(sys.stdin, sys.stdout) else: ! for file in args: ! name, ext = os.path.splitext(file) ! if ext == ".htp": ! error("file %s is already an HTML prototype" % name) ! continue ! sys.stderr.write("Processing %s ...\n" % file) ! htpname = prefix + name + ".htp" ! dirname = os.path.dirname(htpname) ! if dirname: ! if not makedirs(dirname): ! error("can't create directory %s" % dirname) ! continue ! fi = open(file, "r") ! fo = open(htpname, "w") ! strip(fi, fo) ! fi.close() ! fo.close() if __name__ == '__main__': From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:03 2003 Subject: [Spambayes-checkins] spambayes/spambayes/resources __init__.py, 1.2, 1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/resources In directory sc8-pr-cvs1:/tmp/cvs-serv12349/spambayes/resources Modified Files: __init__.py Log Message: Whitespace normalization. Index: __init__.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/resources/__init__.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** __init__.py 5 Sep 2003 01:15:28 -0000 1.2 --- __init__.py 16 Dec 2003 05:06:34 -0000 1.3 *************** *** 6,36 **** """ try: ! __file__ except NameError: ! pass else: ! import os ! if os.path.splitext(os.path.basename( __file__ ))[0] == "__init__": ! try: ! from resourcepackage import package, defaultgenerators ! generators = defaultgenerators.generators.copy() ! ! ### CUSTOMISATION POINT ! ## import specialised generators here, such as for wxPython ! #from resourcepackage import wxgenerators ! #generators.update( wxgenerators.generators ) ! except ImportError: ! pass ! else: ! package = package.Package( ! packageName = __name__, ! directory = os.path.dirname( os.path.abspath(__file__) ), ! generators = generators, ! ) ! package.scan( ! ### CUSTOMISATION POINT ! ## force true -> always re-loads from external files, otherwise ! ## only reloads if the file is newer than the generated .py file. ! # force = 1, ! ) ! --- 6,35 ---- """ try: ! __file__ except NameError: ! pass else: ! import os ! if os.path.splitext(os.path.basename( __file__ ))[0] == "__init__": ! try: ! from resourcepackage import package, defaultgenerators ! generators = defaultgenerators.generators.copy() ! ! ### CUSTOMISATION POINT ! ## import specialised generators here, such as for wxPython ! #from resourcepackage import wxgenerators ! #generators.update( wxgenerators.generators ) ! except ImportError: ! pass ! else: ! package = package.Package( ! packageName = __name__, ! directory = os.path.dirname( os.path.abspath(__file__) ), ! generators = generators, ! ) ! package.scan( ! ### CUSTOMISATION POINT ! ## force true -> always re-loads from external files, otherwise ! ## only reloads if the file is newer than the generated .py file. ! # force = 1, ! ) From tim_one at users.sourceforge.net Tue Dec 16 00:06:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:05 2003 Subject: [Spambayes-checkins] spambayes/contrib mod_spambayes.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/contrib In directory sc8-pr-cvs1:/tmp/cvs-serv12349/contrib Modified Files: mod_spambayes.py Log Message: Whitespace normalization. Index: mod_spambayes.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/contrib/mod_spambayes.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** mod_spambayes.py 4 May 2003 03:10:36 -0000 1.2 --- mod_spambayes.py 16 Dec 2003 05:06:33 -0000 1.3 *************** *** 25,29 **** print "text:", s[0:40], "...", s[-40:] return "not authorized" ! return s from proxy3_util import * --- 25,29 ---- print "text:", s[0:40], "...", s[-40:] return "not authorized" ! return s from proxy3_util import * From tim_one at users.sourceforge.net Tue Dec 16 00:06:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:07 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/sandbox dump_props.py, 1.11, 1.12 extract_bad_msg_from_log.py, 1.1, 1.2 extract_prop.py, 1.1, 1.2 find_dupe_props.py, 1.1, 1.2 set_read_flag.py, 1.1, 1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/sandbox In directory sc8-pr-cvs1:/tmp/cvs-serv12349/Outlook2000/sandbox Modified Files: dump_props.py extract_bad_msg_from_log.py extract_prop.py find_dupe_props.py set_read_flag.py Log Message: Whitespace normalization. Index: dump_props.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/dump_props.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** dump_props.py 10 Dec 2003 05:06:23 -0000 1.11 --- dump_props.py 16 Dec 2003 05:06:33 -0000 1.12 *************** *** 140,144 **** shorten, get_large_props) print "%-20s: %s" % (prop_name, prop_repr) ! def usage(driver, extra = None): folder_doc = driver.GetFolderNameDoc() --- 140,144 ---- shorten, get_large_props) print "%-20s: %s" % (prop_name, prop_repr) ! def usage(driver, extra = None): folder_doc = driver.GetFolderNameDoc() Index: extract_bad_msg_from_log.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/extract_bad_msg_from_log.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** extract_bad_msg_from_log.py 19 Aug 2003 13:32:29 -0000 1.1 --- extract_bad_msg_from_log.py 16 Dec 2003 05:06:33 -0000 1.2 *************** *** 21,25 **** msg_str = msg_str.replace("\r\n", "\n") sys.stdout.write(msg_str) ! inname = sys.argv[1] --- 21,25 ---- msg_str = msg_str.replace("\r\n", "\n") sys.stdout.write(msg_str) ! inname = sys.argv[1] Index: extract_prop.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/extract_prop.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** extract_prop.py 1 Sep 2003 06:04:35 -0000 1.1 --- extract_prop.py 16 Dec 2003 05:06:33 -0000 1.2 *************** *** 17,21 **** propIds = obj.GetIDsFromNames(props, 0) prop = mapitags.PROP_TAG( mapitags.PT_UNSPECIFIED, mapitags.PROP_ID(propIds[0])) ! hr, data = item.GetProps((prop,), 0) prop_tag, prop_val = data[0] --- 17,21 ---- propIds = obj.GetIDsFromNames(props, 0) prop = mapitags.PROP_TAG( mapitags.PT_UNSPECIFIED, mapitags.PROP_ID(propIds[0])) ! hr, data = item.GetProps((prop,), 0) prop_tag, prop_val = data[0] *************** *** 35,49 **** elif mapitags.PROP_TYPE(prop_tag)==mapitags.PT_ERROR and \ prop_val in [mapi.MAPI_E_NOT_ENOUGH_MEMORY,'MAPI_E_NOT_ENOUGH_MEMORY']: ! prop_tag = mapitags.PROP_TAG(mapitags.PT_BINARY, mapitags.PROP_ID(prop_tag)) ! stream = item.OpenProperty(prop_tag, ! pythoncom.IID_IStream, ! 0, 0) ! chunks = [] ! while 1: ! chunk = stream.Read(4096) ! if not chunk: ! break ! chunks.append(chunk) ! prop_val = "".join(chunks) outfile.write(prop_val) --- 35,49 ---- elif mapitags.PROP_TYPE(prop_tag)==mapitags.PT_ERROR and \ prop_val in [mapi.MAPI_E_NOT_ENOUGH_MEMORY,'MAPI_E_NOT_ENOUGH_MEMORY']: ! prop_tag = mapitags.PROP_TAG(mapitags.PT_BINARY, mapitags.PROP_ID(prop_tag)) ! stream = item.OpenProperty(prop_tag, ! pythoncom.IID_IStream, ! 0, 0) ! chunks = [] ! while 1: ! chunk = stream.Read(4096) ! if not chunk: ! break ! chunks.append(chunk) ! prop_val = "".join(chunks) outfile.write(prop_val) Index: find_dupe_props.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/find_dupe_props.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** find_dupe_props.py 28 Jul 2003 03:24:16 -0000 1.1 --- find_dupe_props.py 16 Dec 2003 05:06:33 -0000 1.2 *************** *** 102,106 **** print details sys.exit(1) ! FindDupeProps(driver, folder, args[0], dupe_dict) DumpDupes(dupe_dict) --- 102,106 ---- print details sys.exit(1) ! FindDupeProps(driver, folder, args[0], dupe_dict) DumpDupes(dupe_dict) Index: set_read_flag.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/sandbox/set_read_flag.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** set_read_flag.py 17 Jun 2003 02:04:43 -0000 1.1 --- set_read_flag.py 16 Dec 2003 05:06:33 -0000 1.2 *************** *** 1,4 **** from __future__ import generators ! # Set items to read/unread import pythoncom --- 1,4 ---- from __future__ import generators ! # Set items to read/unread import pythoncom From tim_one at users.sourceforge.net Tue Dec 16 00:06:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:09 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/installer crank.py, 1.2, 1.3 spambayes_addin.py, 1.1, 1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/installer In directory sc8-pr-cvs1:/tmp/cvs-serv12349/Outlook2000/installer Modified Files: crank.py spambayes_addin.py Log Message: Whitespace normalization. Index: crank.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/installer/crank.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** crank.py 10 Aug 2003 07:06:12 -0000 1.2 --- crank.py 16 Dec 2003 05:06:33 -0000 1.3 *************** *** 41,45 **** return 1 return 0 ! if __name__=='__main__': main() --- 41,45 ---- return 1 return 0 ! if __name__=='__main__': main() Index: spambayes_addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/installer/spambayes_addin.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** spambayes_addin.py 20 Feb 2003 08:16:31 -0000 1.1 --- spambayes_addin.py 16 Dec 2003 05:06:33 -0000 1.2 *************** *** 34,38 **** DllUnregisterServer() break ! # MS seems to like /automate to run the class factories. if string.find(arg, "/automate") > -1: --- 34,38 ---- DllUnregisterServer() break ! # MS seems to like /automate to run the class factories. if string.find(arg, "/automate") > -1: From tim_one at users.sourceforge.net Tue Dec 16 00:06:36 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:07:10 2003 Subject: [Spambayes-checkins] spambayes/scripts sb_dbexpimp.py, 1.3, 1.4 sb_imapfilter.py, 1.16, 1.17 sb_notesfilter.py, 1.4, 1.5 sb_pop3dnd.py, 1.3, 1.4 sb_server.py, 1.13, 1.14 Message-ID: Update of /cvsroot/spambayes/spambayes/scripts In directory sc8-pr-cvs1:/tmp/cvs-serv12349/scripts Modified Files: sb_dbexpimp.py sb_imapfilter.py sb_notesfilter.py sb_pop3dnd.py sb_server.py Log Message: Whitespace normalization. Index: sb_dbexpimp.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_dbexpimp.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** sb_dbexpimp.py 26 Nov 2003 00:12:44 -0000 1.3 --- sb_dbexpimp.py 16 Dec 2003 05:06:33 -0000 1.4 *************** *** 11,35 **** a spambayes database into/from a flat file. This is useful in a number of scenarios. ! Platform portability of database - flat files can be exported and imported across platforms (winduhs and linux, for example) ! Database implementation changes - databases can survive database implementation upgrades or new database implementations. For example, if a dbm implementation changes between python x.y and python x.y+1... ! Database reorganization - an export followed by an import reorgs an ! existing database, improving performance, at least in some database implementations ! Database sharing - it is possible to distribute particular databases for research purposes, database sharing purposes, or for new users to have a 'seed' database to start with. ! Database merging - multiple databases can be merged into one quite easily by simply not specifying -n on an import. This will add the two database nham and nspams together (assuming the two databases do not share corpora) and for wordinfo conflicts, will add spamcount and hamcount together. ! Spambayes software release migration - an export can be executed before a release upgrade, as part of the installation script. Then, after the --- 11,35 ---- a spambayes database into/from a flat file. This is useful in a number of scenarios. ! Platform portability of database - flat files can be exported and imported across platforms (winduhs and linux, for example) ! Database implementation changes - databases can survive database implementation upgrades or new database implementations. For example, if a dbm implementation changes between python x.y and python x.y+1... ! Database reorganization - an export followed by an import reorgs an ! existing database, improving performance, at least in some database implementations ! Database sharing - it is possible to distribute particular databases for research purposes, database sharing purposes, or for new users to have a 'seed' database to start with. ! Database merging - multiple databases can be merged into one quite easily by simply not specifying -n on an import. This will add the two database nham and nspams together (assuming the two databases do not share corpora) and for wordinfo conflicts, will add spamcount and hamcount together. ! Spambayes software release migration - an export can be executed before a release upgrade, as part of the installation script. Then, after the *************** *** 37,43 **** effectively preserve existing training. This eliminates the need for retraining every time a release is installed. ! Others? I'm sure I haven't thought of everything... ! Usage: sb_dbexpimp [options] --- 37,43 ---- effectively preserve existing training. This eliminates the need for retraining every time a release is installed. ! Others? I'm sure I haven't thought of everything... ! Usage: sb_dbexpimp [options] *************** *** 61,75 **** Export pickled mybayes.db into mybayes.db.export as a csv flat file sb_dbexpimp -e -d mybayes.db -f mybayes.db.export ! Import mybayes.eb.export into a new DBM mybayes.db sb_dbexpimp -i -D mybayes.db -f mybayes.db.export ! Export, then import (reorganize) new pickled mybayes.db sb_dbexpimp -e -i -n -d mybayes.db -f mybayes.db.export ! Convert a bayes database from pickle to DBM sb_dbexpimp -e -d abayes.db -f abayes.export sb_dbexpimp -i -D abayes.db -f abayes.export ! Create a new database (newbayes.db) from two databases (abayes.db, bbayes.db) --- 61,75 ---- Export pickled mybayes.db into mybayes.db.export as a csv flat file sb_dbexpimp -e -d mybayes.db -f mybayes.db.export ! Import mybayes.eb.export into a new DBM mybayes.db sb_dbexpimp -i -D mybayes.db -f mybayes.db.export ! Export, then import (reorganize) new pickled mybayes.db sb_dbexpimp -e -i -n -d mybayes.db -f mybayes.db.export ! Convert a bayes database from pickle to DBM sb_dbexpimp -e -d abayes.db -f abayes.export sb_dbexpimp -i -D abayes.db -f abayes.export ! Create a new database (newbayes.db) from two databases (abayes.db, bbayes.db) *************** *** 97,101 **** # Maintain compatibility with Python 2.2 True, False = 1, 0 ! import spambayes.storage from spambayes.Options import options --- 97,101 ---- # Maintain compatibility with Python 2.2 True, False = 1, 0 ! import spambayes.storage from spambayes.Options import options *************** *** 126,140 **** except IOError, e: if e.errno != errno.ENOENT: ! raise ! nham = bayes.nham; nspam = bayes.nspam; ! print "Exporting database %s to file %s" % (dbFN, outFN) print "Database has %s ham, %s spam, and %s words" \ % (nham, nspam, len(words)) ! fp.write("%s,%s,\n" % (nham, nspam)) ! for word in words: wi = bayes._wordinfoget(word) --- 126,140 ---- except IOError, e: if e.errno != errno.ENOENT: ! raise ! nham = bayes.nham; nspam = bayes.nspam; ! print "Exporting database %s to file %s" % (dbFN, outFN) print "Database has %s ham, %s spam, and %s words" \ % (nham, nspam, len(words)) ! fp.write("%s,%s,\n" % (nham, nspam)) ! for word in words: wi = bayes._wordinfoget(word) *************** *** 143,147 **** word = uquote(word) fp.write("%s`%s`%s`\n" % (word, hamcount, spamcount)) ! fp.close() --- 143,147 ---- word = uquote(word) fp.write("%s`%s`%s`\n" % (word, hamcount, spamcount)) ! fp.close() *************** *** 154,158 **** if e.errno != 2: # errno. raise ! try: os.unlink(dbFN+".dat") --- 154,158 ---- if e.errno != 2: # errno. raise ! try: os.unlink(dbFN+".dat") *************** *** 160,164 **** if e.errno != 2: # errno. raise ! try: os.unlink(dbFN+".dir") --- 160,164 ---- if e.errno != 2: # errno. raise ! try: os.unlink(dbFN+".dir") *************** *** 166,170 **** if e.errno != 2: # errno. raise ! if useDBM: bayes = spambayes.storage.DBDictClassifier(dbFN) --- 166,170 ---- if e.errno != 2: # errno. raise ! if useDBM: bayes = spambayes.storage.DBDictClassifier(dbFN) *************** *** 176,184 **** except IOError, e: if e.errno != errno.ENOENT: ! raise ! nline = fp.readline() (nham, nspam, junk) = re.split(',', nline) ! if newDBM: bayes.nham = int(nham) --- 176,184 ---- except IOError, e: if e.errno != errno.ENOENT: ! raise ! nline = fp.readline() (nham, nspam, junk) = re.split(',', nline) ! if newDBM: bayes.nham = int(nham) *************** *** 187,204 **** bayes.nham += int(nham) bayes.nspam += int(nspam) ! if newDBM: impType = "Importing" else: impType = "Merging" ! print "%s database %s using file %s" % (impType, dbFN, inFN) lines = fp.readlines() ! for line in lines: (word, hamcount, spamcount, junk) = re.split('`', line) word = uunquote(word) ! try: wi = bayes.wordinfo[word] --- 187,204 ---- bayes.nham += int(nham) bayes.nspam += int(nspam) ! if newDBM: impType = "Importing" else: impType = "Merging" ! print "%s database %s using file %s" % (impType, dbFN, inFN) lines = fp.readlines() ! for line in lines: (word, hamcount, spamcount, junk) = re.split('`', line) word = uunquote(word) ! try: wi = bayes.wordinfo[word] *************** *** 208,212 **** wi.hamcount += int(hamcount) wi.spamcount += int(spamcount) ! bayes._wordinfoset(word, wi) --- 208,212 ---- wi.hamcount += int(hamcount) wi.spamcount += int(spamcount) ! bayes._wordinfoset(word, wi) *************** *** 217,221 **** bayes.store() print "Finished storing database" ! if useDBM: words = bayes.db.keys() --- 217,221 ---- bayes.store() print "Finished storing database" ! if useDBM: words = bayes.db.keys() *************** *** 223,227 **** else: words = bayes.wordinfo.keys() ! print "Database has %s ham, %s spam, and %s words" \ % (bayes.nham, bayes.nspam, len(words)) --- 223,227 ---- else: words = bayes.wordinfo.keys() ! print "Database has %s ham, %s spam, and %s words" \ % (bayes.nham, bayes.nspam, len(words)) Index: sb_imapfilter.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_imapfilter.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** sb_imapfilter.py 15 Dec 2003 09:15:19 -0000 1.16 --- sb_imapfilter.py 16 Dec 2003 05:06:33 -0000 1.17 *************** *** 10,15 **** sb_imapfilter [options] ! note: option values with spaces in them must be enclosed ! in double quotes options: --- 10,15 ---- sb_imapfilter [options] ! note: option values with spaces in them must be enclosed ! in double quotes options: *************** *** 31,35 **** Classify inbox, with dbm database sb_imapfilter -c -D bayes.db ! Train Spam and Ham, then classify inbox, with dbm database sb_imapfilter -t -c -D bayes.db --- 31,35 ---- Classify inbox, with dbm database sb_imapfilter -c -D bayes.db ! Train Spam and Ham, then classify inbox, with dbm database sb_imapfilter -t -c -D bayes.db *************** *** 53,57 **** mail flagged as such, *or* if you set the imap_expunge option to True, then this mail will be irretrievably lost. ! To Do: o IMAPMessage and IMAPFolder currently carry out very simple checks --- 53,57 ---- mail flagged as such, *or* if you set the imap_expunge option to True, then this mail will be irretrievably lost. ! To Do: o IMAPMessage and IMAPFolder currently carry out very simple checks *************** *** 181,185 **** class IMAPSession(BaseIMAP): '''A class extending the IMAP4 class, with a few optimizations''' ! def __init__(self, server, port, debug=0, do_expunge=False): try: --- 181,185 ---- class IMAPSession(BaseIMAP): '''A class extending the IMAP4 class, with a few optimizations''' ! def __init__(self, server, port, debug=0, do_expunge=False): try: *************** *** 212,216 **** raise self.logged_in = True ! def logout(self): # sign off --- 212,216 ---- raise self.logged_in = True ! def logout(self): # sign off *************** *** 230,234 **** self.expunge() BaseIMAP.logout(self) # superclass logout ! def SelectFolder(self, folder): '''A method to point ensuing imap operations at a target folder''' --- 230,234 ---- self.expunge() BaseIMAP.logout(self) # superclass logout ! def SelectFolder(self, folder): '''A method to point ensuing imap operations at a target folder''' *************** *** 587,591 **** move_opt_name])) msg.Save() ! return num_trained def Filter(self, classifier, spamfolder, unsurefolder): --- 587,591 ---- move_opt_name])) msg.Save() ! return num_trained def Filter(self, classifier, spamfolder, unsurefolder): *************** *** 621,629 **** self.unsure_folder = IMAPFolder(options["imap", "unsure_folder"]) self.classifier = classifier ! def Train(self): if options["globals", "verbose"]: t = time.time() ! total_ham_trained = 0 total_spam_trained = 0 --- 621,629 ---- self.unsure_folder = IMAPFolder(options["imap", "unsure_folder"]) self.classifier = classifier ! def Train(self): if options["globals", "verbose"]: t = time.time() ! total_ham_trained = 0 total_spam_trained = 0 *************** *** 657,661 **** if total_ham_trained or total_spam_trained: self.classifier.store() ! if options["globals", "verbose"]: print "Training took %s seconds, %s messages were trained" \ --- 657,661 ---- if total_ham_trained or total_spam_trained: self.classifier.store() ! if options["globals", "verbose"]: print "Training took %s seconds, %s messages were trained" \ *************** *** 673,677 **** imap.SelectFolder(self.spam_folder.name) imap.SelectFolder(self.unsure_folder.name) ! for filter_folder in options["imap", "filter_folders"]: # Select the folder to make sure it exists --- 673,677 ---- imap.SelectFolder(self.spam_folder.name) imap.SelectFolder(self.unsure_folder.name) ! for filter_folder in options["imap", "filter_folders"]: # Select the folder to make sure it exists *************** *** 682,686 **** for key in count.keys(): count[key] += subcount.get(key, 0) ! if options["globals", "verbose"]: if count is not None: --- 682,686 ---- for key in count.keys(): count[key] += subcount.get(key, 0) ! if options["globals", "verbose"]: if count is not None: *************** *** 689,693 **** print "Classifying took", time.time() - t, "seconds." ! def run(): global imap --- 689,693 ---- print "Classifying took", time.time() - t, "seconds." ! def run(): global imap *************** *** 751,755 **** bdbname = os.path.expanduser(bdbname) ! if options["globals", "verbose"]: print "Loading database %s..." % (bdbname), --- 751,755 ---- bdbname = os.path.expanduser(bdbname) ! if options["globals", "verbose"]: print "Loading database %s..." % (bdbname), *************** *** 758,762 **** if options["globals", "verbose"]: ! print "Done." if options["imap", "server"]: --- 758,762 ---- if options["globals", "verbose"]: ! print "Done." if options["imap", "server"]: *************** *** 817,821 **** imap.logout() ! if sleepTime: time.sleep(sleepTime) --- 817,821 ---- imap.logout() ! if sleepTime: time.sleep(sleepTime) Index: sb_notesfilter.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_notesfilter.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** sb_notesfilter.py 10 Nov 2003 20:21:52 -0000 1.4 --- sb_notesfilter.py 16 Dec 2003 05:06:33 -0000 1.5 *************** *** 10,14 **** database. The Notes client must be running when this process is executed. ! It requires a Notes folder, named as a parameter, with four subfolders: --- 10,14 ---- database. The Notes client must be running when this process is executed. ! It requires a Notes folder, named as a parameter, with four subfolders: *************** *** 25,29 **** 3. Replicate (-r option) 4. Classify the inbox (-c option) ! Mail that is to be trained as spam should be manually moved to that folder by the user. Likewise mail that is to be trained as --- 25,29 ---- 3. Replicate (-r option) 4. Classify the inbox (-c option) ! Mail that is to be trained as spam should be manually moved to that folder by the user. Likewise mail that is to be trained as *************** *** 65,69 **** which if allowed to continue, would cause Spambayes to tend to classify everything as Spam. ! Because there is no programmatic way to determine if a particular mail has been previously processed by this classification program, --- 65,69 ---- which if allowed to continue, would cause Spambayes to tend to classify everything as Spam. ! Because there is no programmatic way to determine if a particular mail has been previously processed by this classification program, *************** *** 80,85 **** sb_notesfilter [options] ! note: option values with spaces in them must be enclosed ! in double quotes options: --- 80,85 ---- sb_notesfilter [options] ! note: option values with spaces in them must be enclosed ! in double quotes options: *************** *** 108,118 **** Replicate and classify inbox sb_notesfilter -c -d notesbayes -r mynoteserv -l mail.nsf -f Spambayes ! Train Spam and Ham, then classify inbox sb_notesfilter -t -c -d notesbayes -l mail.nsf -f Spambayes ! ! Replicate, then classify inbox sb_notesfilter -c -d test7 -l mail.nsf -r nynoteserv -f Spambayes ! To Do: o Dump/purge notesindex file --- 108,118 ---- Replicate and classify inbox sb_notesfilter -c -d notesbayes -r mynoteserv -l mail.nsf -f Spambayes ! Train Spam and Ham, then classify inbox sb_notesfilter -t -c -d notesbayes -l mail.nsf -f Spambayes ! ! Replicate, then classify inbox sb_notesfilter -c -d test7 -l mail.nsf -r nynoteserv -f Spambayes ! To Do: o Dump/purge notesindex file *************** *** 159,163 **** else: firsttime = 0 ! docstomove = [] numham = 0 --- 159,163 ---- else: firsttime = 0 ! docstomove = [] numham = 0 *************** *** 165,174 **** numuns = 0 numdocs = 0 ! doc = v.GetFirstDocument() while doc: nid = doc.NOTEID if firsttime: ! notesindex[nid] = 'never classified' else: if not notesindex.has_key(nid): --- 165,174 ---- numuns = 0 numdocs = 0 ! doc = v.GetFirstDocument() while doc: nid = doc.NOTEID if firsttime: ! notesindex[nid] = 'never classified' else: if not notesindex.has_key(nid): *************** *** 182,186 **** # The com interface returns basic data types as tuples # only, thus the subscript on GetItemValue ! try: subj = doc.GetItemValue('Subject')[0] --- 182,186 ---- # The com interface returns basic data types as tuples # only, thus the subscript on GetItemValue ! try: subj = doc.GetItemValue('Subject')[0] *************** *** 230,234 **** print " %s classified as ham" % (numham) print " %s classified as unsure" % (numuns) ! def processAndTrain(v, vmoveto, bayes, is_spam, notesindex): --- 230,234 ---- print " %s classified as ham" % (numham) print " %s classified as unsure" % (numuns) ! def processAndTrain(v, vmoveto, bayes, is_spam, notesindex): *************** *** 240,244 **** print "Training %s" % (str) ! docstomove = [] doc = v.GetFirstDocument() --- 240,244 ---- print "Training %s" % (str) ! docstomove = [] doc = v.GetFirstDocument() *************** *** 253,257 **** except: body = 'No Body' ! message = "Subject: %s\r\n%s" % (subj, body) --- 253,257 ---- except: body = 'No Body' ! message = "Subject: %s\r\n%s" % (subj, body) *************** *** 270,274 **** # msg is trained as ham, is to be retrained as spam bayes.unlearn(tokens, False) ! bayes.learn(tokens, is_spam) --- 270,274 ---- # msg is trained as ham, is to be retrained as spam bayes.unlearn(tokens, False) ! bayes.learn(tokens, is_spam) *************** *** 282,286 **** print "%s documents trained" % (len(docstomove)) ! def run(bdbname, useDBM, ldbname, rdbname, foldname, doTrain, doClassify): --- 282,286 ---- print "%s documents trained" % (len(docstomove)) ! def run(bdbname, useDBM, ldbname, rdbname, foldname, doTrain, doClassify): *************** *** 302,306 **** notesindex = pickle.load(fp) fp.close() ! sess = win32com.client.Dispatch("Lotus.NotesSession") try: --- 302,306 ---- notesindex = pickle.load(fp) fp.close() ! sess = win32com.client.Dispatch("Lotus.NotesSession") try: *************** *** 309,315 **** print "Session aborted" sys.exit() ! db = sess.GetDatabase("",ldbname) ! vinbox = db.getView('($Inbox)') vspam = db.getView("%s\Spam" % (foldname)) --- 309,315 ---- print "Session aborted" sys.exit() ! db = sess.GetDatabase("",ldbname) ! vinbox = db.getView('($Inbox)') vspam = db.getView("%s\Spam" % (foldname)) *************** *** 317,331 **** vtrainspam = db.getView("%s\Train as Spam" % (foldname)) vtrainham = db.getView("%s\Train as Ham" % (foldname)) ! if doTrain: processAndTrain(vtrainspam, vspam, bayes, True, notesindex) # for some reason, using inbox as a target here loses the mail processAndTrain(vtrainham, vham, bayes, False, notesindex) ! if rdbname: print "Replicating..." db.Replicate(rdbname) print "Done" ! if doClassify: classifyInbox(vinbox, vtrainspam, bayes, ldbname, notesindex) --- 317,331 ---- vtrainspam = db.getView("%s\Train as Spam" % (foldname)) vtrainham = db.getView("%s\Train as Ham" % (foldname)) ! if doTrain: processAndTrain(vtrainspam, vspam, bayes, True, notesindex) # for some reason, using inbox as a target here loses the mail processAndTrain(vtrainham, vham, bayes, False, notesindex) ! if rdbname: print "Replicating..." db.Replicate(rdbname) print "Done" ! if doClassify: classifyInbox(vinbox, vtrainspam, bayes, ldbname, notesindex) *************** *** 339,343 **** pickle.dump(notesindex, fp) fp.close() ! if __name__ == '__main__': --- 339,343 ---- pickle.dump(notesindex, fp) fp.close() ! if __name__ == '__main__': *************** *** 390,392 **** pass else: ! print >>sys.stderr, __doc__ \ No newline at end of file --- 390,392 ---- pass else: ! print >>sys.stderr, __doc__ Index: sb_pop3dnd.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_pop3dnd.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** sb_pop3dnd.py 18 Sep 2003 03:58:59 -0000 1.3 --- sb_pop3dnd.py 16 Dec 2003 05:06:33 -0000 1.4 *************** *** 172,176 **** return self._flags_iter() ! def _flags_iter(self): if self.deleted: yield "\\DELETED" --- 172,176 ---- return self._flags_iter() ! def _flags_iter(self): if self.deleted: yield "\\DELETED" *************** *** 209,216 **** def getSubPart(self, part): """Retrieve a MIME sub-message ! @type part: C{int} @param part: The number of the part to retrieve, indexed from 0. ! @rtype: Any object implementing C{IMessage}. @return: The specified sub-part. --- 209,216 ---- def getSubPart(self, part): """Retrieve a MIME sub-message ! @type part: C{int} @param part: The number of the part to retrieve, indexed from 0. ! @rtype: Any object implementing C{IMessage}. @return: The specified sub-part. *************** *** 243,247 **** else: print "Tried to set invalid flag", flag, "to", value ! def flags(self): """Return the message flags.""" --- 243,247 ---- else: print "Tried to set invalid flag", flag, "to", value ! def flags(self): """Return the message flags.""" *************** *** 273,277 **** """Body structure data describes the MIME-IMB format of a message and consists of a sequence of mime type, mime ! subtype, parameters, content id, description, encoding, and size. The fields following the size field are variable: if the mime type/subtype is message/rfc822, the contained message's envelope --- 273,277 ---- """Body structure data describes the MIME-IMB format of a message and consists of a sequence of mime type, mime ! subtype, parameters, content id, description, encoding, and size. The fields following the size field are variable: if the mime type/subtype is message/rfc822, the contained message's envelope *************** *** 307,311 **** return s ! def body(self): rfc822 = self.as_string() bodyRE = re.compile(r"\r?\n(\r?\n)(.*)", --- 307,311 ---- return s ! def body(self): rfc822 = self.as_string() bodyRE = re.compile(r"\r?\n(\r?\n)(.*)", *************** *** 333,337 **** def string_contains(self, whole, sub): return whole.find(sub) != -1 ! def matches(self, criteria): """Return True iff the messages matches the specified IMAP --- 333,337 ---- def string_contains(self, whole, sub): return whole.find(sub) != -1 ! def matches(self, criteria): """Return True iff the messages matches the specified IMAP *************** *** 368,372 **** "SINCE" : (self.since, self.date), } ! result = True test = None --- 368,372 ---- "SINCE" : (self.since, self.date), } ! result = True test = None *************** *** 437,441 **** """Add a mailbox change listener.""" self.listeners.append(listener) ! def removeListener(self, listener): """Remove a mailbox change listener.""" --- 437,441 ---- """Add a mailbox change listener.""" self.listeners.append(listener) ! def removeListener(self, listener): """Remove a mailbox change listener.""" *************** *** 465,469 **** if msg.recent: self.recent_count += 1 ! def getUIDNext(self, increase=False): """Return the likely UID for the next message added to this --- 465,469 ---- if msg.recent: self.recent_count += 1 ! def getUIDNext(self, increase=False): """Return the likely UID for the next message added to this *************** *** 496,500 **** """Return the number of messages with the 'Unseen' flag.""" return self.unseen_count ! def isWriteable(self): """Get the read/write status of the mailbox.""" --- 496,500 ---- """Return the number of messages with the 'Unseen' flag.""" return self.unseen_count ! def isWriteable(self): """Get the read/write status of the mailbox.""" *************** *** 584,588 **** matches.append(id) break ! return matches def _messagesIter(self, messages, uid): --- 584,588 ---- matches.append(id) break ! return matches def _messagesIter(self, messages, uid): *************** *** 641,645 **** # We don't care pass ! def flagsChanged(self, newFlags): # We don't care --- 641,645 ---- # We don't care pass ! def flagsChanged(self, newFlags): # We don't care *************** *** 811,815 **** date = imaplib.Time2Internaldate(time.time())[1:-1] dest_folder.addMessage(msg, (), date) ! # We have to return something, because the client is expecting # us to. We return a short message indicating that a message --- 811,815 ---- date = imaplib.Time2Internaldate(time.time())[1:-1] dest_folder.addMessage(msg, (), date) ! # We have to return something, because the client is expecting # us to. We return a short message indicating that a message *************** *** 899,903 **** httpServer.register(serverUI) ! return app def run(): --- 899,903 ---- httpServer.register(serverUI) ! return app def run(): Index: sb_server.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/scripts/sb_server.py,v retrieving revision 1.13 retrieving revision 1.14 diff -C2 -d -r1.13 -r1.14 *** sb_server.py 14 Dec 2003 01:30:49 -0000 1.13 --- sb_server.py 16 Dec 2003 05:06:33 -0000 1.14 *************** *** 505,509 **** traceback.print_exc(None, stream) details = stream.getvalue() ! # Build the header. This will strip leading whitespace from # the lines, so we add a leading dot to maintain indentation. --- 505,509 ---- traceback.print_exc(None, stream) details = stream.getvalue() ! # Build the header. This will strip leading whitespace from # the lines, so we add a leading dot to maintain indentation. *************** *** 512,516 **** headerName = 'X-Spambayes-Exception' header = Header(dottedDetails, header_name=headerName) ! # Insert the header, converting email.Header's '\n' line # breaks to POP3's '\r\n'. --- 512,516 ---- headerName = 'X-Spambayes-Exception' header = Header(dottedDetails, header_name=headerName) ! # Insert the header, converting email.Header's '\n' line # breaks to POP3's '\r\n'. *************** *** 676,684 **** assert self.platform_mutex is None, "Should not already have the mutex" self.platform_mutex = open_platform_mutex() ! # Do whatever we've been asked to do... self.createWorkers() self.prepared = True ! def buildServerStrings(self): """After the server details have been set up, this creates string --- 676,684 ---- assert self.platform_mutex is None, "Should not already have the mutex" self.platform_mutex = open_platform_mutex() ! # Do whatever we've been asked to do... self.createWorkers() self.prepared = True ! def buildServerStrings(self): """After the server details have been set up, this creates string *************** *** 713,717 **** big = "spam" small = "ham" ! if big is not None: self.warning = "Warning: you have much more %s than %s - " \ "SpamBayes works best with approximately even " \ --- 713,717 ---- big = "spam" small = "ham" ! if big is not None: self.warning = "Warning: you have much more %s than %s - " \ "SpamBayes works best with approximately even " \ *************** *** 919,923 **** elif len(args) == 2: state.servers = [(args[0], int(args[1]))] ! # Default to listening on port 110 for command-line-specified servers. if len(args) > 0 and state.proxyPorts == []: --- 919,923 ---- elif len(args) == 2: state.servers = [(args[0], int(args[1]))] ! # Default to listening on port 110 for command-line-specified servers. if len(args) > 0 and state.proxyPorts == []: From tim_one at users.sourceforge.net Tue Dec 16 00:19:10 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:19:12 2003 Subject: [Spambayes-checkins] spambayes/spambayes classifier.py,1.14,1.15 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv14339/spambayes Modified Files: classifier.py Log Message: _enhance_wordstream(): Simplify and speed; repaired docstring; now delivers the last token in the input stream too. NOT TESTED, though. Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.14 retrieving revision 1.15 diff -C2 -d -r1.14 -r1.15 *** classifier.py 16 Dec 2003 04:59:58 -0000 1.14 --- classifier.py 16 Dec 2003 05:19:08 -0000 1.15 *************** *** 427,434 **** def _enhance_wordstream(self, wordstream): ! """Add bigrams to the wordstream. This wraps the last token ! to the first one, so a small number of odd tokens might get ! generated from that, but it shouldn't be significant. Note ! that these are *token* bigrams, and not *word* bigrams - i.e. 'synthetic' tokens get bigram'ed, too. --- 427,435 ---- def _enhance_wordstream(self, wordstream): ! """Add bigrams to the wordstream. ! ! For example, a b c -> a b "a b" c "b c" ! ! Note that these are *token* bigrams, and not *word* bigrams - i.e. 'synthetic' tokens get bigram'ed, too. *************** *** 438,453 **** If the experimental "Classifier":"x-use_bigrams" option is ! removed, this function can be removed, too.""" ! p = None ! while True: ! try: ! if p: ! yield p ! q = wordstream.next() ! if p: ! yield "%s %s" % (p, q) ! p = q ! except StopIteration: ! break def _wordinfokeys(self): --- 439,451 ---- If the experimental "Classifier":"x-use_bigrams" option is ! removed, this function can be removed, too. ! """ ! ! last = None ! for token in wordstream: ! yield token ! if last: ! yield "%s %s" % (last, token) ! last = token def _wordinfokeys(self): From tim_one at users.sourceforge.net Tue Dec 16 00:36:43 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 00:36:45 2003 Subject: [Spambayes-checkins] spambayes/spambayes classifier.py,1.15,1.16 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv17442/spambayes Modified Files: classifier.py Log Message: learn() and unlearn(): when x-use_bigrams is in effect, the *type* of wordstream shouldn't make any difference, so don't test it. In general, the only assumption about streams made in classifier.py is that they're iterable objects (generators, lists, doesn't matter). Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** classifier.py 16 Dec 2003 05:19:08 -0000 1.15 --- classifier.py 16 Dec 2003 05:36:41 -0000 1.16 *************** *** 211,216 **** else that it's definitely not spam. """ ! if options["Classifier", "x-use_bigrams"] and \ ! isinstance(wordstream, types.GeneratorType): wordstream = self._enhance_wordstream(wordstream) self._add_msg(wordstream, is_spam) --- 211,215 ---- else that it's definitely not spam. """ ! if options["Classifier", "x-use_bigrams"]: wordstream = self._enhance_wordstream(wordstream) self._add_msg(wordstream, is_spam) *************** *** 221,226 **** Pass the same arguments you passed to learn(). """ ! if options["Classifier", "x-use_bigrams"] and \ ! isinstance(wordstream, types.GeneratorType): wordstream = self._enhance_wordstream(wordstream) self._remove_msg(wordstream, is_spam) --- 220,224 ---- Pass the same arguments you passed to learn(). """ ! if options["Classifier", "x-use_bigrams"]: wordstream = self._enhance_wordstream(wordstream) self._remove_msg(wordstream, is_spam) From kennypitt at hotmail.com Tue Dec 16 10:17:16 2003 From: kennypitt at hotmail.com (Kenny Pitt) Date: Tue Dec 16 10:17:52 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionsClass.py, 1.19, 1.20 In-Reply-To: Message-ID: I was looking at this same code a little bit yesterday, and one thing struck me as odd. The get(), get_option(), and set() functions use self.conversion_table to translate the requested option, but many of the other functions such as display_name() don't. Is there a reason for that? If not, I was wondering if it would be easier to just fix get_option() for case-insensitivity and then have all the other getter functions call it instead of accessing self._options directly. Also note that the self.conversion_table translation in get() is already redundant, as it then calls get_option() which will do the exact same translation. Tony Meyer wrote: > Update of /cvsroot/spambayes/spambayes/spambayes > In directory sc8-pr-cvs1:/tmp/cvs-serv9453/spambayes > > Modified Files: > OptionsClass.py > Log Message: > Option names are always case insensitive, no matter what. > > Index: OptionsClass.py > =================================================================== > RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionsClass.py,v > retrieving revision 1.19 > retrieving revision 1.20 > diff -C2 -d -r1.19 -r1.20 > *** OptionsClass.py 15 Dec 2003 09:20:33 -0000 1.19 > --- OptionsClass.py 16 Dec 2003 04:48:28 -0000 1.20 > *************** > *** 552,586 **** > def display_name(self, sect, opt): > '''A name for the option suitable for display to a user.''' > ! return self._options[sect, opt].display_name() >[snip] > def get_option(self, sect, opt): > --- 552,586 ---- > def display_name(self, sect, opt): > '''A name for the option suitable for display to a user.''' > ! return self._options[sect, opt.lower()].display_name() >[snip] > def get_option(self, sect, opt): > *************** > *** 588,598 **** > if self.conversion_table.has_key((sect, opt)): > sect, opt = self.conversion_table[sect, opt] > ! return self._options[sect, opt] > > def get(self, sect, opt): > '''Get an option value.''' > ! if self.conversion_table.has_key((sect, opt)): > ! sect, opt = self.conversion_table[sect, opt] > ! return self.get_option(sect, opt).get() > > def __getitem__(self, key): > --- 588,598 ---- > if self.conversion_table.has_key((sect, opt)): > sect, opt = self.conversion_table[sect, opt] > ! return self._options[sect, opt.lower()] > > def get(self, sect, opt): > '''Get an option value.''' > ! if self.conversion_table.has_key((sect, opt.lower())): > ! sect, opt = self.conversion_table[sect, opt.lower()] > ! return self.get_option(sect, opt.lower()).get() > > def __getitem__(self, key): > *************** > *** 601,612 **** > def set(self, sect, opt, val=None): > '''Set an option.''' > ! if self.conversion_table.has_key((sect, opt)): > ! sect, opt = self.conversion_table[sect, opt] > if self.is_valid(sect, opt, val): > ! self._options[sect, opt].set(val) > else: > print >> sys.stderr, ("Attempted to set [%s] %s with > invalid" " value %s (%s)" % > ! (sect, opt, val, type(val))) > > def set_from_cmdline(self, arg, stream=None): > --- 601,612 ---- > def set(self, sect, opt, val=None): > '''Set an option.''' > ! if self.conversion_table.has_key((sect, opt.lower())): > ! sect, opt = self.conversion_table[sect, opt.lower()] > if self.is_valid(sect, opt, val): > ! self._options[sect, opt.lower()].set(val) > else: > print >> sys.stderr, ("Attempted to set [%s] %s with > invalid" " value %s (%s)" % > ! (sect, opt.lower(), val, > type(val))) > > def set_from_cmdline(self, arg, stream=None): -- Kenny Pitt From tim_one at users.sourceforge.net Tue Dec 16 21:05:35 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Tue Dec 16 21:05:39 2003 Subject: [Spambayes-checkins] spambayes/spambayes classifier.py,1.16,1.17 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27807/spambayes Modified Files: classifier.py Log Message: Implemented the intended "tiling" version of x-use_bigrams. Tried to restore most of the speed lost when this option *isn't* in use. Will add comments later. Anyone using x-use_bigrams needs to retrain: synthesized bigrams now begin with a "bi:" prefix. Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** classifier.py 16 Dec 2003 05:36:41 -0000 1.16 --- classifier.py 17 Dec 2003 02:05:33 -0000 1.17 *************** *** 361,404 **** def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] - if not isinstance(wordstream, types.ListType): - wordstream = list(wordstream) - - # clues is now a set so that that duplicates are removed. - # Otherwise if a token is a stronger clue than the bigrams - # it forms with the previous *and* next tokens, then it - # would be used twice. As is, it is possible for a single - # token to be present in two bigram tokens (but in different - # positions). - clues = Set() # (distance, prob, word, record) tuples - pushclue = clues.add if options["Classifier", "x-use_bigrams"]: ! # The tokens list contains 3-tuples of a token, ! # the bigram it forms with the next token, and the ! # next token. This makes it easier to select which ! # one to use later on. ! l = wordstream ! tokens = [(l[i],"%s %s" % (l[i],l[(i+1)%len(l)]), ! l[(i+1)%len(l)]) for i in xrange(len(l))] - # Run through all the triplets and add the strongest - # clue from each (note also the comment above explaining - # how duplicates are treated). - for grams in tokens: - winner = (0, None, None, None) # distance, prob, word, record - for gram in grams: - contestant = self._worddistanceget(gram) - if contestant[0] > winner[0]: - winner = contestant - if winner[0] >= mindist: - pushclue(winner) else: ! for word in wordstream: # Set() not necessary; see above. ! contestant = self._worddistanceget(word) ! if contestant[0] >= mindist: ! pushclue(contestant) - clues = list(clues) - clues.sort() if len(clues) > options["Classifier", "max_discriminators"]: del clues[0 : -options["Classifier", "max_discriminators"]] --- 361,403 ---- def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] if options["Classifier", "x-use_bigrams"]: ! raw = [] ! push = raw.append ! pair = None ! seen = {pair: 1} ! for i, token in enumerate(wordstream): ! if i: ! pair = "bi:%s %s" % (last_token, token) ! last_token = token ! for clue, indices in (token, (i,)), (pair, (i-1, i)): ! if clue not in seen: ! seen[clue] = 1 ! tup = self._worddistanceget(clue) ! if tup[0] >= mindist: ! push((tup, indices)) ! ! raw.sort() ! raw.reverse() ! clues = [] ! push = clues.append ! seen = {} ! for tup, indices in raw: ! overlap = [i for i in indices if i in seen] ! if not overlap: ! for i in indices: ! seen[i] = 1 ! push(tup) ! clues.reverse() else: ! clues = [] ! push = clues.append ! for word in Set(wordstream): ! tup = self._worddistanceget(word) ! if tup[0] >= mindist: ! push(tup) ! clues.sort() if len(clues) > options["Classifier", "max_discriminators"]: del clues[0 : -options["Classifier", "max_discriminators"]] *************** *** 444,448 **** yield token if last: ! yield "%s %s" % (last, token) last = token --- 443,447 ---- yield token if last: ! yield "bi:%s %s" % (last, token) last = token From tim_one at users.sourceforge.net Wed Dec 17 00:43:44 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Wed Dec 17 00:43:48 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.92, 1.93 classifier.py, 1.17, 1.18 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv524/spambayes Modified Files: Options.py classifier.py Log Message: Fulfilling a promise to write useful comments for the x-use_bigrams option. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.92 retrieving revision 1.93 diff -C2 -d -r1.92 -r1.93 *** Options.py 16 Dec 2003 05:06:34 -0000 1.92 --- Options.py 17 Dec 2003 05:43:42 -0000 1.93 *************** *** 429,446 **** ("x-use_bigrams", "(EXPERIMENTAL) Use mixed uni/bi-grams scheme", False, ! """Enabling this option means that SpamBayes will generate both ! unigrams (words) and bigrams (pairs of words). However, Graham's ! scheme is also used, where, for each clue triplet of two unigrams and ! the bigram they make, the clue that is strongest (i.e. has a ! probability furtherest from 0.5) is used, and the other two are not. Note that to really test this option you need to retrain with it on, so that your database includes the bigrams - if you subsequently turn ! it off, these tokens will have no effect. ! Note also that you should probably also increase the max_discriminators ! (Maximum number of extreme words) option if you enable this option; ! this may need to be doubled or quadrupled to see the benefit from the ! bigrams. This option is experimental, and may be removed in a future release. --- 429,452 ---- ("x-use_bigrams", "(EXPERIMENTAL) Use mixed uni/bi-grams scheme", False, ! """Generate both unigrams (words) and bigrams (pairs of words). ! However, extending an idea originally from Gary Robinson, the message ! is 'tiled' into non-overlapping unigrams and bigrams, approximating ! the strongest outcome over all possible tilings. Note that to really test this option you need to retrain with it on, so that your database includes the bigrams - if you subsequently turn ! it off, these tokens will have no effect. This option will at least ! double your database size given the same training data, and will ! probably at least triple it. ! You may also wish to increase the max_discriminators (maximum number ! of extreme words) option if you enable this option, perhaps doubling or ! quadrupling it. It's not yet clear. Bigrams create many more hapaxes, ! and that seems to increase the brittleness of minimalist training ! regimes; increasing max_discriminators may help to soften that effect. ! OTOH, max_discriminators defaults to 150 in part because that makes it ! easy to prove that the chi-squared math is immune from numeric ! problems. Increase it too much, and insane results will eventually ! result (including fatal floating-point exceptions on some boxes). This option is experimental, and may be removed in a future release. Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** classifier.py 17 Dec 2003 02:05:33 -0000 1.17 --- classifier.py 17 Dec 2003 05:43:42 -0000 1.18 *************** *** 359,376 **** pass def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] if options["Classifier", "x-use_bigrams"]: raw = [] push = raw.append pair = None ! seen = {pair: 1} for i, token in enumerate(wordstream): ! if i: pair = "bi:%s %s" % (last_token, token) last_token = token for clue, indices in (token, (i,)), (pair, (i-1, i)): ! if clue not in seen: seen[clue] = 1 tup = self._worddistanceget(clue) --- 359,405 ---- pass + # Return list of (prob, word, record) triples, sorted by increasing + # prob. "word" is a token from wordstream; "prob" is its spamprob (a + # float in 0.0 through 1.0); and "record" is word's associated + # WordInfo record if word is in the training database, or None if it's + # not. No more than max_discriminators items are returned, and have + # the strongest (farthest from 0.5) spamprobs of all tokens in wordstream. + # Tokens with spamprobs less than minimum_prob_strength away from 0.5 + # aren't returned. def _getclues(self, wordstream): mindist = options["Classifier", "minimum_prob_strength"] if options["Classifier", "x-use_bigrams"]: + # This scheme mixes single tokens with pairs of adjacent tokens. + # wordstream is "tiled" into non-overlapping unigrams and + # bigrams. Non-overlap is important to prevent a single original + # token from contributing to more than one spamprob returned + # (systematic correlation probably isn't a good thing). + + # First fill list raw with + # (distance, prob, word, record), indices + # pairs, one for each unigram and bigram in wordstream. + # indices is a tuple containing the indices (0-based relative to + # the start of wordstream) of the tokens that went into word. + # indices is a 1-tuple for an original token, and a 2-tuple for + # a synthesized bigram token. The indices are needed to detect + # overlap later. raw = [] push = raw.append pair = None ! # Keep track of which tokens we've already seen. ! # Don't use a Set here! This is an innermost loop, so speed is ! # important here (direct dict fiddling is much quicker than ! # invoking Python-level Set methods; in Python 2.4 that will ! # change). ! seen = {pair: 1} # so the bigram token is skipped on 1st loop trip for i, token in enumerate(wordstream): ! if i: # not the 1st loop trip, so there is a preceding token ! # This string interpolation must match the one in ! # _enhance_wordstream(). pair = "bi:%s %s" % (last_token, token) last_token = token for clue, indices in (token, (i,)), (pair, (i-1, i)): ! if clue not in seen: # as always, skip duplicates seen[clue] = 1 tup = self._worddistanceget(clue) *************** *** 378,395 **** push((tup, indices)) raw.sort() raw.reverse() clues = [] push = clues.append seen = {} for tup, indices in raw: overlap = [i for i in indices if i in seen] ! if not overlap: for i in indices: seen[i] = 1 push(tup) clues.reverse() else: clues = [] push = clues.append --- 407,431 ---- push((tup, indices)) + # Sort raw, strongest to weakest spamprob. raw.sort() raw.reverse() + # Fill clues with the strongest non-overlapping clues. clues = [] push = clues.append + # Keep track of which indices have already contributed to a + # clue in clues. seen = {} for tup, indices in raw: overlap = [i for i in indices if i in seen] ! if not overlap: # no overlap with anything already in clues for i in indices: seen[i] = 1 push(tup) + # Leave sorted from smallest to largest spamprob. clues.reverse() else: + # The all-unigram scheme just scores the tokens as-is. A Set() + # is used to weed out duplicates at high speed. clues = [] push = clues.append *************** *** 443,446 **** --- 479,484 ---- yield token if last: + # This string interpolation must match the one in + # _getclues(). yield "bi:%s %s" % (last, token) last = token From mhammond at users.sourceforge.net Wed Dec 17 01:31:59 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 17 01:32:02 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.117,1.118 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv8186 Modified Files: addin.py Log Message: Now we create a temporary mail item to create the 'Spam' field, we need to create it *before* we hook folder events, else we attempt to filter the temp message. Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.117 retrieving revision 1.118 diff -C2 -d -r1.117 -r1.118 *** addin.py 16 Dec 2003 05:06:32 -0000 1.117 --- addin.py 17 Dec 2003 06:31:57 -0000 1.118 *************** *** 1314,1317 **** --- 1314,1333 ---- folder = msgstore_folder.GetOutlookItem() name = msgstore_folder.GetFQName() + # Ensure the field is created before we hook the folder + # events, else there is a chance our event handler will + # see the temporary message we create. + try: + self.manager.EnsureOutlookFieldsForFolder(msgstore_folder.GetID()) + except: + # An exception checking that Outlook's folder has a + # 'spam' field is not fatal, nor really even worth + # telling the user about, nor even worth a traceback + # (as it is likely a COM error). + print "ERROR: Failed to check folder '%s' for " \ + "Spam field" % name + etype, value, tb = sys.exc_info() + tb = None # dont want it, and nuke circular ref + traceback.print_exception(etype, value, tb) + # now setup the hook. try: new_hook = DispatchWithEvents(folder.Items, HandlerClass) *************** *** 1322,1337 **** new_hook.Init(msgstore_folder, self.application, self.manager) new_hooks[msgstore_folder.id] = new_hook - try: - self.manager.EnsureOutlookFieldsForFolder(msgstore_folder.GetID()) - except: - # An exception checking that Outlook's folder has a - # 'spam' field is not fatal, nor really even worth - # telling the user about, nor even worth a traceback - # (as it is likely a COM error). - print "ERROR: Failed to check folder '%s' for " \ - "Spam field" % name - etype, value, tb = sys.exc_info() - tb = None # dont want it, and nuke circular ref - traceback.print_exception(etype, value, tb) print "SpamBayes: Watching for new messages in folder", name else: --- 1338,1341 ---- From mhammond at users.sourceforge.net Wed Dec 17 01:45:47 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 17 01:45:49 2003 Subject: [Spambayes-checkins] spambayes/windows/resources sb-started.ico, 1.1, 1.2 sb-stopped.ico, 1.1, 1.2 sbicon.ico, 1.1, 1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/windows/resources In directory sc8-pr-cvs1:/tmp/cvs-serv10059/resources Modified Files: sb-started.ico sb-stopped.ico sbicon.ico Log Message: Better icons and icon loading code from Kenny Pitt. Index: sb-started.ico =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/resources/sb-started.ico,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 Binary files /tmp/cvsQdZrZe and /tmp/cvsqsZ77l differ Index: sb-stopped.ico =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/resources/sb-stopped.ico,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 Binary files /tmp/cvs6m1BAh and /tmp/cvsUV5bXo differ Index: sbicon.ico =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/resources/sbicon.ico,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 Binary files /tmp/cvszFT2ui and /tmp/cvsalRL9s differ From mhammond at users.sourceforge.net Wed Dec 17 01:45:46 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Dec 17 01:45:53 2003 Subject: [Spambayes-checkins] spambayes/windows pop3proxy_tray.py,1.18,1.19 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv10059 Modified Files: pop3proxy_tray.py Log Message: Better icons and icon loading code from Kenny Pitt. Index: pop3proxy_tray.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_tray.py,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** pop3proxy_tray.py 16 Dec 2003 05:06:34 -0000 1.18 --- pop3proxy_tray.py 17 Dec 2003 06:45:44 -0000 1.19 *************** *** 170,185 **** hexe = GetModuleHandle(None) icon_flags = win32con.LR_DEFAULTSIZE ! self.hstartedicon = LoadImage(hexe, 1000, win32con.IMAGE_ICON, 0, ! 0, icon_flags) ! self.hstoppedicon = LoadImage(hexe, 1010, win32con.IMAGE_ICON, 0, ! 0, icon_flags) else: # If we have no icon we fail in all sorts of places - so may as # well make it here :) icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE ! self.hstartedicon = LoadImage(hinst, startedIconPathName, win32con.IMAGE_ICON, 0, ! 0, icon_flags) ! self.hstoppedicon = LoadImage(hinst, stoppedIconPathName, win32con.IMAGE_ICON, 0, ! 0, icon_flags) flags = NIF_ICON | NIF_MESSAGE | NIF_TIP --- 170,185 ---- hexe = GetModuleHandle(None) icon_flags = win32con.LR_DEFAULTSIZE ! self.hstartedicon = LoadImage(hexe, 1000, win32con.IMAGE_ICON, ! 16, 16, icon_flags) ! self.hstoppedicon = LoadImage(hexe, 1010, win32con.IMAGE_ICON, ! 16, 16, icon_flags) else: # If we have no icon we fail in all sorts of places - so may as # well make it here :) icon_flags = win32con.LR_LOADFROMFILE | win32con.LR_DEFAULTSIZE ! self.hstartedicon = LoadImage(hinst, startedIconPathName, win32con.IMAGE_ICON, ! 16, 16, icon_flags) ! self.hstoppedicon = LoadImage(hinst, stoppedIconPathName, win32con.IMAGE_ICON, ! 16, 16, icon_flags) flags = NIF_ICON | NIF_MESSAGE | NIF_TIP From anadelonbrin at users.sourceforge.net Wed Dec 17 03:55:07 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 17 03:55:10 2003 Subject: [Spambayes-checkins] spambayes/spambayes Corpus.py,1.12,1.13 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv29377/spambayes Modified Files: Corpus.py Log Message: Remove unused import. Index: Corpus.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Corpus.py,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** Corpus.py 16 Dec 2003 05:06:34 -0000 1.12 --- Corpus.py 17 Dec 2003 08:55:04 -0000 1.13 *************** *** 92,96 **** import sys # for output of docstring import time - from spambayes import tokenizer from spambayes.Options import options --- 92,95 ---- From anadelonbrin at users.sourceforge.net Wed Dec 17 04:09:55 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Dec 17 04:10:06 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py, 1.93, 1.94 classifier.py, 1.18, 1.19 tokenizer.py, 1.22, 1.23 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv32014/spambayes Modified Files: Options.py classifier.py tokenizer.py Log Message: Add the basis of a new experimental (and highly debatable) option to 'slurp' URLs. This is based on the urlslurper.py script in the testtools directory, which in turn was based on Richard Jowsey's URLSlurper.java. Basically, when the option is enabled, instead of just tokenizing the URLs in a message, we also retrieve the content at that address (if it's not text, we ignore it). When classifying, if the message has a 'raw' score in the unsure range, and if the number of tokens is less than max_discriminators, and adding these 'slurped' tokens would push the message into the ham/spam range, then they are used. When training, the tokens are always added (with the current setup). Improvements to this (note the XXX sections in particular) most welcome. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.93 retrieving revision 1.94 diff -C2 -d -r1.93 -r1.94 *** Options.py 17 Dec 2003 05:43:42 -0000 1.93 --- Options.py 17 Dec 2003 09:09:52 -0000 1.94 *************** *** 172,175 **** --- 172,234 ---- ), + # These options are all experimental; it seemed better to put them into + # their own category than have several interdependant experimental options. + # If this capability is removed, the entire section can go. + "URLRetriever" : ( + ("x-slurp_urls", "Tokenize text content at the end of URLs", False, + """If this option is enabled, when a message normally scores in the + 'unsure' range, and has fewer tokens than the maximum looked at, + and contains URLs, then the text at those URLs is obtained and + tokenized. If those tokens result in the message moving to a + score outside the 'unsure' range, then they are added to the + tokens for the message. This should be particularly effective + for messages that contain only a single URL and no other text.""", + BOOLEAN, RESTORE), + + ("x-cache_expiry_days", "Number of days to store URLs in cache", 7, + """This is the number of days that local cached copies of the text + at the URLs will be stored for.""", + INTEGER, RESTORE), + + ("x-cache_directory", "URL Cache Directory", "url-cache", + """So that SpamBayes doesn't need to retrieve the same URL + over and over again, it stores local copies of the text + at the end of the URL. This is the directory that will be + used for those copies.""", + PATH, RESTORE), + + ("x-only_slurp_base", "Retrieve base url", False, + """To try and speed things up, and to avoid following unique URLS, if + this option is enabled, SpamBayes will convert the URL to as basic a + form it we can. All directory information is removed and the domain + is reduced to the two (or three for those with a country TLD) top-most + elements. For example, + http://www.massey.ac.nz/~tameyer/index.html?you=me + would become + http://massey.ac.nz + and + http://id.example.com + would become http://example.com + + This should have two beneficial effects: + o It's unlikely that any information could be contained in this 'base' + url that could identify the user (unless they have a *lot* of domains). + o Many urls (both spam and ham) will strip down into the same 'base' url. + Since we have a limited form of caching, this means that a lot fewer + urls will have to be retrieved. + However, this does mean that if the 'base' url is hammy and the full is + spammy, or vice-versa, that the slurp will give back the wrong information. + Whether or not this is the case would have to be determined by testing. + """, + BOOLEAN, RESTORE), + + ("x-web_prefix", "Prefix for tokens from web pages", "", + """It may be that what is hammy/spammy for you in email isn't from + webpages. You can then set this option (to "web:", for example), + and effectively create an independent (sub)database for tokens + derived from parsing web pages.""", + r"[\S]+", RESTORE), + ), + # These options control how a message is categorized "Categorization" : ( Index: classifier.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/classifier.py,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** classifier.py 17 Dec 2003 05:43:42 -0000 1.18 --- classifier.py 17 Dec 2003 09:09:52 -0000 1.19 *************** *** 57,60 **** --- 57,62 ---- LN2 = math.log(2) # used frequently by chi-combining + slurp_wordstream = [] + PICKLE_VERSION = 5 *************** *** 201,206 **** return prob if options["Classifier", "use_chi_squared_combining"]: ! spamprob = chi2_spamprob def learn(self, wordstream, is_spam): --- 203,234 ---- return prob + def slurping_spamprob(self, wordstream, evidence=False): + """Do the standard chi-squared spamprob, but if the evidence + leaves the score in the unsure range, and we have fewer tokens + than max_discriminators, also generate tokens from the text + obtained by following http URLs in the message.""" + h_cut = options["Categorization", "ham_cutoff"] + s_cut = options["Categorization", "spam_cutoff"] + + # Get the raw score. + prob, clues = self.chi2_spamprob(wordstream, True) + + # If necessary, enhance it with the tokens from whatever is + # at the URL's destination. + if len(clues) < options["Classifier", "max_discriminators"] and \ + prob > h_cut and prob < s_cut: + sprob, sclues = self.chi2_spamprob(slurp_wordstream, True) + if sprob < h_cut or sprob > s_cut: + prob = sprob + clues = sclues + if evidence: + return prob, clues + return prob + if options["Classifier", "use_chi_squared_combining"]: ! if options["URLRetriever", "x-slurp_urls"]: ! spamprob = slurping_spamprob ! else: ! spamprob = chi2_spamprob def learn(self, wordstream, is_spam): *************** *** 213,216 **** --- 241,246 ---- if options["Classifier", "x-use_bigrams"]: wordstream = self._enhance_wordstream(wordstream) + if options["URLRetriever", "x-slurp_urls"]: + wordstream = self._add_slurped(wordstream) self._add_msg(wordstream, is_spam) *************** *** 222,225 **** --- 252,257 ---- if options["Classifier", "x-use_bigrams"]: wordstream = self._enhance_wordstream(wordstream) + if options["URLRetriever", "x-slurp_urls"]: + wordstream = self._add_slurped(wordstream) self._remove_msg(wordstream, is_spam) *************** *** 483,486 **** --- 515,527 ---- yield "bi:%s %s" % (last, token) last = token + + def _add_slurped(self, wordstream): + """Add tokens generated by 'slurping' (i.e. tokenizing + the text at the web pages pointed to by URLs in messages) + to the wordstream.""" + for token in wordstream: + yield token + for token in slurp_wordstream: + yield token def _wordinfokeys(self): Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.22 retrieving revision 1.23 diff -C2 -d -r1.22 -r1.23 *** tokenizer.py 16 Dec 2003 05:06:34 -0000 1.22 --- tokenizer.py 17 Dec 2003 09:09:52 -0000 1.23 *************** *** 19,22 **** --- 19,33 ---- from compatsets import Set + # XXX At time of writing, these are only necessary for the + # XXX experimental url retrieving/slurping code. If that + # XXX gets ripped out, either rip these out, or run + # XXX PyChecker over the code. + import sys + import socket + import pickle + import urllib2 + from spambayes import classifier + from email import message_from_string + # XXX ---- ends ---- from spambayes.Options import options *************** *** 995,999 **** return tokens ! crack_urls = URLStripper().analyze # Nuke HTML