From anadelonbrin at users.sourceforge.net Sat Mar 1 14:45:18 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sat Mar 1 17:45:22 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.57,1.58 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27999 Modified Files: pop3proxy.py Log Message: Tidying up smtpproxy related pop3proxy options to use fewer variables and be more expandable. Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.57 retrieving revision 1.58 diff -C2 -d -r1.57 -r1.58 *** pop3proxy.py 28 Feb 2003 04:25:02 -0000 1.57 --- pop3proxy.py 1 Mar 2003 22:45:16 -0000 1.58 *************** *** 498,512 **** if options.pop3proxy_strip_incoming_mailids == True: ! s = re.compile(options.pop3proxy_mailid_header_name + ': [\d-]+[\\r]?[\\n]?') messageText = s.sub('', messageText) headers, body = re.split(r'\n\r?\n', messageText, 1) messageName = state.getNewMessageName() ! if command == 'RETR' and not state.isTest \ ! and options.pop3proxy_add_mailid_header == True: ! if options.pop3proxy_mailid_as_header == True: id_header = options.pop3proxy_mailid_header_name + ": " \ + messageName + "\r\n" ! if options.pop3proxy_mailid_in_msgbody == True: body = body[:len(body)-3] + \ options.pop3proxy_mailid_header_name + ": " \ --- 498,513 ---- if options.pop3proxy_strip_incoming_mailids == True: ! s = re.compile(options.pop3proxy_mailid_header_name + \ ! ': [\d-]+[\\r]?[\\n]?') messageText = s.sub('', messageText) headers, body = re.split(r'\n\r?\n', messageText, 1) messageName = state.getNewMessageName() ! id_header = "" ! if command == 'RETR' and not state.isTest: ! if options.pop3proxy_add_mailid_to.find("header") != -1: id_header = options.pop3proxy_mailid_header_name + ": " \ + messageName + "\r\n" ! if options.pop3proxy_add_mailid_to.find("body") != -1: body = body[:len(body)-3] + \ options.pop3proxy_mailid_header_name + ": " \ From anadelonbrin at users.sourceforge.net Sat Mar 1 14:45:19 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sat Mar 1 17:45:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py,1.17,1.18 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27999/spambayes Modified Files: Options.py Log Message: Tidying up smtpproxy related pop3proxy options to use fewer variables and be more expandable. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.17 retrieving revision 1.18 diff -C2 -d -r1.17 -r1.18 *** Options.py 27 Feb 2003 04:13:32 -0000 1.17 --- Options.py 1 Mar 2003 22:45:16 -0000 1.18 *************** *** 389,398 **** pop3proxy_persistent_storage_file: hammie.db pop3proxy_notate_to: False ! pop3proxy_add_mailid_header: False pop3proxy_mailid_header_name: X-Spambayes-MailId - pop3proxy_mailid_as_header: False - pop3proxy_mailid_in_msgbody: False pop3proxy_strip_incoming_mailids: False - pop3proxy_notate_subject: False # Deprecated - use pop3proxy_servers and pop3proxy_ports instead. --- 389,398 ---- pop3proxy_persistent_storage_file: hammie.db pop3proxy_notate_to: False ! pop3proxy_notate_subject: False ! # valid options for pop3proxy_add_mailid_to include ! # "", "header", "body", and "header body" ! pop3proxy_add_mailid_to: pop3proxy_mailid_header_name: X-Spambayes-MailId pop3proxy_strip_incoming_mailids: False # Deprecated - use pop3proxy_servers and pop3proxy_ports instead. *************** *** 508,517 **** 'pop3proxy_persistent_storage_file': string_cracker, 'pop3proxy_notate_to': boolean_cracker, ! 'pop3proxy_add_mailid_header' : boolean_cracker, 'pop3proxy_mailid_header_name' : string_cracker, - 'pop3proxy_mailid_as_header' : boolean_cracker, - 'pop3proxy_mailid_in_msgbody' : boolean_cracker, 'pop3proxy_strip_incoming_mailids' : boolean_cracker, - 'pop3proxy_notate_subject': boolean_cracker, }, 'smtpproxy': {'smtpproxy_ham_address' : string_cracker, --- 508,515 ---- 'pop3proxy_persistent_storage_file': string_cracker, 'pop3proxy_notate_to': boolean_cracker, ! 'pop3proxy_notate_subject': boolean_cracker, ! 'pop3proxy_add_mailid_to' : string_cracker, 'pop3proxy_mailid_header_name' : string_cracker, 'pop3proxy_strip_incoming_mailids' : boolean_cracker, }, 'smtpproxy': {'smtpproxy_ham_address' : string_cracker, From anadelonbrin at users.sourceforge.net Sat Mar 1 14:52:08 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sat Mar 1 17:52:11 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv29815/spambayes Modified Files: OptionConfig.py Log Message: Expose options for adding a mail id to incoming mail, and options for using the SMTP proxy. Index: OptionConfig.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** OptionConfig.py 9 Feb 2003 02:25:14 -0000 1.3 --- OptionConfig.py 1 Mar 2003 22:52:06 -0000 1.4 *************** *** 47,50 **** --- 47,56 ---- 'p3ports': ('pop3proxy', 'pop3proxy_ports'), 'p3notate': ('pop3proxy', 'pop3proxy_notate_to'), + 'p3addid': ('pop3proxy', 'pop3proxy_add_mailid_to'), + 'p3stripid': ('pop3proxy', 'pop3proxy_strip_incoming_mailids'), + 'smtpservers': ('smtpproxy', 'smtpproxy_servers'), + 'smtpports': ('smtpproxy', 'smtpproxy_ports'), + 'smtpham': ('smtpproxy', 'smtpproxy_ham_address'), + 'smtpspam': ('smtpproxy', 'smtpproxy_spam_address'), } *************** *** 52,56 **** # destructive - they default to being empty, so you gain nothing by # restoring them. ! noRestore = ('pop3proxy_servers', 'pop3proxy_ports', 'pop3_notate_to') # This governs the order in which the options appear on the configurator --- 58,63 ---- # destructive - they default to being empty, so you gain nothing by # restoring them. ! noRestore = ('pop3proxy_servers', 'pop3proxy_ports', 'pop3_notate_to', ! 'smtpproxy_servers', 'smtpproxy_ports') # This governs the order in which the options appear on the configurator *************** *** 75,82 **** """Each POP3 server that is being monitored must be assigned to a 'port' in the Spambayes POP3 proxy. This port must be different for ! each monitored server, and there MUST be a port for each monitored ! server. Again, you need to configure your email client to use this ! port. If there are multiple servers, you must specify the same ! number of ports as servers, separated by commas."""), ("p3notate", "Notate To", --- 82,89 ---- """Each POP3 server that is being monitored must be assigned to a 'port' in the Spambayes POP3 proxy. This port must be different for ! each monitored server, and there must be a port for ! each monitored server. Again, you need to configure your email ! client to use this port. If there are multiple servers, you must ! specify the same number of ports as servers, separated by commas."""), ("p3notate", "Notate To", *************** *** 91,94 **** --- 98,162 ---- whatever other action is supported and appropriate for the mail classification."""), + + ("p3addid", "Add id tag", + """If you wish to be able to find a specific message (via the 'find' + box on the home page), or use the SMTP proxy to + train, you will need to know the unique id of each message. If your + mailer allows you to view all message headers, and includes all these + headers in forwarded/bounced mail, then the best place for this id + is in the headers of incoming mail. Unfortunately, some mail clients + do not offer these capabilities. For these clients, you will need to + have the id added to the body of the message. If you are not sure, + the safest option is to use both. Valid options include neither + the header nor the body (leave this blank), header only ("header"), + body only ("body"), or both ("header body")."""), + + ("p3stripid", "Strip incoming ids", + """If you receive messages from other spambayes users, you might + find that incoming mail (generally replies) already has an id, + particularly if they have set the id to appear in the body (see + above). This might confuse the SMTP proxy when it tries to identify + the message to train, and make it difficult for you to identify + the correct id to find a message. This option strips all spambayes + ids from incoming mail."""), + )), + + ("SMTP Options", + ( ("smtpservers", "Servers", + """The Spambayes SMTP proxy intercepts outgoing email - if you have + sent it to one of the addresses below, it is examined for an id and + the message corresponding to that id is trained as ham/spam. All + other mail is sent along to your outgoing mail server. You need to + specify which SMTP server(s) you wish it to intercept - a SMTP server + address typically looks like "smtp.myisp.net". If you use more than + one server, simply separate their names with commas. You can get + these server names from your existing email configuration, or from + your ISP or system administrator. If you are using Web-based email, + you can't use the Spambayes SMTP proxy (sorry!). In your email + client's configuration, where you would normally put your SMTP server + address, you should now put the address of the machine running + Spambayes."""), + + ("smtpports", "Ports", + """Each SMTP server that is being monitored must be assigned to a + 'port' in the Spambayes SMTP proxy. This port must be different for + each monitored server, and there must be a port for + each monitored server. Again, you need to configure your email + client to use this port. If there are multiple servers, you must + specify the same number of ports as servers, separated by commas."""), + + ("smtpham", "Ham Address", + """When a message is received that you wish to train on (for example, + one that was incorrectly classified), you need to forward or bounce + it to one of two special addresses so that the SMTP proxy can identify + it. If you wish to train it as ham, forward or bounce it to this + address. You will want to use an address that is not + a valid email address, like ham@nowhere.nothing."""), + + ("smtpspam", "Spam Address", + """As with Ham Address above, but the address that you need to forward + or bounce mail that you wish to train as spam. You will want to use + an address that is not a valid email address, like + spam@nowhere.nothing."""), )), From anadelonbrin at users.sourceforge.net Sun Mar 2 20:38:34 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Mar 2 23:38:37 2003 Subject: [Spambayes-checkins] spambayes smtpproxy.py,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv3694 Modified Files: smtpproxy.py Log Message: Added support for extracting an id from a message forwarded where the headers are converted to an HTML table (a la Mozilla Mail) Index: smtpproxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/smtpproxy.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** smtpproxy.py 27 Feb 2003 04:15:03 -0000 1.1 --- smtpproxy.py 3 Mar 2003 04:38:31 -0000 1.2 *************** *** 41,47 **** 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 * --- 41,47 ---- 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 * *************** *** 75,79 **** Becky! 2.05.10 Forward * Becky! 2.05.10 Redirect * ! Becky! 2.05.10 Redirect as attachment * * """ --- 75,86 ---- Becky! 2.05.10 Forward * Becky! 2.05.10 Redirect * ! Becky! 2.05.10 Redirect as attachment * * ! Mozilla Mail 1.2.1 Forward (attachment) * * ! Mozilla Mail 1.2.1 Forward (inline, plain) *1 * ! Mozilla Mail 1.2.1 Forward (inline, plain & html) *1 * ! Mozilla Mail 1.2.1 Forward (inline, html) *1 * ! ! *1 The header method will only work if auto-include original message ! is set, and if view all headers is true. """ *************** *** 84,88 **** from pop3proxy import _addressPortStr, ServerLineReader from pop3proxy import _addressAndPort, proxyListeners ! import string import socket, asyncore, asynchat --- 91,95 ---- from pop3proxy import _addressPortStr, ServerLineReader from pop3proxy import _addressAndPort, proxyListeners ! import string, re import socket, asyncore, asynchat *************** *** 390,396 **** return None else: ! id_location += len(options.pop3proxy_mailid_header_name) + 2 ! id_end = text.find('\r\n', id_location) id = text[id_location:id_end] return id --- 397,419 ---- return None else: ! # A MUA might enclose the id in a table ! # (Mozilla Mail does this with inline html) ! s = re.compile(options.pop3proxy_mailid_header_name + \ ! ':[\s]*[\s]*[\s]*') ! if s.search(text[id_location:]) is not None: ! id_location += s.search(text[id_location:]).end() ! s = re.compile('[\d-]+') ! id_end = s.search(text[id_location:]).end() + id_location ! else: ! id_location += len(options.pop3proxy_mailid_header_name) + 2 ! s = re.compile('[\w -]+[\\r]?\\n') ! id_end = s.search(text[id_location:]).end() + id_location id = text[id_location:id_end] + s = re.compile('') + if s.search(id) is not None: + id = s.split(id)[0] + s = re.compile('[\\r]?\\n') + if s.search(id) is not None: + id = s.split(id)[0] return id From mhammond at users.sourceforge.net Mon Mar 3 14:24:41 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Mar 3 17:31:04 2003 Subject: [Spambayes-checkins] website applications.ht,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/website In directory sc8-pr-cvs1:/tmp/cvs-serv31209 Modified Files: applications.ht Log Message: Remove note about needing CDO, and note that ActivePython isn't current suitable. Index: applications.ht =================================================================== RCS file: /cvsroot/spambayes/website/applications.ht,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** applications.ht 1 Nov 2002 04:50:19 -0000 1.1 --- applications.ht 3 Mar 2003 22:24:39 -0000 1.2 *************** *** 16,21 ****
  • Outlook 2000 (not Outlook Express)
  • Python's win32com ! extensions (win32all-149 or later) !
  • CDO installed. For more on this, see the README.txt or --- 16,20 ----
  • Outlook 2000 (not Outlook Express)
  • Python's win32com ! extensions (win32all-149 or later - currently ActivePython is not suitable) For more on this, see the README.txt or From mhammond at users.sourceforge.net Mon Mar 3 15:20:56 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Mar 3 18:22:13 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.48,1.49 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv25953 Modified Files: manager.py Log Message: shutil.move doesn't exist pre Python 2.3 Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.48 retrieving revision 1.49 diff -C2 -d -r1.48 -r1.49 *** manager.py 19 Feb 2003 04:18:01 -0000 1.48 --- manager.py 3 Mar 2003 23:20:24 -0000 1.49 *************** *** 6,9 **** --- 6,10 ---- import errno import shutil + import win32api, win32con import win32com.client *************** *** 26,30 **** if hasattr(sys, "frozen"): if sys.frozen == "dll": - import win32api this_filename = win32api.GetModuleFileName(sys.frozendllhandle) else: --- 27,30 ---- *************** *** 209,216 **** if os.path.isfile(src) and not os.path.isfile(dest): if do_move: ! shutil.move(src, dest) else: shutil.copyfile(src, dest) ! def FormatFolderNames(self, folder_ids, include_sub): names = [] --- 209,218 ---- if os.path.isfile(src) and not os.path.isfile(dest): if do_move: ! # shutil in 2.2 and earlier does not contain 'move' ! win32api.MoveFileEx(src, dest, ! win32con.MOVEFILE_COPY_ALLOWED) else: shutil.copyfile(src, dest) ! def FormatFolderNames(self, folder_ids, include_sub): names = [] From mhammond at users.sourceforge.net Mon Mar 3 18:07:16 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Mar 3 21:07:19 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py,1.4,1.5 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv26756 Modified Files: tokenizer.py Log Message: Fix [ 696995 ] Invalid HTML comments are not ignored Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** tokenizer.py 29 Jan 2003 03:23:35 -0000 1.4 --- tokenizer.py 4 Mar 2003 02:07:13 -0000 1.5 *************** *** 912,915 **** --- 912,918 ---- m = self.find_end(text, end) if not m: + # No matching end - act as if the open + # tag did not exist. + pushretained(text[start:]) break dummy, i = m.span() From montanaro at users.sourceforge.net Mon Mar 3 20:10:48 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Mon Mar 3 23:10:51 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv8515 Modified Files: tokenizer.py Log Message: make lemonade from lemons - lots of people are noticing bogus charset specifiers in subjects which is causing a binascii barf. Catch the exception and note that the subject charset is invalid. These two may not be the only places requiring a change. Anywhere email.Header.decode_header() is called - particularly when passed a subject or email address - should probably be guarded. Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** tokenizer.py 4 Mar 2003 02:07:13 -0000 1.5 --- tokenizer.py 4 Mar 2003 04:10:45 -0000 1.6 *************** *** 13,16 **** --- 13,17 ---- import time import os + import binascii try: from sets import Set *************** *** 834,838 **** def try_to_repair_damaged_base64(text): - import binascii i = 0 while True: --- 835,838 ---- *************** *** 1107,1111 **** # but real benefit to keeping case intact in this specific context. x = msg.get('subject', '') ! for x, subjcharset in email.Header.decode_header(x): if subjcharset is not None: yield 'subjectcharset:' + subjcharset --- 1107,1115 ---- # but real benefit to keeping case intact in this specific context. x = msg.get('subject', '') ! try: ! subjcharsetlist = email.Header.decode_header(x) ! except binascii.Error: ! subjcharsetlist = [(x, 'invalid')] ! for x, subjcharset in subjcharsetlist: if subjcharset is not None: yield 'subjectcharset:' + subjcharset *************** *** 1135,1139 **** for name, addr in email.Utils.getaddresses(addrlist): if name: ! for name, charset in email.Header.decode_header(name): yield "%s:name:%s" % (field, name.lower()) if charset is not None: --- 1139,1147 ---- for name, addr in email.Utils.getaddresses(addrlist): if name: ! try: ! subjcharsetlist = email.Header.decode_header(name) ! except binascii.Error: ! subjcharsetlist = [(name, 'invalid')] ! for name, charset in subjcharsetlist: yield "%s:name:%s" % (field, name.lower()) if charset is not None: From mhammond at users.sourceforge.net Mon Mar 3 21:41:07 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 4 00:41:10 2003 Subject: [Spambayes-checkins] spambayes/spambayes storage.py,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv4250 Modified Files: storage.py Log Message: Fix a bug that could cause incorrect word scores to be used and saved when a bsddb database is used. Consider the following: * Database loaded, with word 'x' having 0 spam, 1 ham. * unlearn message - word 'x' now has 0 spam, 0 ham. self.wordinfo['x'] is set to None * learn message called to train as spam. _wordinfoget() called for 'x' * ret = self.wordinfo.get(word) returns None as None is stored in the dictionary (ie, get did *not* return the default value) * Code sees 'None', and loads saved data from database. * 'x' with 0 spam, 1 ham loaded from database. Correct value should be 0 spam, 0 ham. This patch handles the case of None in the dict differently from no entry in the dict. This may be slightly slower, as a KeyError is thrown and caught for words not yet loaded. Another alternative would be some "sentinal" value other than None. Index: storage.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/storage.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** storage.py 28 Jan 2003 07:39:34 -0000 1.3 --- storage.py 4 Mar 2003 05:41:04 -0000 1.4 *************** *** 188,193 **** def _wordinfoget(self, word): ! ret = self.wordinfo.get(word) ! if not ret: r = self.db.get(word) if r: --- 188,198 ---- def _wordinfoget(self, word): ! # Note an explicit None in the dict means the word ! # has previously been deleted, but the DB has not been saved, ! # so therefore should not be re-fecthed. ! try: ! return self.wordinfo[word] ! except KeyError: ! ret = None r = self.db.get(word) if r: *************** *** 195,199 **** ret.__setstate__(r) self.wordinfo[word] = ret ! return ret # _wordinfoset is the same --- 200,204 ---- ret.__setstate__(r) self.wordinfo[word] = ret ! return ret # _wordinfoset is the same From mhammond at users.sourceforge.net Mon Mar 3 21:42:24 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 4 00:42:28 2003 Subject: [Spambayes-checkins] spambayes/spambayes storage.py,1.4,1.5 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv6356 Modified Files: storage.py Log Message: Remove unused 'wordcache' instance variable, and change '== None' to 'is None' Index: storage.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/storage.py,v retrieving revision 1.4 retrieving revision 1.5 diff -C2 -d -r1.4 -r1.5 *** storage.py 4 Mar 2003 05:41:04 -0000 1.4 --- storage.py 4 Mar 2003 05:42:22 -0000 1.5 *************** *** 134,138 **** classifier.Classifier.__init__(self) - self.wordcache = {} self.statekey = "saved state" self.mode = mode --- 134,137 ---- *************** *** 175,179 **** for key in self.wordinfo.keys(): val = self.wordinfo[key] ! if val == None: del self.wordinfo[key] try: --- 174,178 ---- for key in self.wordinfo.keys(): val = self.wordinfo[key] ! if val is None: del self.wordinfo[key] try: From mhammond at users.sourceforge.net Mon Mar 3 21:46:38 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 4 00:46:41 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.49,1.50 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv7425 Modified Files: addin.py Log Message: Show all tokens in the message when showing clues, and when restoring a message, ensure that we are not attempting to restore to the source! Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.49 retrieving revision 1.50 diff -C2 -d -r1.49 -r1.50 *** addin.py 14 Feb 2003 01:24:20 -0000 1.49 --- addin.py 4 Mar 2003 05:46:35 -0000 1.50 *************** *** 254,259 **** push(escape(msg.as_string(), True)) push("\n") body = ''.join(body) - new_msg.Subject = "Spam Clues: " + item.Subject # As above, use HTMLBody else Outlook refuses to behave. --- 254,276 ---- push(escape(msg.as_string(), True)) push("\n") + # Show all the tokens in the message + from spambayes.tokenizer import tokenize + from spambayes.classifier import Set # whatever classifier uses + push("

    Message Tokens:


    ") + # need to re-fetch, as the tokens we see may be different based on + # header stripping. + toks = Set(tokenize( + msgstore_message.GetEmailPackageObject(strip_mime_headers=True))) + # create a sorted list + toks = [tok for tok in toks] + toks.sort() + push("%d unique tokens
    " % len(toks)) + # Use instead of
    , as 
     is not word-wrapped by IE
    +     # However,  does not require escaping.
    +     # could use pprint, but not worth it.
    +     for token in toks:
    +         push("" + repr(token) + ", ")
    +     # Put the body together, then the rest of the message.
          body = ''.join(body)
          new_msg.Subject = "Spam Clues: " + item.Subject
          # As above, use HTMLBody else Outlook refuses to behave.
    ***************
    *** 347,352 ****
              for msgstore_message in msgstore_messages:
                  # Recover where they were moved from
                  restore_folder = msgstore_message.GetRememberedFolder()
    !             if restore_folder is None:
                      restore_folder = inbox_folder
      
    --- 364,373 ----
              for msgstore_message in msgstore_messages:
                  # Recover where they were moved from
    +             # During experimenting/playing/debugging, it is possible
    +             # that the source folder == dest folder - restore to
    +             # the inbox in this case.
                  restore_folder = msgstore_message.GetRememberedFolder()
    !             if restore_folder is None or \
    !                msgstore_message.GetFolder() == restore_folder:
                      restore_folder = inbox_folder
      
    ***************
    *** 359,363 ****
                      print "already was trained as ham"
                  # Now move it.
    -             # XXX - still don't write the source, so no point looking :(
                  msgstore_message.MoveTo(restore_folder)
                  # Note the move will possibly also trigger a re-train
    --- 380,383 ----
    
    
    
    From mhammond at users.sourceforge.net  Mon Mar  3 21:48:45 2003
    From: mhammond at users.sourceforge.net (Mark Hammond)
    Date: Tue Mar  4 00:48:48 2003
    Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.49,1.50
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/Outlook2000
    In directory sc8-pr-cvs1:/tmp/cvs-serv8090
    
    Modified Files:
    	manager.py 
    Log Message:
    Remove a return-value dance that is no longer necessary.
    
    
    Index: manager.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
    retrieving revision 1.49
    retrieving revision 1.50
    diff -C2 -d -r1.49 -r1.50
    *** manager.py	3 Mar 2003 23:20:24 -0000	1.49
    --- manager.py	4 Mar 2003 05:48:43 -0000	1.50
    ***************
    *** 429,441 ****
              """
              email = msg.GetEmailPackageObject()
    !         result = self.bayes.spamprob(bayes_tokenize(email), evidence)
    !         if evidence:
    !             score, the_evidence = result
    !         else:
    !             score = result
    !         if evidence:
    !             return score, the_evidence
    !         else:
    !             return score
      
          def ShowManager(self):
    --- 429,433 ----
              """
              email = msg.GetEmailPackageObject()
    !         return self.bayes.spamprob(bayes_tokenize(email), evidence)
      
          def ShowManager(self):
    
    
    
    From mhammond at users.sourceforge.net  Mon Mar  3 21:49:57 2003
    From: mhammond at users.sourceforge.net (Mark Hammond)
    Date: Tue Mar  4 00:50:01 2003
    Subject: [Spambayes-checkins] spambayes/Outlook2000 tester.py,1.4,1.5
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/Outlook2000
    In directory sc8-pr-cvs1:/tmp/cvs-serv8270
    
    Modified Files:
    	tester.py 
    Log Message:
    Fix a bug in the test code that used *all* ham/spam words, not just the
    top 50.  Also added some fairly rigorous tests of the word database trying
    to find my "incremental training" bug - it didn't help, but it seems 
    worthwhile keeping this test code in place.
    
    
    Index: tester.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/Outlook2000/tester.py,v
    retrieving revision 1.4
    retrieving revision 1.5
    diff -C2 -d -r1.4 -r1.5
    *** tester.py	14 Feb 2003 00:11:55 -0000	1.4
    --- tester.py	4 Mar 2003 05:49:54 -0000	1.5
    ***************
    *** 13,16 ****
    --- 13,17 ----
      from win32com.client import constants
      from time import sleep
    + import copy
      
      HAM="ham"
    ***************
    *** 66,75 ****
              if get_spam:
                  if info.hamcount==0:
    !                 items.append((info.spamcount, word))
              else:
                  if info.spamcount==0:
    !                 items.append((info.hamcount, word))
          items.sort()
    !     return [item[1] for item in items]
      
      # A little driver/manager for our tests
    --- 67,83 ----
              if get_spam:
                  if info.hamcount==0:
    !                 items.append((info.spamcount, word, info))
              else:
                  if info.spamcount==0:
    !                 items.append((info.hamcount, word, info))
          items.sort()
    !     # Throw an error if we don't have enough tokens - otherwise
    !     # the test itself may fail, which will be more confusing than this.
    !     if len(items) < num:
    !         TestFailed("Error: could not find %d words with Spam=%s - only found %d" % (num, get_spam, len(items)))
    !     ret = {}
    !     for n, word, info in items[:num]:
    !         ret[word]=copy.copy(info)
    !     return ret
      
      # A little driver/manager for our tests
    ***************
    *** 122,126 ****
      
          def CreateTestMessageInFolder(self, spam_status, folder):
    !         msg = self.CreateTestMessage(spam_status)
              msg.Save() # Put into "Drafts".
              assert self.FindTestMessage(self.folder_drafts) is not None
    --- 130,134 ----
      
          def CreateTestMessageInFolder(self, spam_status, folder):
    !         msg, words = self.CreateTestMessage(spam_status)
              msg.Save() # Put into "Drafts".
              assert self.FindTestMessage(self.folder_drafts) is not None
    ***************
    *** 128,144 ****
              msg.Move(folder)
              # And now find it in the specified folder
    !         return self.FindTestMessage(folder)
      
          def CreateTestMessage(self, spam_status):
    !         words = []
              if spam_status != SPAM:
    !             words.extend(FindTopWords(self.manager.bayes, 50, False))
              if spam_status != HAM:
    !             words.extend(FindTopWords(self.manager.bayes, 50, True))
              # Create a new blank message with our words
              msg = self.manager.outlook.CreateItem(0)
    !         msg.Body = "\n".join(words)
              msg.Subject = TEST_SUBJECT
    !         return msg
      
      # The tests themselves.
    --- 136,165 ----
              msg.Move(folder)
              # And now find it in the specified folder
    !         return self.FindTestMessage(folder), words
      
          def CreateTestMessage(self, spam_status):
    !         words = {}
              if spam_status != SPAM:
    !             words.update(FindTopWords(self.manager.bayes, 50, False))
              if spam_status != HAM:
    !             words.update(FindTopWords(self.manager.bayes, 50, True))
              # Create a new blank message with our words
              msg = self.manager.outlook.CreateItem(0)
    !         msg.Body = "\n".join(words.keys())
              msg.Subject = TEST_SUBJECT
    !         return msg, words
    ! 
    ! def check_words(words, bayes, spam_offset, ham_offset):
    !     for word, existing_info in words.items():
    !         new_info = bayes._wordinfoget(word)
    !         if existing_info.spamcount+spam_offset != new_info.spamcount or \
    !            existing_info.hamcount+ham_offset != new_info.hamcount:
    !             TestFailed("Word check for '%s failed. "
    !                        "old spam/ham=%d/%d, new spam/ham=%d/%d,"
    !                        "spam_offset=%d, ham_offset=%d" % \
    !                        (word,
    !                         existing_info.spamcount, existing_info.hamcount,
    !                         new_info.spamcount, new_info.hamcount,
    !                         spam_offset, ham_offset))
      
      # The tests themselves.
    ***************
    *** 148,155 ****
          nspam = driver.manager.bayes.nspam
          nham = driver.manager.bayes.nham
    -     import copy
          original_bayes = copy.copy(driver.manager.bayes)
          # Create a spam message in the Inbox - it should get immediately filtered
    !     msg = driver.CreateTestMessageInFolder(SPAM, driver.folder_watch)
          # sleep to ensure filtering.
          WaitForFilters()
    --- 169,175 ----
          nspam = driver.manager.bayes.nspam
          nham = driver.manager.bayes.nham
          original_bayes = copy.copy(driver.manager.bayes)
          # Create a spam message in the Inbox - it should get immediately filtered
    !     msg, words = driver.CreateTestMessageInFolder(SPAM, driver.folder_watch)
          # sleep to ensure filtering.
          WaitForFilters()
    ***************
    *** 166,169 ****
    --- 186,190 ----
          if nham != driver.manager.bayes.nham:
              TestFailed("Something caused a new ham message to appear")
    +     check_words(words, driver.manager.bayes, 0, 0)
      
          # Now move the message back to the inbox - it should get trained.
    ***************
    *** 188,191 ****
    --- 209,214 ----
              if not train.been_trained_as_ham(store_msg, driver.manager):
                  TestFailed("This new spam message should have been trained as ham now")
    +         # word infos should have one extra ham
    +         check_words(words, driver.manager.bayes, 0, 1)
              # Now move it back to the Spam folder.
              # This should see the message un-trained as ham, and re-trained as Spam
    ***************
    *** 204,207 ****
    --- 227,232 ----
              if train.been_trained_as_ham(store_msg, driver.manager):
                  TestFailed("This new spam message should have been un-trained as ham")
    +         # word infos should have one extra spam, no extra ham
    +         check_words(words, driver.manager.bayes, 1, 0)
              # Move the message to another folder, and make sure we still
              # identify it correctly as having been trained.
    ***************
    *** 216,219 ****
    --- 241,247 ----
                  TestFailed("Message was not identified as Spam after moving")
      
    +         # word infos still be 'spam'
    +         check_words(words, driver.manager.bayes, 1, 0)
    + 
              # Now undo the damage we did.
              was_spam = train.untrain_message(store_msg, driver.manager)
    ***************
    *** 233,236 ****
    --- 261,265 ----
          if nham != driver.manager.bayes.nham:
              TestFailed("Ham count didn't get back to the same")
    +     check_words(words, driver.manager.bayes, 0, 0)
      
          if driver.manager.bayes.wordinfo != original_bayes.wordinfo:
    ***************
    *** 244,248 ****
      def TestHamFilter(driver):
          # Create a spam message in the Inbox - it should get immediately filtered
    !     msg = driver.CreateTestMessageInFolder(HAM, driver.folder_watch)
          # sleep to ensure filtering.
          WaitForFilters()
    --- 273,277 ----
      def TestHamFilter(driver):
          # Create a spam message in the Inbox - it should get immediately filtered
    !     msg, words = driver.CreateTestMessageInFolder(HAM, driver.folder_watch)
          # sleep to ensure filtering.
          WaitForFilters()
    ***************
    *** 255,259 ****
      def TestUnsureFilter(driver):
          # Create a spam message in the Inbox - it should get immediately filtered
    !     msg = driver.CreateTestMessageInFolder(UNSURE, driver.folder_watch)
          # sleep to ensure filtering.
          WaitForFilters()
    --- 284,288 ----
      def TestUnsureFilter(driver):
          # Create a spam message in the Inbox - it should get immediately filtered
    !     msg, words = driver.CreateTestMessageInFolder(UNSURE, driver.folder_watch)
          # sleep to ensure filtering.
          WaitForFilters()
    
    
    
    From mhammond at users.sourceforge.net  Mon Mar  3 21:50:33 2003
    From: mhammond at users.sourceforge.net (Mark Hammond)
    Date: Tue Mar  4 00:50:36 2003
    Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py,1.17,1.18
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/Outlook2000
    In directory sc8-pr-cvs1:/tmp/cvs-serv8746
    
    Modified Files:
    	filter.py 
    Log Message:
    Only remember the source folder when actually filtering, not just scoring.
    
    
    Index: filter.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v
    retrieving revision 1.17
    retrieving revision 1.18
    diff -C2 -d -r1.17 -r1.18
    *** filter.py	14 Feb 2003 01:24:21 -0000	1.17
    --- filter.py	4 Mar 2003 05:50:31 -0000	1.18
    ***************
    *** 29,33 ****
              msg.SetField(mgr.config.field_score_name, prob)
              # and the ID of the folder we were in when scored.
    !         msg.RememberMessageCurrentFolder()
              msg.Save()
      
    --- 29,37 ----
              msg.SetField(mgr.config.field_score_name, prob)
              # and the ID of the folder we were in when scored.
    !         # (but only if we want to perform all actions)
    !         # Note we must do this, and the Save, before the
    !         # filter, else the save will fail.
    !         if all_actions:
    !             msg.RememberMessageCurrentFolder()
              msg.Save()
      
    
    
    
    From mhammond at users.sourceforge.net  Mon Mar  3 22:04:57 2003
    From: mhammond at users.sourceforge.net (Mark Hammond)
    Date: Tue Mar  4 01:05:00 2003
    Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.50,1.51
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/Outlook2000
    In directory sc8-pr-cvs1:/tmp/cvs-serv13596
    
    Modified Files:
    	manager.py 
    Log Message:
    Only attempt to create our fields on a mail item, and if no items are
    found, don't attempt to recurse folders!
    
    Fixes [ 696476 ] Manual filtering in outlook fails
    
    
    
    Index: manager.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
    retrieving revision 1.50
    retrieving revision 1.51
    diff -C2 -d -r1.50 -r1.51
    *** manager.py	4 Mar 2003 05:48:43 -0000	1.50
    --- manager.py	4 Mar 2003 06:04:54 -0000	1.51
    ***************
    *** 255,258 ****
    --- 255,264 ----
              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
    ***************
    *** 280,291 ****
                                % (folder.Name.encode("mbcs", "replace"),)
                          print "", details
              # else no items in this folder - not much worth doing!
    -         if include_sub:
    -             # Recurse down the folder list.
    -             folders = item.Folders
    -             folder = folders.GetFirst()
    -             while folder is not None:
    -                 self.EnsureOutlookFieldsForFolder(folder.EntryID, True)
    -                 folder = folders.GetNext()
      
          def LoadBayes(self):
    --- 286,297 ----
                                % (folder.Name.encode("mbcs", "replace"),)
                          print "", details
    +             if include_sub:
    +                 # Recurse down the folder list.
    +                 folders = item.Folders
    +                 folder = folders.GetFirst()
    +                 while folder is not None:
    +                     self.EnsureOutlookFieldsForFolder(folder.EntryID, True)
    +                     folder = folders.GetNext()
              # else no items in this folder - not much worth doing!
      
          def LoadBayes(self):
    
    
    
    From mhammond at users.sourceforge.net  Tue Mar  4 02:20:55 2003
    From: mhammond at users.sourceforge.net (Mark Hammond)
    Date: Tue Mar  4 05:20:58 2003
    Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.51,1.52
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/Outlook2000
    In directory sc8-pr-cvs1:/tmp/cvs-serv19965
    
    Modified Files:
    	manager.py 
    Log Message:
    Correct fix for
    [ spambayes-Bugs-697120 ] Manual filtering in Outlook (still) fails
    
    
    Index: manager.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
    retrieving revision 1.51
    retrieving revision 1.52
    diff -C2 -d -r1.51 -r1.52
    *** manager.py	4 Mar 2003 06:04:54 -0000	1.51
    --- manager.py	4 Mar 2003 10:20:53 -0000	1.52
    ***************
    *** 288,292 ****
                  if include_sub:
                      # Recurse down the folder list.
    !                 folders = item.Folders
                      folder = folders.GetFirst()
                      while folder is not None:
    --- 288,292 ----
                  if include_sub:
                      # Recurse down the folder list.
    !                 folders = item.Parent.Folders
                      folder = folders.GetFirst()
                      while folder is not None:
    
    
    
    From mhammond at users.sourceforge.net  Tue Mar  4 02:42:25 2003
    From: mhammond at users.sourceforge.net (Mark Hammond)
    Date: Tue Mar  4 05:42:29 2003
    Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.52,1.53
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/Outlook2000
    In directory sc8-pr-cvs1:/tmp/cvs-serv31737a
    
    Modified Files:
    	manager.py 
    Log Message:
    Don't ask 
    
    
    Index: manager.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v
    retrieving revision 1.52
    retrieving revision 1.53
    diff -C2 -d -r1.52 -r1.53
    *** manager.py	4 Mar 2003 10:20:53 -0000	1.52
    --- manager.py	4 Mar 2003 10:42:23 -0000	1.53
    ***************
    *** 291,295 ****
                      folder = folders.GetFirst()
                      while folder is not None:
    !                     self.EnsureOutlookFieldsForFolder(folder.EntryID, True)
                          folder = folders.GetNext()
              # else no items in this folder - not much worth doing!
    --- 291,296 ----
                      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!
    
    
    
    From jhylton at users.sourceforge.net  Tue Mar  4 10:26:02 2003
    From: jhylton at users.sourceforge.net (Jeremy Hylton)
    Date: Tue Mar  4 14:02:08 2003
    Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.58,1.59
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes
    In directory sc8-pr-cvs1:/tmp/cvs-serv4381
    
    Modified Files:
    	pop3proxy.py 
    Log Message:
    Apply Skip's suggested band-aid to decode_header().
    
    XXX There ought to be a better way.
    
    
    Index: pop3proxy.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v
    retrieving revision 1.58
    retrieving revision 1.59
    diff -C2 -d -r1.58 -r1.59
    *** pop3proxy.py	1 Mar 2003 22:45:16 -0000	1.58
    --- pop3proxy.py	4 Mar 2003 18:25:55 -0000	1.59
    ***************
    *** 140,144 ****
          import StringIO
      
    ! import os, sys, re, operator, errno, getopt, time, bisect
      import socket, asyncore, asynchat, cgi
      import mailbox, email.Header
    --- 140,144 ----
          import StringIO
      
    ! import os, sys, re, operator, errno, getopt, time, bisect, binascii
      import socket, asyncore, asynchat, cgi
      import mailbox, email.Header
    ***************
    *** 646,650 ****
              Granger's name.  8-)"""
      
    !         sections = email.Header.decode_header(field)
              field = ' '.join([text for text, unused in sections])
              if len(field) > limit:
    --- 646,653 ----
              Granger's name.  8-)"""
      
    !         try:
    !             sections = email.Header.decode_header(field)
    !         except binascii.Error:
    !             sections = [(field, None)]
              field = ' '.join([text for text, unused in sections])
              if len(field) > limit:
    
    
    
    From timstone4 at users.sourceforge.net  Thu Mar  6 06:13:24 2003
    From: timstone4 at users.sourceforge.net (Tim Stone)
    Date: Thu Mar  6 09:13:28 2003
    Subject: [Spambayes-checkins] 
    	spambayes/spambayes Options.py,1.18,1.19 OptionConfig.py,1.4,1.5
    Message-ID: 
    
    Update of /cvsroot/spambayes/spambayes/spambayes
    In directory sc8-pr-cvs1:/tmp/cvs-serv484
    
    Modified Files:
    	Options.py OptionConfig.py 
    Log Message:
    Added option to disable caching of messages per feature request 690928
    
    Index: Options.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v
    retrieving revision 1.18
    retrieving revision 1.19
    diff -C2 -d -r1.18 -r1.19
    *** Options.py	1 Mar 2003 22:45:16 -0000	1.18
    --- Options.py	6 Mar 2003 14:13:22 -0000	1.19
    ***************
    *** 390,393 ****
    --- 390,394 ----
      pop3proxy_notate_to: False
      pop3proxy_notate_subject: False
    + pop3proxy_cache_messages: True
      # valid options for pop3proxy_add_mailid_to include
      # "", "header", "body", and "header body"
    ***************
    *** 507,512 ****
                        'pop3proxy_persistent_use_database': boolean_cracker,
                        'pop3proxy_persistent_storage_file': string_cracker,
    !                   'pop3proxy_notate_to': boolean_cracker,
    !                   'pop3proxy_notate_subject': boolean_cracker,
                        'pop3proxy_add_mailid_to' : string_cracker,
                        'pop3proxy_mailid_header_name' : string_cracker,
    --- 508,514 ----
                        'pop3proxy_persistent_use_database': boolean_cracker,
                        'pop3proxy_persistent_storage_file': string_cracker,
    !                   'pop3proxy_notate_to' : boolean_cracker,
    !                   'pop3proxy_notate_subject' : boolean_cracker,
    !                   'pop3proxy_cache_messages' : boolean_cracker,
                        'pop3proxy_add_mailid_to' : string_cracker,
                        'pop3proxy_mailid_header_name' : string_cracker,
    
    Index: OptionConfig.py
    ===================================================================
    RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v
    retrieving revision 1.4
    retrieving revision 1.5
    diff -C2 -d -r1.4 -r1.5
    *** OptionConfig.py	1 Mar 2003 22:52:06 -0000	1.4
    --- OptionConfig.py	6 Mar 2003 14:13:22 -0000	1.5
    ***************
    *** 46,50 ****
          'p3servers':    ('pop3proxy',       'pop3proxy_servers'),
          'p3ports':      ('pop3proxy',       'pop3proxy_ports'),
    !     'p3notate':     ('pop3proxy',       'pop3proxy_notate_to'),
          'p3addid':      ('pop3proxy',       'pop3proxy_add_mailid_to'),
          'p3stripid':    ('pop3proxy',       'pop3proxy_strip_incoming_mailids'),
    --- 46,52 ----
          'p3servers':    ('pop3proxy',       'pop3proxy_servers'),
          'p3ports':      ('pop3proxy',       'pop3proxy_ports'),
    !     'p3notateto':   ('pop3proxy',       'pop3proxy_notate_to'),
    !     'p3notatesub':  ('pop3proxy',       'pop3proxy_notate_subject'),
    !     'p3cachemsg':   ('pop3proxy',       'pop3proxy_cache_messages'),
          'p3addid':      ('pop3proxy',       'pop3proxy_add_mailid_to'),
          'p3stripid':    ('pop3proxy',       'pop3proxy_strip_incoming_mailids'),
    ***************
    *** 87,101 ****
               specify the same number of ports as servers, separated by commas."""),
               
    !         ("p3notate", "Notate To",
    !          """Some email clients (Outlook Express, for example) can only set
    !          up filtering rules on a limited set of headers.  These clients
    !          cannot test for the existence/value of an arbitrary header and filter
    !          mail based on that information.  To accomodate these kind of mail
    !          clients, the Notate To: can be checked, which will add "spam,",
    !          "ham,", or "unsure," to the recipient list.  A filter rule can then
    !          test to see if one of these words (followed by a comma) is in the
    !          recipient list, and route the mail to an appropriate folder, or take
    !          whatever other action is supported and appropriate for the mail
    !          classification."""),
      
              ("p3addid", "Add id tag",
    --- 89,117 ----
               specify the same number of ports as servers, separated by commas."""),
               
    !         ("p3notateto", "Notate To",
    !          """Some email clients (Outlook Express, for example) can only
    !          set up filtering rules on a limited set of headers.  These
    !          clients cannot test for the existence/value of an arbitrary
    !          header and filter mail based on that information.  To
    !          accomodate these kind of mail clients, the Notate To: can be
    !          checked, which will add "spam", "ham", or "unsure" to the
    !          recipient list.  Notate Subject: will add the same to the
    !          start of the mail subject line.  A filter rule can then use
    !          to see if one of these words (followed by a comma) is in the
    !          this information in filter rules to route the mail to an
    !          appropriate folder, or take whatever other action is
    !          supported and appropriate for the mail classification."""),
    ! 
    !        ("p3notatesub", "Notate Subject",
    !          ""),
    ! 
    !        ("p3cachemsg", "Cache Messages",
    !          """You can disable the pop3proxy caching of messages.  This
    !          will make the proxy a bit faster, and make it use less space
    !          on your hard drive.  The proxy uses its cache for reviewing
    !          and training of messages, so if you disable caching you won't
    !          be able to do further training unless you re-enable it.
    !          Thus, you should only turn caching off when you are satisfied
    !          with the filtering that Spambayes is doing for you."""),
      
              ("p3addid", "Add id tag",
    ***************
    *** 385,389 ****
      
          try:
    !         nto = parms['p3notate']
          except KeyError:
              if options.pop3proxy_notate_to:
    --- 401,405 ----
      
          try:
    !         nto = parms['p3notateto']
          except KeyError:
              if options.pop3proxy_notate_to:
    ***************
    *** 394,397 ****
    --- 410,435 ----
          if not nto == "True" and not nto == "False":
              errmsg += """
  • Notate To: must be "True" or "False".
  • \n""" + + try: + nsub = parms['p3notatesub'] + except KeyError: + if options.pop3proxy_notate_sub: + nsub = "True" + else: + nsub = "False" + + if not nsub == "True" and not nsub == "False": + errmsg += """
  • Notate Subject: must be "True" or "False".
  • \n""" + + try: + cachemsg = parms['p3cachemsg'] + except KeyError: + if options.pop3proxy_cache_messages: + cachemsg = "True" + else: + cachemsg = "False" + + if not nsub == "True" and not nsub == "False": + errmsg += """
  • Cache Messages: must be "True" or "False".
  • \n""" # edit for equal number of pop3servers and ports From timstone4 at users.sourceforge.net Thu Mar 6 06:13:37 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Thu Mar 6 09:13:39 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.59,1.60 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv555 Modified Files: pop3proxy.py Log Message: Added option to disable caching of messages per feature request 690928 Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.59 retrieving revision 1.60 diff -C2 -d -r1.59 -r1.60 *** pop3proxy.py 4 Mar 2003 18:25:55 -0000 1.59 --- pop3proxy.py 6 Mar 2003 14:13:34 -0000 1.60 *************** *** 83,88 **** o "Send me a status email every [...] telling how many mails have been classified, etc." - o Possibly integrate Tim Stone's SMTP code - make it use async, make - the training code update (rather than replace!) the database. o Remove any existing X-Spambayes-Classification header from incoming emails. --- 83,86 ---- *************** *** 507,512 **** if command == 'RETR' and not state.isTest: if options.pop3proxy_add_mailid_to.find("header") != -1: ! id_header = options.pop3proxy_mailid_header_name + ": " \ ! + messageName + "\r\n" if options.pop3proxy_add_mailid_to.find("body") != -1: body = body[:len(body)-3] + \ --- 505,510 ---- if command == 'RETR' and not state.isTest: if options.pop3proxy_add_mailid_to.find("header") != -1: ! id_header = options.pop3proxy_mailid_header_name \ ! + ": " + messageName + "\r\n" if options.pop3proxy_add_mailid_to.find("body") != -1: body = body[:len(body)-3] + \ *************** *** 536,540 **** # Cache the message; don't pollute the cache with test messages. ! if command == 'RETR' and not state.isTest: # Write the message into the Unknown cache. message = state.unknownCorpus.makeMessage(messageName) --- 534,539 ---- # Cache the message; don't pollute the cache with test messages. ! if command == 'RETR' and not state.isTest \ ! and options.pop3proxy_cache_messages: # Write the message into the Unknown cache. message = state.unknownCorpus.makeMessage(messageName) From montanaro at users.sourceforge.net Thu Mar 6 07:47:25 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Mar 6 10:54:02 2003 Subject: [Spambayes-checkins] spambayes/spambayes tokenizer.py,1.6,1.7 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv10305 Modified Files: tokenizer.py Log Message: Add an extra element to the except clause which catches header parse errors. Version 2.5 of the email package intercepts the binascii.Error exception and raises email.Errors.HeaderParseError instead. Index: tokenizer.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/tokenizer.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** tokenizer.py 4 Mar 2003 04:10:45 -0000 1.6 --- tokenizer.py 6 Mar 2003 15:47:19 -0000 1.7 *************** *** 1109,1113 **** try: subjcharsetlist = email.Header.decode_header(x) ! except binascii.Error: subjcharsetlist = [(x, 'invalid')] for x, subjcharset in subjcharsetlist: --- 1109,1113 ---- try: subjcharsetlist = email.Header.decode_header(x) ! except (binascii.Error, email.Errors.HeaderParseError): subjcharsetlist = [(x, 'invalid')] for x, subjcharset in subjcharsetlist: *************** *** 1141,1145 **** try: subjcharsetlist = email.Header.decode_header(name) ! except binascii.Error: subjcharsetlist = [(name, 'invalid')] for name, charset in subjcharsetlist: --- 1141,1145 ---- try: subjcharsetlist = email.Header.decode_header(name) ! except (binascii.Error, email.Errors.HeaderParseError): subjcharsetlist = [(name, 'invalid')] for name, charset in subjcharsetlist: From montanaro at users.sourceforge.net Thu Mar 6 07:46:55 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Thu Mar 6 10:59:01 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.60,1.61 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv10103 Modified Files: pop3proxy.py Log Message: Add an extra element to the except clause which catches header parse errors. Version 2.5 of the email package intercepts the binascii.Error exception and raises email.Errors.HeaderParseError instead. Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.60 retrieving revision 1.61 diff -C2 -d -r1.60 -r1.61 *** pop3proxy.py 6 Mar 2003 14:13:34 -0000 1.60 --- pop3proxy.py 6 Mar 2003 15:46:51 -0000 1.61 *************** *** 647,651 **** try: sections = email.Header.decode_header(field) ! except binascii.Error: sections = [(field, None)] field = ' '.join([text for text, unused in sections]) --- 647,651 ---- try: sections = email.Header.decode_header(field) ! except (binascii.Error, email.Errors.HeaderParseError): sections = [(field, None)] field = ' '.join([text for text, unused in sections]) From timstone4 at users.sourceforge.net Thu Mar 6 10:39:45 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Thu Mar 6 13:39:53 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.61,1.62 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv979 Modified Files: pop3proxy.py Log Message: Added missing classification code to onClassify per bug report 698852 Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.61 retrieving revision 1.62 diff -C2 -d -r1.61 -r1.62 *** pop3proxy.py 6 Mar 2003 15:46:51 -0000 1.61 --- pop3proxy.py 6 Mar 2003 18:39:43 -0000 1.62 *************** *** 1060,1063 **** --- 1060,1064 ---- cluesRow = cluesTable.cluesRow.clone() del cluesTable.cluesRow # Delete dummy row to make way for real ones + (probability, clues) = state.bayes.spamprob(tokenizer.tokenize(message), evidence=True) for word, wordProb in clues: cluesTable += cluesRow % (cgi.escape(word), wordProb) From timstone4 at users.sourceforge.net Thu Mar 6 10:42:54 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Thu Mar 6 13:42:57 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.62,1.63 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv2295 Modified Files: pop3proxy.py Log Message: Fixed long line Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.62 retrieving revision 1.63 diff -C2 -d -r1.62 -r1.63 *** pop3proxy.py 6 Mar 2003 18:39:43 -0000 1.62 --- pop3proxy.py 6 Mar 2003 18:42:49 -0000 1.63 *************** *** 1060,1064 **** cluesRow = cluesTable.cluesRow.clone() del cluesTable.cluesRow # Delete dummy row to make way for real ones ! (probability, clues) = state.bayes.spamprob(tokenizer.tokenize(message), evidence=True) for word, wordProb in clues: cluesTable += cluesRow % (cgi.escape(word), wordProb) --- 1060,1065 ---- cluesRow = cluesTable.cluesRow.clone() del cluesTable.cluesRow # Delete dummy row to make way for real ones ! (probability, clues) = state.bayes.spamprob(tokenizer.tokenize(message),\ ! evidence=True) for word, wordProb in clues: cluesTable += cluesRow % (cgi.escape(word), wordProb) From mhammond at users.sourceforge.net Thu Mar 6 22:28:08 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Fri Mar 7 01:28:12 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.50,1.51 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv12455 Modified Files: addin.py Log Message: Don't process a message if it is None! Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.50 retrieving revision 1.51 diff -C2 -d -r1.50 -r1.51 *** addin.py 4 Mar 2003 05:46:35 -0000 1.50 --- addin.py 7 Mar 2003 06:28:06 -0000 1.51 *************** *** 183,187 **** # PR_TRANSPORT_MESSAGE_HEADERS msgstore_message = self.manager.message_store.GetMessage(item) ! ProcessMessage(msgstore_message, self.manager) # Event fired when item moved into the Spam folder. --- 183,188 ---- # PR_TRANSPORT_MESSAGE_HEADERS msgstore_message = self.manager.message_store.GetMessage(item) ! if msgstore_message is not None: ! ProcessMessage(msgstore_message, self.manager) # Event fired when item moved into the Spam folder. From mhammond at users.sourceforge.net Thu Mar 6 22:29:14 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Fri Mar 7 01:29:19 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 msgstore.py,1.40,1.41 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv12556 Modified Files: msgstore.py Log Message: Handle MAPI exceptions better. Centralize detection of "not found" exceptions, and suppress errors when the hotmail connector can't save the message. Please let me know of any weird exceptions or messages, or if there are any other problems filtering hotmail mail. Index: msgstore.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/msgstore.py,v retrieving revision 1.40 retrieving revision 1.41 diff -C2 -d -r1.40 -r1.41 *** msgstore.py 14 Feb 2003 01:24:21 -0000 1.40 --- msgstore.py 7 Mar 2003 06:29:12 -0000 1.41 *************** *** 95,98 **** --- 95,107 ---- USE_DEFERRED_ERRORS = mapi.MAPI_DEFERRED_ERRORS # or set to zero to see what changes + def IsNotFoundCOMException(exc_val): + hr, msg, exc, arg_err = exc_val + return hr in [mapi.MAPI_E_OBJECT_DELETED, mapi.MAPI_E_NOT_FOUND] + + def GetCOMExceptionString(exc_val): + hr, msg, exc, arg_err = exc_val + err_string = mapiutil.GetScodeString(hr) + return "Exception 0x%x (%s): %s" % (hr, err_string, msg) + class MAPIMsgStore(MsgStore): def __init__(self, outlook = None): *************** *** 208,218 **** folder = self._OpenEntry(folder_id) table = folder.GetContentsTable(0) ! except pythoncom.com_error, (hr, msg, exc, arg_err): # We will ignore *all* such errors for the time # being, but warn for results we don't know about. ! if hr not in [mapi.MAPI_E_OBJECT_DELETED, mapi.MAPI_E_NOT_FOUND]: print "WARNING: Unexpected MAPI error opening folder" ! print "Error:", mapiutil.GetScodeString(hr) ! print "Exception Message:", msg continue rc, props = folder.GetProps( (PR_DISPLAY_NAME_A,), 0) --- 217,226 ---- folder = self._OpenEntry(folder_id) table = folder.GetContentsTable(0) ! except pythoncom.com_error, details: # We will ignore *all* such errors for the time # being, but warn for results we don't know about. ! if not IsNotFoundCOMException(details): print "WARNING: Unexpected MAPI error opening folder" ! print GetCOMExceptionString(details) continue rc, props = folder.GetProps( (PR_DISPLAY_NAME_A,), 0) *************** *** 225,244 **** def GetFolder(self, folder_id): # Return a single folder given the ID. ! if hasattr(folder_id, "EntryID"): ! # An Outlook object ! folder_id = mapi.BinFromHex(folder_id.StoreID), \ ! mapi.BinFromHex(folder_id.EntryID) ! else: folder_id = self.NormalizeID(folder_id) try: folder = self._OpenEntry(folder_id) table = folder.GetContentsTable(0) ! except pythoncom.com_error, (hr, msg, exc, arg_err): # We will ignore *all* such errors for the time # being, but warn for results we don't know about. ! if hr not in [mapi.MAPI_E_OBJECT_DELETED, mapi.MAPI_E_NOT_FOUND]: print "WARNING: Unexpected MAPI error opening folder" ! print "Error:", mapiutil.GetScodeString(hr) ! print "Exception Message:", msg return None # Ensure we have a long-term ID. --- 233,261 ---- def GetFolder(self, folder_id): # Return a single folder given the ID. ! try: ! sid = mapi.BinFromHex(folder_id.StoreID) ! eid = mapi.BinFromHex(folder_id.EntryID) ! folder_id = sid, eid ! except AttributeError: ! # No 'EntryID'/'StoreID' properties - a 'normal' ID folder_id = self.NormalizeID(folder_id) + except pythoncom.com_error, details: + if IsNotFoundCOMException(details): + print "Unable to open folder '%r'" \ + "- the folder was not found" % folder_id + else: + print "Unexpected MAPI error opening folder" + print GetCOMExceptionString(details) + return None + # Also catch COM exceptions opening the folder and table. try: folder = self._OpenEntry(folder_id) table = folder.GetContentsTable(0) ! except pythoncom.com_error, details: # We will ignore *all* such errors for the time # being, but warn for results we don't know about. ! if not IsNotFoundCOMException(details): print "WARNING: Unexpected MAPI error opening folder" ! print GetCOMExceptionString(details) return None # Ensure we have a long-term ID. *************** *** 251,260 **** # Return a single message given either the ID, or an Outlook # message representing the object. ! if hasattr(message_id, "EntryID"): ! # An Outlook object ! message_id = mapi.BinFromHex(message_id.Parent.StoreID), \ ! mapi.BinFromHex(message_id.EntryID) ! else: message_id = self.NormalizeID(message_id) prop_ids = PR_PARENT_ENTRYID, PR_SEARCH_KEY, PR_MESSAGE_FLAGS mapi_object = self._OpenEntry(message_id) --- 268,286 ---- # Return a single message given either the ID, or an Outlook # message representing the object. ! try: ! eid = mapi.BinFromHex(message_id.EntryID) ! sid = mapi.BinFromHex(message_id.Parent.StoreID) ! message_id = sid, eid ! except AttributeError: ! # No 'EntryID'/'StoreID' properties - a 'normal' ID message_id = self.NormalizeID(message_id) + except pythoncom.com_error, details: + if IsNotFoundCOMException(details): + print "Unable to open message '%r'" \ + "- the message was not found" % message_id + else: + print "Unexpected MAPI error opening message" + print GetCOMExceptionString(details) + return None prop_ids = PR_PARENT_ENTRYID, PR_SEARCH_KEY, PR_MESSAGE_FLAGS mapi_object = self._OpenEntry(message_id) *************** *** 682,686 **** def Save(self): assert self.dirty, "asking me to save a clean message!" ! self.mapi_object.SaveChanges(mapi.KEEP_OPEN_READWRITE | USE_DEFERRED_ERRORS) self.dirty = False --- 708,718 ---- def Save(self): assert self.dirty, "asking me to save a clean message!" ! try: ! self.mapi_object.SaveChanges(mapi.KEEP_OPEN_READWRITE | USE_DEFERRED_ERRORS) ! except pythoncom.com_error, details: ! # hotmail gives this error - not sure what code, but ! # we don't want to mask other errors. ! if details[0] != -2147164169: # 0x8004dff7 ! raise self.dirty = False From mhammond at users.sourceforge.net Thu Mar 6 22:42:59 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Fri Mar 7 01:43:02 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 about.html,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv16321 Modified Files: about.html Log Message: Correct a few points. Index: about.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/about.html,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** about.html 24 Nov 2002 22:43:43 -0000 1.5 --- about.html 7 Mar 2003 06:42:57 -0000 1.6 *************** *** 56,60 **** trained as spam.  Thus, as the classifier misses spam (or is unsure about them), it learns as you correct it.
    ! If messages are dropped back into the Inbox, they are trained as good - thus, the system learns what good messages look like should it incorrectly classify it as spam or possible spam.
    --- 56,60 ---- trained as spam.  Thus, as the classifier misses spam (or is unsure about them), it learns as you correct it.
    ! If messages are dropped back into a folder being watched, they are trained as good - thus, the system learns what good messages look like should it incorrectly classify it as spam or possible spam.
    *************** *** 62,68 **** the Spam folder) and a "Recover from Spam" button in the Spam and Unsure folders.  These buttons have the same effect as the drags above. !  (Note that currently the "Recover from Spam" option will move the ! item to the Inbox - this is a bug - it should restore the message to ! the folder it was originally filtered from in the first place)

    Viewing the Spam Score Field

    A custom property named Spam --- 62,67 ---- the Spam folder) and a "Recover from Spam" button in the Spam and Unsure folders.  These buttons have the same effect as the drags above. !  (Note that currently the "Recover from Spam" option will also restore the message to ! the folder from which it was originally filtered.

    Viewing the Spam Score Field

    A custom property named Spam From montanaro at users.sourceforge.net Fri Mar 7 15:22:23 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Fri Mar 7 18:22:26 2003 Subject: [Spambayes-checkins] spambayes/spambayes .cvsignore,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv11152 Modified Files: .cvsignore Log Message: ignore .pyo files Index: .cvsignore =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/.cvsignore,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** .cvsignore 14 Jan 2003 05:38:20 -0000 1.2 --- .cvsignore 7 Mar 2003 23:22:20 -0000 1.3 *************** *** 1 **** ! *.pyc --- 1 ---- ! *.py[co] From montanaro at users.sourceforge.net Fri Mar 7 15:23:58 2003 From: montanaro at users.sourceforge.net (Skip Montanaro) Date: Fri Mar 7 18:24:01 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py,1.19,1.20 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv11885 Modified Files: Options.py Log Message: add trailing newline Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.19 retrieving revision 1.20 diff -C2 -d -r1.19 -r1.20 *** Options.py 6 Mar 2003 14:13:22 -0000 1.19 --- Options.py 7 Mar 2003 23:23:54 -0000 1.20 *************** *** 604,606 **** if not optionsPathname: ! optionsPathname = os.path.abspath('bayescustomize.ini') \ No newline at end of file --- 604,606 ---- if not optionsPathname: ! optionsPathname = os.path.abspath('bayescustomize.ini') From popiel at users.sourceforge.net Fri Mar 7 16:33:26 2003 From: popiel at users.sourceforge.net (T. Alexander Popiel) Date: Fri Mar 7 19:33:30 2003 Subject: [Spambayes-checkins] spambayes/testtools regimes.py,1.1,1.2 timcv.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/testtools In directory sc8-pr-cvs1:/tmp/cvs-serv5750 Modified Files: regimes.py timcv.py Log Message: Adding another regime, mangling python path at the top of timcv.py to make it easier for those of us without spambayes on the pythonpath... Index: regimes.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/regimes.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** regimes.py 28 Feb 2003 17:35:29 -0000 1.1 --- regimes.py 8 Mar 2003 00:33:22 -0000 1.2 *************** *** 83,84 **** --- 83,111 ---- return actual return 0 + + ### + ### This is a training regime for the incremental.py harness. + ### It does guess-based training on all messages, followed by + ### correction to perfect at the end of each group. + ### + + class expire4months: + def __init__(self): + self.ham = [[]] + self.spam = [[]] + + def group_action(self, which, test): + if len(self.ham) >= 120: + test.untrain(self.ham[119], self.spam[119]) + self.ham = self.ham[:119] + self.spam = self.spam[:119] + self.ham.insert(-1, []) + self.spam.insert(-1, []) + + def guess_action(self, which, test, guess, actual, msg): + if actual < 0: + self.spam[0].append(msg) + else: + self.ham[0].append(msg) + return actual + Index: timcv.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/timcv.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** timcv.py 17 Jan 2003 06:42:54 -0000 1.2 --- timcv.py 8 Mar 2003 00:33:23 -0000 1.3 *************** *** 49,53 **** --- 49,58 ---- from __future__ import generators + import os import sys + + sys.path.insert(-1, os.getcwd()) + sys.path.insert(-1, os.path.dirname(os.getcwd())) + from spambayes.Options import options From tim_one at users.sourceforge.net Sat Mar 8 16:54:07 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Sat Mar 8 19:54:10 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.53,1.54 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv17649 Modified Files: manager.py Log Message: SF bug: 700165: MoveFileEx doesn't exist on Win98 _MigrateFile(): MoveFileEx doesn't exist at or before Win98, so don't use it. Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.53 retrieving revision 1.54 diff -C2 -d -r1.53 -r1.54 *** manager.py 4 Mar 2003 10:42:23 -0000 1.53 --- manager.py 9 Mar 2003 00:54:04 -0000 1.54 *************** *** 204,217 **** self._MigrateFile("default_configuration.pck") def _MigrateFile(self, filename, do_move = True): src = os.path.join(self.application_directory, filename) dest = os.path.join(self.data_directory, filename) if os.path.isfile(src) and not os.path.isfile(dest): if do_move: ! # shutil in 2.2 and earlier does not contain 'move' ! win32api.MoveFileEx(src, dest, ! win32con.MOVEFILE_COPY_ALLOWED) ! else: ! shutil.copyfile(src, dest) def FormatFolderNames(self, folder_ids, include_sub): --- 204,219 ---- self._MigrateFile("default_configuration.pck") + # Copy a file from the application_directory to the data_directory. + # 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) if os.path.isfile(src) and not os.path.isfile(dest): + # shutil in 2.2 and earlier don't contain 'move'. + # Win95 and Win98 don't support MoveFileEx. + shutil.copyfile(src, dest) if do_move: ! os.remove(src) def FormatFolderNames(self, folder_ids, include_sub): From tim_one at users.sourceforge.net Sat Mar 8 17:13:00 2003 From: tim_one at users.sourceforge.net (Tim Peters) Date: Sat Mar 8 20:13:03 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.51,1.52 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv24249 Modified Files: addin.py Log Message: ShowClues(): List the unique tokens one per line. I had a hard time reading them all smushed together. Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.51 retrieving revision 1.52 diff -C2 -d -r1.51 -r1.52 *** addin.py 7 Mar 2003 06:28:06 -0000 1.51 --- addin.py 9 Mar 2003 01:12:58 -0000 1.52 *************** *** 255,258 **** --- 255,259 ---- push(escape(msg.as_string(), True)) push("
    \n") + # Show all the tokens in the message from spambayes.tokenizer import tokenize *************** *** 264,275 **** msgstore_message.GetEmailPackageObject(strip_mime_headers=True))) # create a sorted list ! toks = [tok for tok in toks] toks.sort() ! push("%d unique tokens
    " % len(toks)) # Use instead of
    , as 
     is not word-wrapped by IE
          # However,  does not require escaping.
          # could use pprint, but not worth it.
          for token in toks:
    !         push("" + repr(token) + ", ")
          # Put the body together, then the rest of the message.
          body = ''.join(body)
    --- 265,277 ----
              msgstore_message.GetEmailPackageObject(strip_mime_headers=True)))
          # create a sorted list
    !     toks = list(toks)
          toks.sort()
    !     push("%d unique tokens

    " % len(toks)) # Use instead of
    , as 
     is not word-wrapped by IE
          # However,  does not require escaping.
          # could use pprint, but not worth it.
          for token in toks:
    !         push("" + repr(token) + "
    \n") ! # Put the body together, then the rest of the message. body = ''.join(body) From mhammond at users.sourceforge.net Sun Mar 9 15:13:35 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 9 18:13:38 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 export.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv4866 Modified Files: export.py Log Message: Default 'Data' directory has changed with the old directory reorg. Index: export.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/export.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** export.py 21 Nov 2002 12:06:55 -0000 1.2 --- export.py 9 Mar 2003 23:13:31 -0000 1.3 *************** *** 99,103 **** if len(args)==0: ! directory = os.path.join(os.path.dirname(sys.argv[0]), "..\\Data") else: directory = args[0] --- 99,103 ---- if len(args)==0: ! directory = os.path.join(os.path.dirname(sys.argv[0]), "..\\spambayes\\Data") else: directory = args[0] From mhammond at users.sourceforge.net Sun Mar 9 15:21:58 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 9 18:22:01 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 export.py,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv8027 Modified Files: export.py Log Message: Doh - 'Data' is relative to 'testtools'. Index: export.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/export.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** export.py 9 Mar 2003 23:13:31 -0000 1.3 --- export.py 9 Mar 2003 23:21:54 -0000 1.4 *************** *** 99,103 **** if len(args)==0: ! directory = os.path.join(os.path.dirname(sys.argv[0]), "..\\spambayes\\Data") else: directory = args[0] --- 99,103 ---- if len(args)==0: ! directory = os.path.join(os.path.dirname(sys.argv[0]), "..\\testtools\\Data") else: directory = args[0] From anadelonbrin at users.sourceforge.net Sun Mar 9 15:55:29 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Mar 9 19:01:46 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.63,1.64 smtpproxy.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv19707 Modified Files: pop3proxy.py smtpproxy.py Log Message: Added pop3proxy_include_prob, which notes the score/prob in the classification header (off by default). While I'm here, a couple of typos and a todo in smtpproxy as well. Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.63 retrieving revision 1.64 diff -C2 -d -r1.63 -r1.64 *** pop3proxy.py 6 Mar 2003 18:42:49 -0000 1.63 --- pop3proxy.py 9 Mar 2003 23:55:27 -0000 1.64 *************** *** 72,76 **** webbrowser? o Can it cleanly dynamically update its status display while having a ! POP3 converation? Hammering reload sucks. o Save the stats (num classified, etc.) between sessions. o "Reload database" button. --- 72,76 ---- webbrowser? o Can it cleanly dynamically update its status display while having a ! POP3 conversation? Hammering reload sucks. o Save the stats (num classified, etc.) between sessions. o "Reload database" button. *************** *** 513,518 **** else: id_header = options.hammie_header_name + "-ID: Test\r\n" ! ! header = '%s: %s\r\n' % (options.hammie_header_name, disposition) headers = headers + "\n" + header + id_header + "\r\n" --- 513,522 ---- else: id_header = options.hammie_header_name + "-ID: Test\r\n" ! ! if options.pop3proxy_include_prob: ! header = '%s: %s, %s\r\n' % (options.hammie_header_name, ! disposition, prob) ! else: ! header = '%s: %s\r\n' % (options.hammie_header_name, disposition) headers = headers + "\n" + header + id_header + "\r\n" Index: smtpproxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/smtpproxy.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** smtpproxy.py 3 Mar 2003 04:38:31 -0000 1.2 --- smtpproxy.py 9 Mar 2003 23:55:27 -0000 1.3 *************** *** 28,31 **** --- 28,37 ---- todo = """ + It would be nice if spam/ham could be bulk forwarded to the proxy, + rather than one by one. This would require separating the different + messages and extracting the correct ids. Simply changing to find + *all* the ids in a message, rather than stopping after one *might* + work, but I don't really know. Richie Hindle suggested something along + these lines back in September '02. Testing: From anadelonbrin at users.sourceforge.net Sun Mar 9 15:55:30 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Mar 9 19:02:45 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py,1.20,1.21 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv19707/spambayes Modified Files: Options.py Log Message: Added pop3proxy_include_prob, which notes the score/prob in the classification header (off by default). While I'm here, a couple of typos and a todo in smtpproxy as well. Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.20 retrieving revision 1.21 diff -C2 -d -r1.20 -r1.21 *** Options.py 7 Mar 2003 23:23:54 -0000 1.20 --- Options.py 9 Mar 2003 23:55:27 -0000 1.21 *************** *** 390,393 **** --- 390,394 ---- pop3proxy_notate_to: False pop3proxy_notate_subject: False + pop3proxy_include_prob: False pop3proxy_cache_messages: True # valid options for pop3proxy_add_mailid_to include *************** *** 510,513 **** --- 511,515 ---- 'pop3proxy_notate_to' : boolean_cracker, 'pop3proxy_notate_subject' : boolean_cracker, + 'pop3proxy_include_prob' : boolean_cracker, 'pop3proxy_cache_messages' : boolean_cracker, 'pop3proxy_add_mailid_to' : string_cracker, From anadelonbrin at users.sourceforge.net Sun Mar 9 15:55:29 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Mar 9 19:06:18 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 manager.py,1.54,1.55 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv19707/Outlook2000 Modified Files: manager.py Log Message: Added pop3proxy_include_prob, which notes the score/prob in the classification header (off by default). While I'm here, a couple of typos and a todo in smtpproxy as well. Index: manager.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/manager.py,v retrieving revision 1.54 retrieving revision 1.55 diff -C2 -d -r1.54 -r1.55 *** manager.py 9 Mar 2003 00:54:04 -0000 1.54 --- manager.py 9 Mar 2003 23:55:27 -0000 1.55 *************** *** 175,179 **** def LocateDataDirectory(self): ! # Locate the best directory the our data files. from win32com.shell import shell, shellcon try: --- 175,179 ---- def LocateDataDirectory(self): ! # Locate the best directory for our data files. from win32com.shell import shell, shellcon try: From anadelonbrin at users.sourceforge.net Sun Mar 9 16:51:57 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Mar 9 19:52:01 2003 Subject: [Spambayes-checkins] spambayes/testtools timtest.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/testtools In directory sc8-pr-cvs1:/tmp/cvs-serv5742/testtools Modified Files: timtest.py Log Message: Mangle path for those without spambayes in pythonpath, like Alex's mod of testcv. Index: timtest.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/testtools/timtest.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** timtest.py 17 Jan 2003 06:42:54 -0000 1.2 --- timtest.py 10 Mar 2003 00:51:55 -0000 1.3 *************** *** 35,39 **** --- 35,43 ---- from __future__ import generators + import os import sys + + sys.path.insert(-1, os.getcwd()) + sys.path.insert(-1, os.path.dirname(os.getcwd())) from spambayes.Options import options From timstone4 at users.sourceforge.net Sun Mar 9 20:53:21 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Sun Mar 9 23:53:25 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv31265 Modified Files: OptionConfig.py Log Message: Added configuration for pop3proxy_include_prob option, and edits for pop3proxy_add_mailid_to and pop3proxy_strip_incoming_mailids options. Index: OptionConfig.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** OptionConfig.py 6 Mar 2003 14:13:22 -0000 1.5 --- OptionConfig.py 10 Mar 2003 04:53:15 -0000 1.6 *************** *** 51,54 **** --- 51,55 ---- 'p3addid': ('pop3proxy', 'pop3proxy_add_mailid_to'), 'p3stripid': ('pop3proxy', 'pop3proxy_strip_incoming_mailids'), + 'p3prob': ('pop3proxy', 'pop3proxy_include_prob'), 'smtpservers': ('smtpproxy', 'smtpproxy_servers'), 'smtpports': ('smtpproxy', 'smtpproxy_ports'), *************** *** 136,139 **** --- 137,147 ---- the correct id to find a message. This option strips all spambayes ids from incoming mail."""), + + ("p3prob", "Add spam probability header", + """You can have spambayes insert a header with the calculated spam + probability into each mail. If you can view headers with your + mailer, then you can see this information, which can be interesting + and even instructive if you're a serious spambayes junkie."""), + )), *************** *** 430,436 **** cachemsg = "False" ! if not nsub == "True" and not nsub == "False": errmsg += """
  • Cache Messages: must be "True" or "False".
  • \n""" # edit for equal number of pop3servers and ports try: --- 438,477 ---- cachemsg = "False" ! if not cachemsg == "True" and not cachemsg == "False": errmsg += """
  • Cache Messages: must be "True" or "False".
  • \n""" + try: + prob = parms['p3prob'] + except KeyError: + if options.pop3proxy_include_prob: + prob = "True" + else: + prob = "False" + + if not prob == "True" and not prob == "False": + errmsg += """
  • Add Spam Probability Header: must be "True" or "False".
  • \n""" + + try: + aid = parms['p3addid'] + except KeyError: + if options.pop3proxy_add_mailid_to: + aid = "True" + else: + aid = "False" + + if not aid == "True" and not aid == "False": + errmsg += """
  • Add Id Tag: must be "True" or "False".
  • \n""" + + try: + sid = parms['p3stripid'] + except KeyError: + if options.pop3proxy_strip_incoming_mailids: + sid = "True" + else: + sid = "False" + + if not sid == "True" and not sid == "False": + errmsg += """
  • Strip Incoming Ids: must be "True" or "False".
  • \n""" + # edit for equal number of pop3servers and ports try: From T.A.Meyer at massey.ac.nz Tue Mar 11 12:41:33 2003 From: T.A.Meyer at massey.ac.nz (Meyer, Tony) Date: Mon Mar 10 18:53:47 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.5,1.6 Message-ID: <1ED4ECF91CDED24C8D012BCF2B034F13C8C899@its-xchg4.massey.ac.nz> > Log Message: > Added configuration for pop3proxy_include_prob option, and edits for > pop3proxy_add_mailid_to and pop3proxy_strip_incoming_mailids options. Thanks Tim. I always forget about the OptionsConfig since I just work with the config files themselves. I had the edits for add_mailid_to and strip_incoming_mailids on my todo list, but hadn't got to it. BTW, is there any reason that the options config page looks like it does? It seems to me that if it could handle checkboxes and radio buttons for certain options that would be nicer than having string values of "True" and "False" and so on. I can play around with this if it is a good idea. Cheers, Tony From tim at fourstonesExpressions.com Mon Mar 10 18:08:54 2003 From: tim at fourstonesExpressions.com (Tim Stone - Four Stones Expressions) Date: Mon Mar 10 19:09:01 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.5,1.6 In-Reply-To: <1ED4ECF91CDED24C8D012BCF2B034F13C8C899@its-xchg4.massey.ac.nz> Message-ID: 3/10/2003 5:41:33 PM, "Meyer, Tony" wrote: > >BTW, is there any reason that the options config page looks like it does? It seems to me that if it could handle checkboxes and radio buttons for certain options that would be nicer than having string values of "True" and "False" and so on. I can play around with this if it is a good idea. > Ya, I hate it the way it is, but it was easier to do it that way than to figure out Dibbler at the time... Radio buttons for true and false would be great, and also the help text should probably be a popup window or something. It just clutters up the screen as it is... On another note, I think the spamprob should probably be a different header, rather than added to the spambayes classification header. That would make it more consistent with hammie, which has a 'debug' header. Also did you read the post on having a 10* bargraph for spamprob? It's from Skip, see what you think about that. Seems simple enough to do... but I'm not quite sure I understand why... This requirement has been presented before... So I propose the following headers: X-Spambayes-Classification: {Ham|Spam|Unsure} X-Spambayes-Spam-Probability: 0.981231492 X-Spambayes-Evidence: {clues list with probs here, like hammie} X-Spambayes-Level: ******* (one * per 10%) c'est moi - TimS http://www.fourstonesExpressions.com http://wecanstopspam.org From tim at fourstonesExpressions.com Mon Mar 10 18:22:26 2003 From: tim at fourstonesExpressions.com (Tim Stone - Four Stones Expressions) Date: Mon Mar 10 19:22:32 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.5,1.6 In-Reply-To: <1ED4ECF91CDED24C8D012BCF2B034F13C8C89B@its-xchg4.massey.ac.nz> Message-ID: <1UZUUSJVQSP2V42GBK6320621362.3e6d2c42@myst> 3/10/2003 6:12:44 PM, "Meyer, Tony" wrote: >I'll try to work on this when I get a chance. It really should be simple. If I had more life bandwidth, it'd be done by now. >> Also did you read the post on having a 10* bargraph for >> spamprob? > >Yup. Almost finished it :) DUDE! >> X-Spambayes-Evidence: {clues list with probs here, like hammie} > >Pop3proxy doesn't have anything like this at the moment, does it? I'll work on this once I've done the others. Nothing like this. You can yank the code from hammie.py formatclues function. c'est moi - TimS http://www.fourstonesExpressions.com http://wecanstopspam.org From T.A.Meyer at massey.ac.nz Tue Mar 11 13:12:44 2003 From: T.A.Meyer at massey.ac.nz (Meyer, Tony) Date: Mon Mar 10 19:29:02 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.5,1.6 Message-ID: <1ED4ECF91CDED24C8D012BCF2B034F13C8C89B@its-xchg4.massey.ac.nz> > Ya, I hate it the way it is, but it was easier to do it that > way than to > figure out Dibbler at the time... Radio buttons for true and > false would be > great, and also the help text should probably be a popup > window or something. > It just clutters up the screen as it is... I'll try to work on this when I get a chance. > On another note, I think the spamprob should probably be a > different header, rather than added to the spambayes > classification header. That would make it > more consistent with hammie, which has a 'debug' header. Ok then. I wasn't sure at the time, so went with the existing one. I'll change this now. > Also did you read the post on having a 10* bargraph for > spamprob? Yup. Almost finished it :) > So I propose the following headers: > X-Spambayes-Classification: {Ham|Spam|Unsure} > X-Spambayes-Spam-Probability: 0.981231492 > X-Spambayes-Level: ******* (one * per 10%) These will be done in a moment. > X-Spambayes-Evidence: {clues list with probs here, like hammie} Pop3proxy doesn't have anything like this at the moment, does it? I'll work on this once I've done the others. Cheers, Tony From anadelonbrin at users.sourceforge.net Mon Mar 10 17:51:08 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Mar 10 20:51:12 2003 Subject: [Spambayes-checkins] spambayes INTEGRATION.txt,1.6,1.7 README.txt,1.46,1.47 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv9660 Modified Files: INTEGRATION.txt README.txt Log Message: Update documentation to include smtpproxy. Index: INTEGRATION.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/INTEGRATION.txt,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** INTEGRATION.txt 30 Jan 2003 01:41:53 -0000 1.6 --- INTEGRATION.txt 11 Mar 2003 01:51:05 -0000 1.7 *************** *** 246,249 **** --- 246,269 ---- can train on individual messages (which is tedious) or using mbox files. + An alternative to training via the web interface is to run the SMTP proxy. + Just as the POP3 proxy sits between your mail client and your POP3 server, + the SMTP proxy sits between your mail client and your SMTP server. To run + the server, start pop3proxy with the "-s" switch. You will need to setup + your mail client just as with the POP3 proxy - change the outgoing mail + (SMTP) server to localhost (or if you are running pop3proxy on a different + machine, replace localhost with the name of the machine). In the web + interface, set the SMTP options to the address and port of your SMTP + server. You will also need to set the "add mail id to" option to "header". + To train, you can now forward or bounce mail to spambayes_ham@localhost, + or spambayes_spam@localhost (you can change these addresses via the web + interface). + + Note that some mail clients (particularly Outlook Express) do not forward + all headers when you bounce, forward or redirect mail. For these clients, + you will need to set (via the web interface) the "add mail id to" option + to body, which will add a unique id to the body of each message you + receive. You can also use this id to find a particular message via the + web interface. + Procmail filtering Index: README.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/README.txt,v retrieving revision 1.46 retrieving revision 1.47 diff -C2 -d -r1.46 -r1.47 *** README.txt 14 Feb 2003 02:38:57 -0000 1.46 --- README.txt 11 Mar 2003 01:51:05 -0000 1.47 *************** *** 95,98 **** --- 95,105 ---- a separate module. + smtpproxy.py + A message training SMTP proxy. It sits between your email client and + your SMTP server and intercepts mail to set ham and spam addresses. + A unique spambayes id is extracted from the message and it is + (re)trained appropriately. All other mail is simply passed through + to the SMTP server. + mailsort.py A delivery agent that uses a CDB of word probabilities and delivers From anadelonbrin at users.sourceforge.net Mon Mar 10 18:48:32 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Mar 10 21:48:35 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.64,1.65 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27657 Modified Files: pop3proxy.py Log Message: Adding capability for level and evidence headers in pop3proxy. Also changes prob/score header so that it is on a separate header, not with the classification. Tidies up the whole extra header section in pop3proxy. Fixed the check for correct add_mailid in OptionConfig. Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.64 retrieving revision 1.65 diff -C2 -d -r1.64 -r1.65 *** pop3proxy.py 9 Mar 2003 23:55:27 -0000 1.64 --- pop3proxy.py 11 Mar 2003 02:48:29 -0000 1.65 *************** *** 481,485 **** # Now find the spam disposition and add the header. ! prob = state.bayes.spamprob(tokenizer.tokenize(messageText)) if prob < options.ham_cutoff: disposition = options.header_ham_string --- 481,487 ---- # Now find the spam disposition and add the header. ! (prob, clues) = state.bayes.spamprob\ ! (tokenizer.tokenize(messageText), ! evidence=True) if prob < options.ham_cutoff: disposition = options.header_ham_string *************** *** 502,509 **** headers, body = re.split(r'\n\r?\n', messageText, 1) messageName = state.getNewMessageName() ! id_header = "" if command == 'RETR' and not state.isTest: if options.pop3proxy_add_mailid_to.find("header") != -1: ! id_header = options.pop3proxy_mailid_header_name \ + ": " + messageName + "\r\n" if options.pop3proxy_add_mailid_to.find("body") != -1: --- 504,512 ---- headers, body = re.split(r'\n\r?\n', messageText, 1) messageName = state.getNewMessageName() ! headers += '\r\n%s: %s\r\n' % (options.hammie_header_name, ! disposition) if command == 'RETR' and not state.isTest: if options.pop3proxy_add_mailid_to.find("header") != -1: ! headers += options.pop3proxy_mailid_header_name \ + ": " + messageName + "\r\n" if options.pop3proxy_add_mailid_to.find("body") != -1: *************** *** 512,523 **** + messageName + "\r\n.\r\n" else: ! id_header = options.hammie_header_name + "-ID: Test\r\n" if options.pop3proxy_include_prob: ! header = '%s: %s, %s\r\n' % (options.hammie_header_name, ! disposition, prob) ! else: ! header = '%s: %s\r\n' % (options.hammie_header_name, disposition) ! headers = headers + "\n" + header + id_header + "\r\n" if options.pop3proxy_notate_to \ --- 515,536 ---- + messageName + "\r\n.\r\n" else: ! headers += options.hammie_header_name + "-ID: Test\r\n" if options.pop3proxy_include_prob: ! headers += '%s: %s\r\n' % (options.pop3proxy_prob_header_name, ! prob) ! if options.pop3proxy_include_thermostat: ! thermostat = '**********' ! headers += '%s: %s\r\n' % \ ! (options.pop3proxy_thermostat_header_name, ! thermostat[int(prob*10):]) ! if options.pop3proxy_include_evidence: ! headers += options.pop3proxy_evidence_header_name + ": " ! headers += "; ".join(["%r: %.2f" % (word, prob) ! for word, score in clues ! if (word[0] == '*' or ! score <= options.clue_mailheader_cutoff or ! score >= 1.0 - options.clue_mailheader_cutoff)]) ! headers += "\r\n" if options.pop3proxy_notate_to \ From anadelonbrin at users.sourceforge.net Mon Mar 10 18:48:32 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Mar 10 21:48:37 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.6,1.7 Options.py,1.21,1.22 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27657/spambayes Modified Files: OptionConfig.py Options.py Log Message: Adding capability for level and evidence headers in pop3proxy. Also changes prob/score header so that it is on a separate header, not with the classification. Tidies up the whole extra header section in pop3proxy. Fixed the check for correct add_mailid in OptionConfig. Index: OptionConfig.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** OptionConfig.py 10 Mar 2003 04:53:15 -0000 1.6 --- OptionConfig.py 11 Mar 2003 02:48:29 -0000 1.7 *************** *** 10,13 **** --- 10,14 ---- To Do: + o Replace some of the test options with radio buttons/checkboxes. o Suggestions? *************** *** 52,55 **** --- 53,58 ---- 'p3stripid': ('pop3proxy', 'pop3proxy_strip_incoming_mailids'), 'p3prob': ('pop3proxy', 'pop3proxy_include_prob'), + 'p3thermostat': ('pop3proxy', 'pop3proxy_include_thermostat'), + 'p3evidence': ('pop3proxy', 'pop3proxy_include_evidence'), 'smtpservers': ('smtpproxy', 'smtpproxy_servers'), 'smtpports': ('smtpproxy', 'smtpproxy_ports'), *************** *** 97,109 **** accomodate these kind of mail clients, the Notate To: can be checked, which will add "spam", "ham", or "unsure" to the ! recipient list. Notate Subject: will add the same to the ! start of the mail subject line. A filter rule can then use ! to see if one of these words (followed by a comma) is in the ! this information in filter rules to route the mail to an ! appropriate folder, or take whatever other action is ! supported and appropriate for the mail classification."""), ("p3notatesub", "Notate Subject", ! ""), ("p3cachemsg", "Cache Messages", --- 100,112 ---- accomodate these kind of mail clients, the Notate To: can be checked, which will add "spam", "ham", or "unsure" to the ! recipient list. A filter rule can then use this to see if ! one of these words (followed by a comma) is in the recipient ! list, and route the mail to an appropriate folder, or take ! whatever other action is supported and appropriate for the ! mail classification."""), ("p3notatesub", "Notate Subject", ! """This option will add the same information as Notate to:, ! but to the start of the mail subject line."""), ("p3cachemsg", "Cache Messages", *************** *** 143,146 **** --- 146,163 ---- mailer, then you can see this information, which can be interesting and even instructive if you're a serious spambayes junkie."""), + + ("p3thermostat", "Add spam level header", + """You can have spambayes insert a header with the calculated spam + probability, expressed as a number of '*'s, into each mail (the more + '*'s, the higher the probability it is spam). If your mailer + supports it, you can use this information to fine tune your + classification of ham/spam, ignoring the classification given."""), + + ("p3evidence", "Add evidence header", + """You can have spambayes insert a header into mail, with the + evidence that it used to classify that message (a collection of + words with ham and spam probabilities). If you can view headers + with your mailer, then this may give you some insight as to why + a particular message was scored in a particular way."""), )), *************** *** 453,465 **** try: ! aid = parms['p3addid'] except KeyError: ! if options.pop3proxy_add_mailid_to: ! aid = "True" else: ! aid = "False" ! if not aid == "True" and not aid == "False": ! errmsg += """
  • Add Id Tag: must be "True" or "False".
  • \n""" try: --- 470,504 ---- try: ! prob = parms['p3thermostat'] except KeyError: ! if options.pop3proxy_include_thermostat: ! prob = "True" else: ! prob = "False" ! if not prob == "True" and not prob == "False": ! errmsg += """
  • Add Spam Level Header: must be "True" or "False".
  • \n""" ! ! try: ! prob = parms['p3evidence'] ! except KeyError: ! if options.pop3proxy_include_evidence: ! prob = "True" ! else: ! prob = "False" ! ! if not prob == "True" and not prob == "False": ! errmsg += """
  • Add Spam Evidence Header: must be "True" or "False".
  • \n""" ! ! try: ! aid = parms['p3addid'] ! except KeyError: ! aid = options.pop3proxy_add_mailid_to ! ! if not aid == "" and not aid == "body" \ ! and not aid == "header" and not aid == "body header" \ ! and not aid == "header body": ! errmsg += """
  • Add Id Tag: must be "", ! "body", "header", "body header", or "header body".
  • \n""" try: Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.21 retrieving revision 1.22 diff -C2 -d -r1.21 -r1.22 *** Options.py 9 Mar 2003 23:55:27 -0000 1.21 --- Options.py 11 Mar 2003 02:48:30 -0000 1.22 *************** *** 391,394 **** --- 391,399 ---- pop3proxy_notate_subject: False pop3proxy_include_prob: False + pop3proxy_prob_header_name: X-Spambayes-Spam-Probability + pop3proxy_include_thermostat: False + pop3proxy_thermostat_header_name: X-Spambayes-Level + pop3proxy_include_evidence: False + pop3proxy_evidence_header_name: X-Spambayes-Evidence pop3proxy_cache_messages: True # valid options for pop3proxy_add_mailid_to include *************** *** 512,515 **** --- 517,525 ---- 'pop3proxy_notate_subject' : boolean_cracker, 'pop3proxy_include_prob' : boolean_cracker, + 'pop3proxy_prob_header_name' : string_cracker, + 'pop3proxy_include_thermostat' : boolean_cracker, + 'pop3proxy_thermostat_header_name' : string_cracker, + 'pop3proxy_include_evidence' : boolean_cracker, + 'pop3proxy_evidence_header_name' : string_cracker, 'pop3proxy_cache_messages' : boolean_cracker, 'pop3proxy_add_mailid_to' : string_cracker, From timstone4 at users.sourceforge.net Tue Mar 11 05:40:17 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Tue Mar 11 08:40:20 2003 Subject: [Spambayes-checkins] spambayes dbExpImp.py,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv3657 Modified Files: dbExpImp.py Log Message: Corrected generators import placement Index: dbExpImp.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/dbExpImp.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** dbExpImp.py 28 Feb 2003 18:05:58 -0000 1.5 --- dbExpImp.py 11 Mar 2003 13:40:14 -0000 1.6 *************** *** 90,93 **** --- 90,95 ---- __author__ = "Tim Stone " + from __future__ import generators + try: True, False *************** *** 96,101 **** True, False = 1, 0 - from __future__ import generators - import spambayes.storage from spambayes.Options import options --- 98,101 ---- From anadelonbrin at users.sourceforge.net Tue Mar 11 19:30:10 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Tue Mar 11 22:30:14 2003 Subject: [Spambayes-checkins] spambayes pop3proxy.py,1.65,1.66 smtpproxy.py,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv19736 Modified Files: pop3proxy.py smtpproxy.py Log Message: Fixes a bug where headers will appear in the message body. Thanks to Tony Lownds for pointing it (my mistake) out. Index: pop3proxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/pop3proxy.py,v retrieving revision 1.65 retrieving revision 1.66 diff -C2 -d -r1.65 -r1.66 *** pop3proxy.py 11 Mar 2003 02:48:29 -0000 1.65 --- pop3proxy.py 12 Mar 2003 03:29:58 -0000 1.66 *************** *** 504,508 **** headers, body = re.split(r'\n\r?\n', messageText, 1) messageName = state.getNewMessageName() ! headers += '\r\n%s: %s\r\n' % (options.hammie_header_name, disposition) if command == 'RETR' and not state.isTest: --- 504,508 ---- headers, body = re.split(r'\n\r?\n', messageText, 1) messageName = state.getNewMessageName() ! headers += '\n%s: %s\r\n' % (options.hammie_header_name, disposition) if command == 'RETR' and not state.isTest: Index: smtpproxy.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/smtpproxy.py,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** smtpproxy.py 9 Mar 2003 23:55:27 -0000 1.3 --- smtpproxy.py 12 Mar 2003 03:30:01 -0000 1.4 *************** *** 28,38 **** todo = """ ! It would be nice if spam/ham could be bulk forwarded to the proxy, ! rather than one by one. This would require separating the different ! messages and extracting the correct ids. Simply changing to find ! *all* the ids in a message, rather than stopping after one *might* ! work, but I don't really know. Richie Hindle suggested something along ! these lines back in September '02. ! Testing: --- 28,44 ---- todo = """ ! o It would be nice if spam/ham could be bulk forwarded to the proxy, ! rather than one by one. This would require separating the different ! messages and extracting the correct ids. Simply changing to find ! *all* the ids in a message, rather than stopping after one *might* ! work, but I don't really know. Richie Hindle suggested something along ! these lines back in September '02. ! ! o It would be nice if the proxy could email a confirmation message ! back to the mail client. Something along the lines of "yes, I ! trained message id xxx with subject xxx as sp/ham". This would ! be an option, of course, and it would be nice to be able to set ! it to be sent every time, or as a x days/hours digest. ! Testing: From mhammond at users.sourceforge.net Wed Mar 12 05:19:19 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Mar 12 08:19:22 2003 Subject: [Spambayes-checkins] spambayes/windows - New directory Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv14573/windows Log Message: Directory /cvsroot/spambayes/spambayes/windows added to the repository From mhammond at users.sourceforge.net Wed Mar 12 05:22:46 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Mar 12 08:22:52 2003 Subject: [Spambayes-checkins] spambayes/windows README.txt,NONE,1.1 pop3proxy_service.py,NONE,1.1 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv16014 Added Files: README.txt pop3proxy_service.py Log Message: pop3proxy service for Windows 2000/XP. --- NEW FILE: README.txt --- This directory contains Windows specific functions for the spambayes project. Currently this contains: * Windows Service version of the pop3 proxy Fairly short-term plans include: * Inno setup installer for pop3proxy and Outlook. --- NEW FILE: pop3proxy_service.py --- # Run the pop3proxy as a WinNT service. Should work on Windows 2000 # and Windows XP. # # * Install as a service using "pop3proxy_service.py install" # * Start the service (Use Control Panel etc, or # "pop3proxy_service.py start". Check the event # log should anything go wrong. # * Service should display # * To debug the service: "pop3proxy_service.py debug" # Service then runs in the command prompt, showing all # print statements. # * To remove the service: "pop3proxy_service.py remove" # This module is part of the spambayes project, which is Copyright 2002 # The Python Software Foundation and is covered by the Python Software # Foundation license. # Originally written by Mark Hammond. import sys, os # We are in the 'spambayes\win32' directory. We # need the parent on sys.path, so 'spambayes.spambayes' is a package, # and 'pop3proxy' is a module sb_dir = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, sb_dir) # and change directory here, so pop3proxy uses the default # config file etc os.chdir(sb_dir) # Rest of the standard Python modules we use. import traceback import threading # The spambayes imports we need. import pop3proxy # The win32 specific modules. import win32serviceutil, win32service import pywintypes, win32con, winerror from ntsecuritycon import * class Service(win32serviceutil.ServiceFramework): _svc_name_ = "pop3proxy" _svc_display_name_ = "SpamBayes pop3proxy Service" _svc_deps_ = ['tcpip'] def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.event_stop = threading.Event() self.thread = None def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) self.event_stop.set() def SvcDoRun(self): # Setup our state etc pop3proxy.state.createWorkers() assert not pop3proxy.state.launchUI, "Service can't launch a UI" # Start the thread running the server. thread = threading.Thread(target=self.ServerThread) thread.start() # Write an event log record - in debug mode we will also # see this message printed. import servicemanager servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '') ) # Wait for the stop event. try: self.event_stop.wait() except KeyboardInterrupt: pass # How do we cleanly shutdown the server? # Write another event log record. s = pop3proxy.state status = " after %d sessions (%d ham, %d spam, %d unsure)" % \ (s.totalSessions, s.numHams, s.numSpams, s.numUnsure) servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, status) ) def ServerThread(self): state = pop3proxy.state pop3proxy.main(state.servers, state.proxyPorts, state.uiPort, state.launchUI) if __name__=='__main__': win32serviceutil.HandleCommandLine(Service) From mhammond at users.sourceforge.net Wed Mar 12 05:27:56 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Wed Mar 12 08:28:00 2003 Subject: [Spambayes-checkins] spambayes/windows pop3proxy_service.py,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv18091 Modified Files: pop3proxy_service.py Log Message: Forgot to save before committing. Need sleep (or another cigarette, I'm not sure ) Index: pop3proxy_service.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_service.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** pop3proxy_service.py 12 Mar 2003 13:22:44 -0000 1.1 --- pop3proxy_service.py 12 Mar 2003 13:27:51 -0000 1.2 *************** *** 6,10 **** # "pop3proxy_service.py start". Check the event # log should anything go wrong. - # * Service should display # * To debug the service: "pop3proxy_service.py debug" # Service then runs in the command prompt, showing all --- 6,9 ---- *************** *** 45,49 **** _svc_name_ = "pop3proxy" _svc_display_name_ = "SpamBayes pop3proxy Service" ! _svc_deps_ = ['tcpip'] def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) --- 44,48 ---- _svc_name_ = "pop3proxy" _svc_display_name_ = "SpamBayes pop3proxy Service" ! _svc_deps_ = ['tcpip'] # We depend on the tcpip service. def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) From tim.one at comcast.net Wed Mar 12 09:03:44 2003 From: tim.one at comcast.net (Tim Peters) Date: Wed Mar 12 09:04:19 2003 Subject: [Spambayes-checkins] spambayes/windows pop3proxy_service.py,1.1,1.2 In-Reply-To: Message-ID: > Forgot to save before committing. Need sleep (or another cigarette, I'm > not sure ) I recommend both -- the dangers of smoking while sleeping are grossly overrated. From anadelonbrin at users.sourceforge.net Wed Mar 12 19:25:00 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Mar 12 22:25:04 2003 Subject: [Spambayes-checkins] spambayes/spambayes/resources ui.html,1.5,1.6 ui_html.py,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes/resources In directory sc8-pr-cvs1:/tmp/cvs-serv29590/spambayes/resources Modified Files: ui.html ui_html.py Log Message: Update OptionConfig so that boolean options are presented as radio buttons rather than a text box, and multiple choice options are presented as checkboxes. Broke up the pop3proxy options into pop3proxy options and header options, as the pop3proxy section was too big to be nice - and this hopefully fits with the migration of header options into a separate module. Index: ui.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/resources/ui.html,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** ui.html 27 Feb 2003 04:13:32 -0000 1.5 --- ui.html 13 Mar 2003 03:24:57 -0000 1.6 *************** *** 155,162 **** !

    These are emails that you can use to ! train the classifier. Check the appropriate button for ! each email, then click 'Train' below. 'Defer' leaves the ! message here, to be trained on later. Click one of the Discard / Defer / Ham / Spam headers to check all of the buttons in that section in one go.

    --- 155,162 ---- !

    These are emails that you can use to train the ! classifier. Check the appropriate button for each email, ! then click 'Train' below. 'Defer' leaves the message ! here, to be trained on later. Click one of the Discard / Defer / Ham / Spam headers to check all of the buttons in that section in one go.

    *************** *** 314,323 **** ! --- 314,352 ----
    Label !      
    ! ! ! ! ! ! ! ! ! ! ! ! ! Index: ui_html.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/resources/ui_html.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** ui_html.py 27 Feb 2003 04:13:32 -0000 1.5 --- ui_html.py 13 Mar 2003 03:24:58 -0000 1.6 *************** *** 5,79 **** import zlib ! data = zlib.decompress("xÚÅ[{oã8’ÿ€ù\016\034-º•`\023ÛIºûf\035[Øî$³\031\\?r‰ç\016ƒÁ AK´­$ê$*Ž¯±ßýªŠ¤D=\ ! ’vïàöÒƒD\022Éb±X_\0259³\037.?],~½¹b׋\017ïÙÍ/ïÞÿ|Á¼ãñø¿Î.ÆãËÅ¥nx5šœ°EÁ³\ ! 2V±Ìx2\036_}ô‚ï¿›mTšÐ_Á#ü«b•\010\026Gsž¼à.çé’ïDÉ~)EÁ~Δ(V<\024³1uÀ1¥ÚÑÃ\ ! RF;ö…­d¦¦ì/“\027Œ\0271OŽX¹ËòˆmDò T\034òs–òb\035gS6aÿøþ;Å—0å\036ã°óJ\026)ôm\021øþ\ ! ;>Mâì\036¾‡2‘Å”ýi\002?!5òéC\014«\026Ñ\023­\033ù\000ËjÚÞL^MV+l\033…ÀÈ\024ㆹ㭈×\033àq)“ˆz\ ! <ÄbK3÷ºdÀ'O4w£%Ï2šdÉÃûu!«,‚™Â‰€™ÎYΣ(ÎÖó×õãq\"V@ãäõù÷ß±úg)‹H\024\ ! ÇJæД?²R&qÄ–\011Ð\034ê·”JɴוøÆÝ&†ˆç2þ\037\001ýÎÎ^´–ÝHùä55¤¢,ùZèýR(Ð\016¿â±\ ! YB¡å\000ßhl)BÔ<»×šG—¹?ý8Áçl\033Gj\003zðú…;\0209\006²\035\031®V+.&ç\003|¸\002ùª`ìÜCƒ†7Þ\ ! pe4ÞL\017$'ù£ÙóB rhA—lPA'¤‚½žÃúj:7\032÷\014E§ÓsÄp\035ªˆsñYfz?[ú)Büg:Vñ5x\ ! Šë!½9¼8¯©ÿHÔχÅ6\033[W1\033\033‡\003$Dô8øàÑ7\024ü,Š\037X˜ð²œûÚ€üÀ´”9Ïh\004¸†T(/\ ! ˜Åéš•E8÷õ—Ñ:^ùŒ'ñ:›û|Y¦q\024%Â\037\007/³e™Ÿ\003\037@¡EÍL¤wÁNÔž,/dT…ê#O[n\021>?\ ! î\014Å©3Žk\016e*p+<¶)Ä\012&€w?¸†ß³1\037ž\006L,£9^®Õ9«â\021úè6Ëí\027\020”–$J\014I¤<Î.´\025{\ ! fež±j-ßÙæU\000ž\\/\010ô\030¶ãU#ø#½\015.*3Š@|Æ‘eXñõ\021É{)`8)»\ ! Ù©\0158\017Ã\035Ð5R\037Íƹ«Po‘*ì\032Oó„8ÙÈ­æw+‹{\012ò°«±î\025Åj)\037!\020¨&\000\037»¼\000\011ýÉЄ\001\ ! ¦ÉØà,ÎòJ‘94]<¦v¹\000l\003úãÆ%•¨›=†þlîL¼±1­fÏ\030lrÉv²r„Õ¨\020lÛºàiÊ•V²\ ! ikõh•ä÷ÀõÃÞ›\011þ«][\016b°]gl#]\015|ÎݨS\017G·«}ñÜ#O1ñ\002ø™±\005t\016ô²PÎÂö\034\ ! ‡ÆÏæLæ\";ðŒ3ðŽ˜W(ï\020\002\030\016\016÷¤\024É\020\0105\014ŒÐQ¿\022Å\001\022Ý—L^€_Ab£fwíÆ\007/\023u>¼ý\ ! õ\026?§\010ct¼³q\034ì¿$‡‹\021Qƒ5z\027\033ž­Eäý?.ɲÐ]Ò\030ô¬\027?Œ­ÞIÒlcÚhSä'ª\030‚x­ò\035×\ ! ŽA«\004ÞE™ùÆÿ¢‡Á+\016pš/%°žóR¡÷BÔ‘­Ñý\000š\004Ë?b²Ð\023`+Ño<¨¦¶*dÊÊ°à\012üY¬Ù\ ! \002ßú6‹ú1\010ý\010†»B&\011ð\000†&Àñ/wÖ«B3Öó7?f!Ê\011'GbdåF\010uÄ\024&M`íØ\006Ë•&¾!×\ ! à„³u…\013\007¯H¦\010\0161\023ÛÆÝv„tL^ZGÁRÔÜ $0¦A´Ù1ÁË]×y¾\023\011xK좽|/°6‚\036\022ø@×\ ! ‰\035ãbГ—õ\014\010\005\012tw³Ü\002\005\027éy\001\001¢è\035zW‡'\007€™fÏj“Fø\006N¹¨ßg¡H\022\030\031b²ã7>K\025Á\014Ð\ ! §%\007­^g¼ùì£õàÔsÿ\0240\024MÍÈñ«\010¬\010èô(ú1(Â\005Ìë£E\020\034„üÉïÐGàéëÄ\003HO\\ø§Í\017Ð¥\ ! %ækœY*®ªRãL3ðÕć0†ˆ—ž-Øt0\036ò鼚EÃ\012:`­Å—3‚`\031\006Iæ\010\036¬àƒ,Õ0æ*-’\002íi\ ! è€ñ—0\035H\017,\011MÁê¤bÍ\030°ævoGuuJõ\010\035uðÎ\006±\"º\000þ !ÔEUž@|Ä…Y,2².Èn üB]1\ ! k6h}YŒ»Àw\017ÍÕ;ÔR[­›(sݸÀwÏä…s\017\\¨«¢öÝæçðÞV°Aø®){Oe\020»KH ã¤ÕãæÓ\ ! Í™Î#\020?eèI@F³eÐ\031z\003±»¼#çé\005'''\023#Ùx\031\034¹Û\013}É™É6\021²EMÁD\021HøS‡ÐÈUXýµ\ ! Ù\000üy\013ºù 4Ó`\020H‘6µœvÌÆšÓ ;\001à\024zzËú¨3AŸ²vÚ¥\036ýÜ4J*žì7ËUŠÛ u'^Å\030\ ! ¬öœ$«RÌ\025:ô\031<¥GÏ\017»îÚ|uÐ/YY\025¢3¬¢Ý5-pùLè•A褩³\022ä|Ú™\0039oÓo¹î\017\ ! ÙtGt8館µ± Ó3øÖ\024zÞˆG߸J÷wgŒë½ÝiêÄ\037ôe\025¯Ÿsȶ\036€E\"ßåµ?yÿ÷¯\000ýC¬\023Ð\ ! 4 zL\006Š&gì,\037’å®T\"\005‡mpGG\013¸©\030è\031ýàÂ̬\035(\002.¬ Œ\\99!°ö£_õ“ºü´\000Ô؆\035´\ ! ÏM£õW‹V\006¯V©dQç Oê:!„*\005æ$JãQ¬\010I/©cm{€þx\011\026ˆu¨,\033\022zd-¢F*š1?¸¥\ ! ¿u\024\011ɦŽ,{â\034³P\024Ú0ÒÑ\035Hªu\030ÀB\011„C]‘0F‡Éõά\024b1³qhÕ\020n/6\"¼§ÏÙ\027\022r\000\ ! Ì,—•R ‹•4øM`\026O3\034a_,WÅ0Ì_ Q\037òq\000ˆ@Ì¿\024+Qø¬ò\007]œÑÃt(N\037™¤ßx\007\0249&³\ ! Ä\013\021mð€\036|\031—!/\"6fD\035þ‚;€ß¨\004ÌV-fH+i\020®\036­WRjèÀQ\037\010ÙXà±–m°Kå}N]ê=\006ö\ ! A)\000~ýíjáؾIð)éñ7q\024\001XaX8›û E\011b@Èf\036u:äO\\»†@\006\012¯Çë§Áá\032M\030ð\000m-°`ß\ ! ûx×Á\017-\016Êj™ÆÊr°–\016ÿïHˆþPy\032\014w7(/YA\032Æ\001ÍFq‰ü\001‰\023§îÙ\002-ÚµîÃ\005Ja?&>BÏ\ ! oeà)\007ÙÅË}€ˆ©s‘¶Ðbc¹a\021çvY˜\024ÿÎ\037¸þê\007ãñì‡ß..ß.Þþf\022Ì*Óš)3í\023\016p$\035B\ ! Aʹ¦0ñ¥á'^±\003ÈÖ«\024Àñ\010ù(ÙË—¬ýe”ˆl\015¹X0g§‡ÍÐ/m)’ÖÏ;C;ùý¼×\035ÄÐqr\ ! Îb6£af\002øðç?\037¶»éoU\011\010œ\012Ø@\004Gÿ\026ÿ>Â\036QÃ?õ\017\007\016Ap¡õH»¢ùœázë\006à\027?¢Ô\ ! àû°šØ9M‰d^Kw¤ä{¹\025Å\005„ƒÃÃþè/Ï\023$ç\003Þ\014æ/*1°†´?9¯æÑü\031ÿ½I6HYöôN\ ! 7Ÿî\026~ÐÍ4|›ûêÄÎ=BÛ×a€·p\"Ð]µ´1«•J\002\000\013>ØÀë€Z^2< ž\022Fë% 8ê§B¦O4[¶[\ ! GTà\034ä¶à9\0326Ž\037Æ-^cmÓƦ|äÅ?‚H¥£\012è›\027˜gŠÝãŽ\032Å=¢…O€\022Ä6¢\003ÿ\000\025ŒD\006\ ! \037Ú\007=Z³–Á`Y ]ÄpvýVn›â€=³ó‡7Ëžþùý¡$°¢b½9ò°·\027ô\015æVL\035œ‡U]³e…\014ï\ ! `ìÇãÃÎ\011Öpu\003\013wŸ½à6\0167±`×q\026a`5³ /\005\007˜'Ã$T×({T\014l\017±\006Qø½ªJ/”\027<Š\ ! ¥`Æ\022vS2‚¿úu8€!§‘VC[;õÍ{\035¼ú¿ÿ/˜@ýõšxJïÿZ\0260µsC:¼ÿk\031 tÔa\000ßýñÞ\ ! \026³Lxv\006\0234àBW˜žíS\"Œ¼\007¥=ôµ“\037ödeq—Y²\006ö{¬ùi8ô-µ²*O$úH\034… Û¼:î‘P\ ! Z›×Š€L\000Ž¢%¥U¢âœ\027Š¸:Ž¸âÖ%]ÅT@פ\031¯\023”&Ý’Åç\0247\002À<)Õxi5T¯oŸðéÉð»U\ ! 5ýl$\011Btdÿ© C\010AÉÇv#“:Õ\004d•…IEe|\023è\016)ašºõGÄ‘íq3“¢ì [p»gº<=÷ñ4\017\ ! öÅôtgwÎ$õ~Öª>\004½·\033ðm}ÀX~çÚBŸÞæ\033É]?KÍÚì>$/L߆œUÆ:Y>ÀÚÙsR¯5\036\ ! ªV˜ø#´]J<´@Îè(%‹,Œ¡Zæ±S\"‰37YnªÕ¬\020©| ;\000õAôáÞ%€-drÿQ‰b7l\007usc\012ø\ ! é¿ñ“ß\027!¶yVRðÜ(¤“’øúôÎ?›twaØ+\000>\004}5§iöø<\032\022ùWW»‚Hlðâðz\016Þ~ ·Y<Ž\ ! õjkÌž_üÉ~‹¿BwJ•’8²‡\035Övñ˜\021§ùg\004\022¼S\\•ÃuŸºÙ‚¤Uº\004F€\0034ÞºòÔ)b[(+\ ! ¼psrzöD\031º!µyšÒ¦&ôêõ›'\010Ý\024rÉ—1db;]fi\\j¨×u\001Í(\014ž!!ƒÖ‘v\031\007ÛZzÁdôo\ ! ?þå‰\011¿%¸X+¾\025%D„'ÄÜéä”بΔ7\013DñØ#\021û\015™%1c\022“\007}â•(CJü!g¤Ó4?õ`ki\ ! \016³ŽX\022ß\013-°%ê48KV\037¹\035º×¼š3%¢±p\013„M§¢éAx¢},^Æ>ž-‘w/SBd¹2whp\037ûå”\026\ ! ©!\001õ\0068`b\010P¸Uü<¸\025ª*²¦\012۾͆§øv\037ÁÓ!ùtx?t§·º§#ƒ &6ؽ8\034¾ú¶âQÅ\ ! ü' ö„ÎÕíµºýôéöƒuv\036]\012\0202\007u´\016ÏC‡çõ®ÊaÅ\031KŸ\000\004¨øKåP\034L^\012(˜C,®ìÅ\004¼\000e\ ! Åt\010d¥(õÙ\002Õ{GT7/j\032œî\007É‚Ncûæë›~7\\mÐýúÁØœòsó©eÑ_‘ËEsiº,Þ¾{\ ! åô\031>@\03580]Ü:£À\002NZÕ‹Å¥\001Í\036)ºç\026\030:ÚZ3™ð¥H¼à=þ±·/û\007î‹Ëö<]r?¼ùeѺ\ ! 8£¯Óœ¾ñh\026ŠG&’éT\026\000mú\031üéç”ç^\033 w'£\015Ùˆ$‡-\016±.@‘®/œL&~ðüéÐ3äô}†\026±\ ! §ÎÇ,òº†a\014\027ØØ•EF\003üÃãíS;wÚݹf¯læC›è\007\027UQà=òÿÄø=\035X³ËfÍ\012ê±4\024Ü\003A\ ! €ÃZsŸcÔÉþ ›#K·ï˜\024¹ïRô]\031JÜj^]Lb€±Uˆ\015VeÌM«;þ ¼&<ÖDì\007ô,ÍkËÏ\024B[´\ ! XqŠ}=gó\007غմñìF\023\037`±Åà7_³ØT*’Ûl\010Kê3ÄN\017\017k†wæ\033y!ö7)£å®¹4j¦Äkâön\ ! \032<7ÈTJ…E\022\013JK\020}\037’šÈl΂ÁH^\014\025XgNÒ\037\015_7ojg¾ž\031ãߊ€\017¿Ñ·Á¹{\037ùž\000\031I\ ! ©À\037{Á\007™Á¦„ìlÂN^M'¯¦g§ìt295²\037µæ6s)•OÇãív;*í„#Y¬Ç\016\003øŽÓwŽÏ[–ÙYf\ ! k“È££ƒi™ù\020,GyØQælÈ ~п~qÇ x4\023ö\022$pÎì`ÌÐ5·\006‹4\030ÄÁñ )2Úéÿu@ÿ?Kÿ\013\ ! ‹t¹ª") ### end --- 5,80 ---- import zlib ! data = zlib.decompress("xÚå[{oܸ\021ÿÿ€û\016<\025‰lÔÞ]ÛIz]¯…&¶S\037š\\\\{¯Åáp\010¸\022wWµ$ª\022åõ6¸ïÞ™!)Q\017;\ ! ›\036ú\000ê\004^­H\016‡ÃyüfHϾ¹øp>ÿñú’]Íß¿c×?¼y÷Ý9ó\016Çã¿žœÇ\027ó\013Ýðb49bó‚ge\ ! ¬b™ñd<¾üÞ\013¾þj¶ViBŸ‚Gø©b•\010\026Gg\036=yÁmÎÓ\005ߊ’ýPŠ‚}—)Q,y(fcê€cJµ¥‡…\ ! Œ¶ì\023[ÊLMÙï'Ï\030/bž\034°r\023—å\001[‹ä^¨8ä§,åÅ*ΦlÂ~ùú+Å\0270å\016ã°óR\026)ôm\021øú+\ ! >Mâì\016Þ‡2‘Å”ýf\002?!5òé}\014«\026Ñ#­ky\017ËjÚ^M^L–Kl\033…ÀÈ\024ㆹÈWkàq!“ˆzÜ\ ! ÇbC3÷ºdÀ'O4w£\005Ï2šdÁûU!«,‚™Â‰€™NYΣ(ÎVg/ëÇÃD,ÆÑËÓ¯¿bõÏB\026‘(\016\ ! •Ì¡)`¥Lâˆ-\022 9Ôo!•’i¯+ñ»M\014\021Ïeü\017\001ýNNžµ–ÝHùè%5¤¢,ùJèýR(Ð\016¿â¡Y\ ! B¡å\000ïhl)BÔ<»×šG—¹ß|;Á§l\023Gj\015zðò™;\0209\006²\035\031.—K.&§\003|¸\002ù¬`ìÜCƒ†7Þp\ ! e4ÞL\017$'ùƒÙóB rhA—lPA'¤‚½žÃúj:7\032÷\004E§ÓSÄp\035ªˆsñQfz?[ú)Büg:Vñ\025xŠ\ ! «!½9ž<;­©KÔO‡Å6\033[W1\033\033‡\003$Dô8øàÑ;\024ü,ŠïY˜ð²<óµ\001ùi)sžÑ\010p\015©P^0\ ! ‹Ó\025+‹ðÌ×oF«xé3žÄ«ìÌç‹2£(\021þ8xž-Êü\024ø\000\012-jf\"½\013v¢ödy!£*Tßó´å\026áõÃ\ ! ÖPœ:ã¸æP¦\002·ÂcëB,a\002øî\007Wð{6æÃÓ€‰e4Çó•:eU\017æë¸dË8\021\007l\026ÊH\0045\027ôí€Eb\031g°xµ\026,‘òîgÑáRˆD\ ! “Kj©0bÄ6bØ·ènE\001ÎwÄØ\034^߃חU©\011ä1è^‰#(~éé\"\010\005…€è ˜xP\005\017Q±abðSYœ\ ! 'UÂé…Ò$Š*Sq\012ŽJ²h\013\002…\000’$[¦wQ\020'D[­¹jó¥ÇkæX‰\037%«rv\010½ŒX\030Ì\015¦\012ÿÁö\023¡”\ ! °¬ê±¡Ls™¸KXœ–àõö½H¢w`ŒFˆ,ÖâK\037ô§È\0060w\037G–\003Ë\"¸üC¹ø\033¸\034Xiž£\033L€ÏB\ ! Iù\035ð\007kÓìå²,cp²#Mã’‡k-ÍZ˜4*\023\"*Q6\013¡Åg¤·ÆEeF\021ˆÏ8²\014+¾: y/\004,\020'e×\ ! [µ\006ça¸\003ºFê£Ù8w\025ê5R…]ãiž\020'k¹ÑündqGA\036v5Ö½¢X-ä\003\004\002µÖ\004àe—\027 ¡_\031š0À\ ! 4\031\033œÅY^)2‡¦‹ÇÔ6\027€m@<и¤\022u³ÇПyG\023olL«Ù3\006›\\²­¬\034a5*\004Û¶*xšr¥•l\ ! ÚZ=Z%ù=ðcý°÷j‚ÿj×–ƒ\030lg×\031ÛHW\003ŸS7êÔÃÑíj_|摧?Ÿx\001üÌÆØ\002:\007zY(ga;Ž\ ! CãggLæ\"ÛóŒ3ð\016˜W(o\037\002\030ööw¤\024É\020\0105\014ŒÐQ¿\022Å\036\022Ý•L^€_Ab£fwíÆ\007Ï\023u:¼ý\ ! õ\026?¥\010ct¼³q\034ì¾$‡‹\021Qƒ5zçkž­Däý\027—dYè.i\014zÖ‹\037ÆVo%i¶1m´)ò\023U\014A¼VùŽk\ ! Ç U\002ï¢Ì|ãÑÃàÀ%\0078Í\027\022XÏy©Ð{!êÈVè~\000M‚å\0370Yè\011°•è7\036TS[\0262eeXp\005þ,Öl\ ! o}Eý\030„~\004Ã]!“\004x\000C\023àø\027[ëU¡\031HëyÈ›\037²\020å„“#1²¿r-„:`\012“&°vlƒåJ\023ßk\ ! pÂ٪…ƒW$S\004‡˜‰Mãn;B:$/­£`)jnP\022\030Ó Úl™àå¶ë<߈\004¼%vÑ^¾\027X\033aA\017\011| ë\ ! ÄŽq1èÉËz\006„\002\005º»Yn‚‹ô¼€\000Qô\006½«Ã“\003ÀL³gµI#|\003§\\Ôï³P$\011Œ\0141Ùñ\033Ÿ¥Š`\006è\ ! Ó’ƒV¯3Þ¼öÑzpê3ÿ\0300\024MÍÈñ«\010¬\010èô(ú1(Â9Ìë£E\020\034„üÉïÐGàéëÄ\003HO\\ø§Í\017Ð¥\ ! %ækœY*®ªRãL3ðÅć0†ˆ—ž-Øt0\036òé|5‹†\025tÀZ‹/g\004Á2\014’Ì\021Ñy\004â§\014=\011Èh¶\010:C¯!v—·ä<½àèèhbä1\033/‚\003w{¡/93Ù&BX¶¨)˜(\002\011ê\020\032¹\012«ß\ ! 6\033€?¯A7ï…f\032\014\002)Ò¦–ÓŽÙ¸Ss\032t+\000œBO/pY\037u&èSÖN»Ô£ŸšFIÅ“Ýf¹Lq\033´îÄË\030\ ! ƒÕŽ“dUŠ¹B‡>ƒ§ôàéaWýQëÏ\016ú!+«Bt†Uô²»¦9.Ÿ\011½2\010]˜4uV‚œO;s çmúí1Wý\ ! !ëîˆ\016'\024µ6\026tz\006ßšBÏ+ñà\033WéþîŒq½·;Møƒ¾,ãÕS\016ÙÖ\003°H仼ö'ïÿþ\021 ˆu\002\ ! š\006DÉ@ÑäŒåïB²Ü–J¤à°\015îèh\0017\025\003=£\037œ›™µ\003EÀ…\025„‘+''\004Ö~ô³~R—Ÿæ€\032Û°ƒ\ ! ö¹i´þjÞÊàµÓ*•,ê\034\024òI]'„P¥ÀœDi<Š\025!é%u¬m\017Ð\037/Á\002±\016µ–eCB¬EÔHE3æ\0077\ ! ôY÷G‘lêȲ#Î1\013E¡\015#\035ݤZ‡\001,”@8Ô\025\011ct˜\\oÍJ!\026£\017®\027lŠ\003õª!Üž¯ExGKƒÌ\ ! ¾\003`f¹¨”\002Y,\001\026\013Là‰øA]\025ÀrU\014Ãü9Rö!\037\007€\010Äü\013±\024…\017É*¿7Å\031#\030=\024ãôIúw\ ! @‘c2K¼\020Ñ\006\017èA\027q\031ò\"bcFÔá\023Ü\001üF%`¶j\0114CZIƒpõh½’RC\007Žú@ÈÆ\002•lƒ]*ïsê\ ! Rï1¬\001”\002à×\037/çŽí›\004Ÿ’\036\035G\021€\025†…³3\037¤(A\014\010ÙÌ£N‡ü‰k÷O\020È@áõxý48\\£\011\003\036 \ ! ­\005\026ì÷>ÞuðC‹ƒ²Z¤±²\034¬¤Ãÿ\033\022¢?TžÆ\037ÃÝ5ÊKV†q@³Q\\\"@âÈ©{¶@‹v­»pRØ\ ! ‰ï¡ç—2ð˜ƒìâå>@ÄÔ¹H[h±±Ü°ˆs»,LŠÇã÷\\¿õƒñxöÍOç\027¯ç¯2\011f•iÍ”™ö\011\ ! {8’\016¡ åœKS˜øÔð\023/Ù\036dëU\012àx„|”ìùsÖ~3JD¶‚\\,8cÇûÍÐOm)’ÖŸu†þtôói¯\033\ ! Û‹¡ãä”ÅlFÃÌ\004ðâ·¿ÝowÿÔߪ\022\0208\025°\010Žþ)þy„;=¢†=êï\017\034‚àBë‘vEggì\004×[7\ ! \000¿ø\022¥\006ï‡ÕÄÎiJ$gµtGJ¾“\033QœC\030ØÛßïþô4Ar>àÍ`þ¢\022\003kø¥ýÊùj\036ÍÇxüóÏM²\ ! Aʲ£wºþp;÷ƒn¦áÛÜW'vî\021Ú®\016\003¼…\023n«…Y­T\022\000XðÞ\006^\007Ôò’á\001ñ”0Z/\001ÅQo\013™\ ! >ÒlÙn\035Qs›‚çhØ8~\030·xµM\033›ò‘\027ÿ\000\"•Ž* o^`ž)vÿ5j\024÷ˆ\026>ý\012J\020Ûˆ\016|þ\012\ ! *\030\033‰\014>´\017z´f-‚Á²@»ˆáìúÜ4Å\001{fç\017o–=ýóû;CI`EÅzs åao/è\033̘:8\017«þº\ ! fË\012\031Þ}ÃØ·‡û\023¬áê\006\026î>zÁM\034®cÁ®â,\002ÃÀjfAoþ\000þ\0160O†I¨®Qö¨\030Ø\036b\015¢ð{U•\ ! ^(/x\024K\033ÁŒ%l§d\004ºüq8€!§‘VC[;õÍ÷:xõÿ;˜@ýõšxJßÿ³,`jç†tøþŸe€ÒQ‡\ ! \001üîw¶˜E³;4˜ \001\027ºÂôô×>%ÂÈ;PÚA_;ùaOV\026w™%k`¿Ãš\037‡C_R+«òDò¨ÄQ\010º\ ! Í«ã\036\011¥µy­\010È\004à(ZRZ%*Îy¡ˆ«Ãˆ+n]ÒeL\005tMšq›¥8é–,>¦x¼\021\000æI©ÆK«¡z}û„\ ! OO†ï­ªég#I\020¢#û\017\005\035B\010J>6k™Ô\031\022 «,L**ã›@·O\011ÓÔ­?\"Ž„l›™\024e\007…Ü€Û=Ñå\ ! é3\037Oó`_LOwvçLRï÷G­êCÐ{³\006ÿØÖ\007Œå·®-ôé­¿ÜÕ“Ô¬ÍîBòÜômÈYe¬“å=¬\035˜\ ! ='õZá¡j…‰?BÛ…ÄC\013䌎R²ÈÂ\030ªe\036:%’8s“å¦ZÍ\012‘Ê{º\003P\037Dïï\\\002Ø@&÷çJ\024Ûa;\ ! ¨›\033SÀWÇW~_„ØæYIÁs£NJâëÓ;ÿdÒÝ…a¯\000ø\020ôÕœ¦ÙãóhHäŸ]í\022\"±Á‹Ãëu:x»\ ! Þfñ8Ö«­1{zñG»-þ\022Ý)Õ4âÈ\036vXÛÅcFœæ_\021\004JðVqU\016×}êf\013’¾¯Ò\0050\002\034 ñÖ•§N\ ! \024ÛBYá…›£ã“GÊÐ\015©õã”Ö5¡\027/_=Bèº\013¾ˆ!\023Ûê2KãRC}¼®\013hFað\014\011\031´Ž´Ë8ØÖ\ ! Â\013&£ß}ûûG&ü’àb­øF”\020\021\036\021s§“Sb£:SÞ,\020ÅcDì;d–ÄŒIL\036ô‰W¢t\016)ñ‡œ‘NÓ@\ ! þÔƒ­¤9Ì:`I|'´À\026¨Óà,Y}ä¶ï^ójΔˆÆÜ-\0206Š¦\007á‰>ö±x\031ûx¶DÞ½L\011‘åÒÜ¡Á\ ! }ì—SZ¤†\004Ô\033à€‰!@áVñóàF¨ªÈš*lû6\033žâÛ}\004ÿM‡äÓáýÐ^ë>žŽ\0146‚˜Ø`÷bøê\ ! Û.ŠG\025ó·@í\021«Ûku{ûáæ½uv\036]\012\0202\007u´\016ÏC‡çõ®ÊaÅ\031KŸ\000\004¨øKåP\034L^\012(˜C,®ì\ ! Å\004¼\000eÅt\010d¥(õÙ\002\025}GT7/j\032œî\007É‚Ncûæë›~×\\­ÑýúÁØœòsóªeÑŸ‘Ëysi\ ! ºÌ_¿ywéô\031>@\03580߸£Àùƒ\025\034µ*\030ó\013\003œ=RvÏ-2t4¶f4á\013‘xÁ;ü°70û‡îó‹öÒW³õô±›¿è/\016\0064\013ü\013\025ÉÝe\016ê£\ ! ¹¾÷å‚ä渫FâØ\002\014i”\037œWEÎBâœ\016¬ÇQ9³æ&\022ê±4\024P\012íì~\035@ŸbÔ)BA7G–nß1\ ! ÅÓ>²ÑWö¨~TóÚRA\032Y\035[cqØ(ß-¿\027^ƒÒk\"ö\005\002œæk\013î\024B\003\013±ä\004Á{˜çW°u£iã\021²&\ ! >Àb‹Á/¾íµ®T$7ÙPJ«¯2tzxxtqkÞ\021\030b”2Zl›»ëfJük\025{E\026ž›\004YJ…µZ›\033— ú~\ ! fl\022\004ë}&“gCç<3§ö\030\015ÿÕKSÂ÷õÌ\010»ý\026\020o äµþ£\024î^\013snIÅ\035*€…^ð^f°)!;™°£\ ! \027ÓÉ‹éÉ1;žLŽìG­¹Í\\JåÓñx³ÙŒJ;áH\026«±Ã\000~Çé;·xZ–ÙYfk“\010X¢ƒi™ùPu\000åa\ ! G™#jSx\000ýëטM1\001Í„=\007\011œ2;\030\013…š[“\0225©SN\000M‘ÑVÿ\005“þÓÉ\002JÑò=") ### end From anadelonbrin at users.sourceforge.net Wed Mar 12 19:25:00 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Mar 12 22:25:05 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.7,1.8 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv29590/spambayes Modified Files: OptionConfig.py Log Message: Update OptionConfig so that boolean options are presented as radio buttons rather than a text box, and multiple choice options are presented as checkboxes. Broke up the pop3proxy options into pop3proxy options and header options, as the pop3proxy section was too big to be nice - and this hopefully fits with the migration of header options into a separate module. Index: OptionConfig.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** OptionConfig.py 11 Mar 2003 02:48:29 -0000 1.7 --- OptionConfig.py 13 Mar 2003 03:24:57 -0000 1.8 *************** *** 10,14 **** To Do: ! o Replace some of the test options with radio buttons/checkboxes. o Suggestions? --- 10,20 ---- To Do: ! o Checkboxes need a default value (i.e. what to set the option as ! when no boxes are checked). This needs to be thought about and ! then implemented. add_id is an example of what it does at the ! moment. ! o The values check could be much more generic. Acceptable values are ! (mostly) already in the code, so they can be tested against, in a ! loop, rather than lots of individual, specific, pieces of code. o Suggestions? *************** *** 21,24 **** --- 27,32 ---- __author__ = "Tim Stone " # Blame for bugs caused by using Dibbler: Richie Hindle + # Blame for bugs caused by the radio buttons / checkboxes: Tony Meyer + # try: *************** *** 69,76 **** # This governs the order in which the options appear on the configurator # page, and the headings and help text that are used. page_layout = \ ( ("POP3 Options", ! ( ("p3servers", "Servers", """The Spambayes POP3 proxy intercepts incoming email and classifies it before sending it on to your email client. You need to specify --- 77,85 ---- # This governs the order in which the options appear on the configurator # page, and the headings and help text that are used. + # The field type may be "text", "rb" (radio button), or "cb" (checkbox) page_layout = \ ( ("POP3 Options", ! ( ("p3servers", "Servers", "text", None, """The Spambayes POP3 proxy intercepts incoming email and classifies it before sending it on to your email client. You need to specify *************** *** 85,89 **** Spambayes."""), ! ("p3ports", "Ports", """Each POP3 server that is being monitored must be assigned to a 'port' in the Spambayes POP3 proxy. This port must be different for --- 94,98 ---- Spambayes."""), ! ("p3ports", "Ports", "text", None, """Each POP3 server that is being monitored must be assigned to a 'port' in the Spambayes POP3 proxy. This port must be different for *************** *** 93,97 **** specify the same number of ports as servers, separated by commas."""), ! ("p3notateto", "Notate To", """Some email clients (Outlook Express, for example) can only set up filtering rules on a limited set of headers. These --- 102,106 ---- specify the same number of ports as servers, separated by commas."""), ! ("p3notateto", "Notate To", "rb", ("True", "False"), """Some email clients (Outlook Express, for example) can only set up filtering rules on a limited set of headers. These *************** *** 106,114 **** mail classification."""), ! ("p3notatesub", "Notate Subject", """This option will add the same information as Notate to:, but to the start of the mail subject line."""), ! ("p3cachemsg", "Cache Messages", """You can disable the pop3proxy caching of messages. This will make the proxy a bit faster, and make it use less space --- 115,123 ---- mail classification."""), ! ("p3notatesub", "Notate Subject", "rb", ("True", "False"), """This option will add the same information as Notate to:, but to the start of the mail subject line."""), ! ("p3cachemsg", "Cache Messages", "rb", ("True", "False"), """You can disable the pop3proxy caching of messages. This will make the proxy a bit faster, and make it use less space *************** *** 118,123 **** Thus, you should only turn caching off when you are satisfied with the filtering that Spambayes is doing for you."""), ! ("p3addid", "Add id tag", """If you wish to be able to find a specific message (via the 'find' box on the home page), or use the SMTP proxy to --- 127,135 ---- Thus, you should only turn caching off when you are satisfied with the filtering that Spambayes is doing for you."""), + )), ! ("Header Options", ! ( ("p3addid", "Add id tag", "cb", ! ("header", "body"), """If you wish to be able to find a specific message (via the 'find' box on the home page), or use the SMTP proxy to *************** *** 128,136 **** do not offer these capabilities. For these clients, you will need to have the id added to the body of the message. If you are not sure, ! the safest option is to use both. Valid options include neither ! the header nor the body (leave this blank), header only ("header"), ! body only ("body"), or both ("header body")."""), ! ("p3stripid", "Strip incoming ids", """If you receive messages from other spambayes users, you might find that incoming mail (generally replies) already has an id, --- 140,146 ---- do not offer these capabilities. For these clients, you will need to have the id added to the body of the message. If you are not sure, ! the safest option is to use both."""), ! ("p3stripid", "Strip incoming ids", "rb", ("True", "False"), """If you receive messages from other spambayes users, you might find that incoming mail (generally replies) already has an id, *************** *** 141,145 **** ids from incoming mail."""), ! ("p3prob", "Add spam probability header", """You can have spambayes insert a header with the calculated spam probability into each mail. If you can view headers with your --- 151,155 ---- ids from incoming mail."""), ! ("p3prob", "Add spam probability header", "rb", ("True", "False"), """You can have spambayes insert a header with the calculated spam probability into each mail. If you can view headers with your *************** *** 147,151 **** and even instructive if you're a serious spambayes junkie."""), ! ("p3thermostat", "Add spam level header", """You can have spambayes insert a header with the calculated spam probability, expressed as a number of '*'s, into each mail (the more --- 157,161 ---- and even instructive if you're a serious spambayes junkie."""), ! ("p3thermostat", "Add spam level header", "rb", ("True", "False"), """You can have spambayes insert a header with the calculated spam probability, expressed as a number of '*'s, into each mail (the more *************** *** 154,158 **** classification of ham/spam, ignoring the classification given."""), ! ("p3evidence", "Add evidence header", """You can have spambayes insert a header into mail, with the evidence that it used to classify that message (a collection of --- 164,168 ---- classification of ham/spam, ignoring the classification given."""), ! ("p3evidence", "Add evidence header", "rb", ("True", "False"), """You can have spambayes insert a header into mail, with the evidence that it used to classify that message (a collection of *************** *** 160,168 **** with your mailer, then this may give you some insight as to why a particular message was scored in a particular way."""), - )), ("SMTP Options", ! ( ("smtpservers", "Servers", """The Spambayes SMTP proxy intercepts outgoing email - if you have sent it to one of the addresses below, it is examined for an id and --- 170,177 ---- with your mailer, then this may give you some insight as to why a particular message was scored in a particular way."""), )), ("SMTP Options", ! ( ("smtpservers", "Servers", "text", None, """The Spambayes SMTP proxy intercepts outgoing email - if you have sent it to one of the addresses below, it is examined for an id and *************** *** 179,183 **** Spambayes."""), ! ("smtpports", "Ports", """Each SMTP server that is being monitored must be assigned to a 'port' in the Spambayes SMTP proxy. This port must be different for --- 188,192 ---- Spambayes."""), ! ("smtpports", "Ports", "text", None, """Each SMTP server that is being monitored must be assigned to a 'port' in the Spambayes SMTP proxy. This port must be different for *************** *** 187,191 **** specify the same number of ports as servers, separated by commas."""), ! ("smtpham", "Ham Address", """When a message is received that you wish to train on (for example, one that was incorrectly classified), you need to forward or bounce --- 196,200 ---- specify the same number of ports as servers, separated by commas."""), ! ("smtpham", "Ham Address", "text", None, """When a message is received that you wish to train on (for example, one that was incorrectly classified), you need to forward or bounce *************** *** 195,199 **** a valid email address, like ham@nowhere.nothing."""), ! ("smtpspam", "Spam Address", """As with Ham Address above, but the address that you need to forward or bounce mail that you wish to train as spam. You will want to use --- 204,208 ---- a valid email address, like ham@nowhere.nothing."""), ! ("smtpspam", "Spam Address", "text", None, """As with Ham Address above, but the address that you need to forward or bounce mail that you wish to train as spam. You will want to use *************** *** 203,207 **** ("Statistics Options", ! ( ("hamcutoff", "Ham Cutoff", """Spambayes gives each email message a spam probability between 0 and 1. Emails below the Ham Cutoff probability are classified --- 212,216 ---- ("Statistics Options", ! ( ("hamcutoff", "Ham Cutoff", "text", None, """Spambayes gives each email message a spam probability between 0 and 1. Emails below the Ham Cutoff probability are classified *************** *** 211,215 **** and should be smaller than the Spam Cutoff."""), ! ("spamcutoff", "Spam Cutoff", """Emails with a spam probability above the Spam Cutoff are classified as Spam - just like the Ham Cutoff but at the other --- 220,224 ---- and should be smaller than the Spam Cutoff."""), ! ("spamcutoff", "Spam Cutoff", "text", None, """Emails with a spam probability above the Spam Cutoff are classified as Spam - just like the Ham Cutoff but at the other *************** *** 217,221 **** are classified as Unsure."""), ! ("dbname", "Database filename", """Spambayes builds a database of information that it gathers from incoming emails and from you, the user, to get better and --- 226,230 ---- are classified as Unsure."""), ! ("dbname", "Database filename", "text", None, """Spambayes builds a database of information that it gathers from incoming emails and from you, the user, to get better and *************** *** 308,315 **** # then blank out the example rows to make way for the real ones. configTable = self.html.configTable.clone() ! configRow1 = configTable.configRow1.clone() configRow2 = configTable.configRow2.clone() blankRow = configTable.blankRow.clone() ! del configTable.configRow1 del configTable.configRow2 del configTable.blankRow --- 317,328 ---- # then blank out the example rows to make way for the real ones. configTable = self.html.configTable.clone() ! configTextRow1 = configTable.configTextRow1.clone() ! configRbRow1 = configTable.configRbRow1.clone() ! configCbRow1 = configTable.configCbRow1.clone() configRow2 = configTable.configRow2.clone() blankRow = configTable.blankRow.clone() ! del configTable.configTextRow1 ! del configTable.configRbRow1 ! del configTable.configCbRow1 del configTable.configRow2 del configTable.blankRow *************** *** 319,333 **** # value. isFirstRow = True ! for name, label, unusedHelp in values: ! newConfigRow1 = configRow1.clone() ! newConfigRow2 = configRow2.clone() currentValue = bcini.get(parm_ini_map[name][PIMapSect], \ parm_ini_map[name][PIMapOpt]) # If this is the first row, insert the help text in a cell # with a `rowspan` that covers all the rows. if isFirstRow: entries = [] ! for unusedName, topic, help in values: entries.append("

    %s: %s

    " % (topic, help)) newConfigRow1.helpSpacer = ' ' * 10 --- 332,379 ---- # value. isFirstRow = True ! for name, label, fldtype, validInput, unusedHelp in values: currentValue = bcini.get(parm_ini_map[name][PIMapSect], \ parm_ini_map[name][PIMapOpt]) + # Populate the rows with the details and add them to the table. + if fldtype == "text": + newConfigRow1 = configTextRow1.clone() + newConfigRow1.label = label + newConfigRow1.input.name = name + newConfigRow1.input.value = currentValue + elif fldtype == "rb": + newConfigRow1 = configRbRow1.clone() + newConfigRow1.label = label + newConfigRow1.inputT.name = name + newConfigRow1.inputF.name = name + if currentValue == "True": + newConfigRow1.inputT.checked = "checked" + elif currentValue == "False": + newConfigRow1.inputF.checked = "checked" + elif fldtype == "cb": + newConfigRow1 = configCbRow1.clone() + newConfigRow1.label = label + blankOption = newConfigRow1.input.clone() + firstOpt = True + i = 0 + for val in validInput: + newOption = blankOption.clone() + newOption.val_label = val + newOption.input_box.name = name + '-' + str(i) + i += 1 + newOption.input_box.value = val + if val in currentValue.split(): + newOption.input_box.checked = "checked" + if firstOpt: + newConfigRow1.input = newOption + firstOpt = False + else: + newConfigRow1.input += newOption + # If this is the first row, insert the help text in a cell # with a `rowspan` that covers all the rows. if isFirstRow: entries = [] ! for name, topic, type, vals, help in values: entries.append("

    %s: %s

    " % (topic, help)) newConfigRow1.helpSpacer = ' ' * 10 *************** *** 337,344 **** del newConfigRow1.helpCell ! # Populate the rows with the details and add them to the table. ! newConfigRow1.label = label ! newConfigRow1.input.name = name ! newConfigRow1.input.value = currentValue newConfigRow2.currentValue = currentValue configTable += newConfigRow1 + newConfigRow2 + blankRow --- 383,387 ---- del newConfigRow1.helpCell ! newConfigRow2 = configRow2.clone() newConfigRow2.currentValue = currentValue configTable += newConfigRow1 + newConfigRow2 + blankRow *************** *** 392,395 **** --- 435,450 ---- def editInput(parms): + # This is really a bit of a kludge, and a nicer solution would + # be most welcome. Most especially, note that this will fall + # apart if there are more than 9 checkboxes in an option, or + # if "-" appears as the second-to-last character in an option + # value + for name, value in parms.items(): + if name[-2:-1] == '-': + if parms.has_key(name[:-2]): + parms[name[:-2]] += ' ' + value + else: + parms[name[:-2]] = value + del parms[name] errmsg = '' *************** *** 494,498 **** aid = parms['p3addid'] except KeyError: ! aid = options.pop3proxy_add_mailid_to if not aid == "" and not aid == "body" \ --- 549,554 ---- aid = parms['p3addid'] except KeyError: ! parms['p3addid'] = "" # checkboxes need a default! ! aid = parms['p3addid'] if not aid == "" and not aid == "body" \ From anadelonbrin at users.sourceforge.net Wed Mar 12 20:29:58 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Mar 12 23:30:04 2003 Subject: [Spambayes-checkins] spambayes/spambayes UpdatableConfigParser.py,NONE,1.1 OptionConfig.py,1.8,1.9 Options.py,1.22,1.23 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv28646/spambayes Modified Files: OptionConfig.py Options.py Added Files: UpdatableConfigParser.py Log Message: Adds UpdatableConfigParser which extends ConfigParser. Reading and writing are not changed, but additional updating functions are provided, which preserve the original layout of config files. Modifies Options.py to use UpdatableConfigParser rather than ConfigParser. As a result, modifies OptionConfig.py to use the update functions in UpdatableConfigParser. --- NEW FILE: UpdatableConfigParser.py --- #!/usr/bin/env python """Configuration file parser with update facility. Handles configuration files in the exact manner as ConfigParser, but has the ability to update the configuration files. Handles multiple files, and does not touch whitespace or comment lines. c.f. ConfigParser class: UpdatableConfigParser -- responsible for for parsing a list of configuration files, and managing the parsed database, including updating those files. methods: UpdatableConfigParser inherits all of ConfigParser's methods, and adds the following: changed_options() returns a dictionary of file_name : section_dictionary where section_dictionary is a dictionary of section_name : option_dictionary and option_dictionary is a dictionary of option_name : option_value restore_option(section, option) restores the named option to the value in the file the option was loaded from (overwriting any changes made to the value in memory) update(prune=False, add_missing=False) update all files that have been loaded into the UpdatableConfigParser to their current values. Iff prune is True then sections/options that have been removed (via remove_section or remove_option) will be removed from the files. Iff add_missing is True, any options *that have been changed since init/the last update* which are not in the file will be added to the appropriate section. update_file(fp, prune=False, add_missing=False) update only the specified individual file, as per update() update_files(filename_list, prune=False, add_missing=False) update all files in the given list, as per update() write_updated(fp) create a new configuration file, as with ConfigParser.write(), but only write those options that have been changed since init or the last update Configuration options: UpdatableConfigParser.vi = ": " This is the separator used between an option name and value. The value is overridden on execution of the read(), readfp(), or update methods by the last separator found in the read file / file-like-object, unless the UpdatableConfigParser.lock_vi option is set to True. Note that only update(), update_file(), and write_updated() will use this value - write() will *not*. UpdatableConfigParser.record_original_values = True UpdatableConfigParser has two methods of recording changes to options. The default method is to record all original values, and compare these to current values on update. This requires additional memory, but means that the original values are accessable if required (to revert, for example), and means that if an option is changed, then changed back to the original value, no update is made. The alternative method is to simply record any changes made (via the set() method). This, in general, requires less memory, but unnecessary updates (as described above) may be made. Issues to be aware of: If you load in multiple configuration files, and the files contain conflicting values for an option, the value in the last file to be loaded will be used (as with ConfigParser). However, if you change the value of this option, and then update the previous file(s), the value in the files will be recorded as the new value, even if the original value was not the one loaded. If you read a file-like object (using ConfigParser's readfp) and the object to read has a filename attribute, then it must have a write function as well, and the update functions will attempt to update it. If no filename attribute is present (or the filename is ), then no updating will be attempted on these sources. After calling any of the update functions (update(), update_file(), or update_files()) the 'original values' recorded for those options changed are updated to the new values in the file(s). This means that subsequent calls to an update function will have no effect. For example: we have FileA and FileB, both containing OptionC. We modify the value for OptionC. If we call update(), *both* FileA and FileB will contain the new value for OptionC. If we call update_file(FileA), then *only* FileA will contain the new value for OptionC, and if we *subsequently* call update_file(FileB), no changes will be made. To modify more than one source file, but not all source files, use the update_files() function. The os function tempnam() is currently used to get hold of a temp file to create the new config file before overwriting the old one. This gives a runtime warning about a security risk. It would be nice if someone would change this to some other temp file system. """ # This module is part of the spambayes project, which is Copyright 2002-3 # The Python Software Foundation and is covered by the Python Software # Foundation license. __author__ = "Tony Meyer " __credits__ = "All the Spambayes folk." try: True, False except NameError: # Maintain compatibility with Python 2.2 True, False = 1, 0 from ConfigParser import ConfigParser, ParsingError from ConfigParser import DEFAULTSECT, NoSectionError, NoOptionError from os import rename, remove, tempnam import types issues = """ A file like: [sect1] opt1 = val1 [sect2] opt2 = val2 [sect1] opt1 = val1 or even like: [sect1] opt1 = val1 [sect2] opt2 = val2 [sect1] opt3 = val3 will not work as it should with add_missing set to True. """ class UpdatableConfigParser(ConfigParser): def __init__(self, defaults=None): ConfigParser.__init__(self, defaults) self.__data = {} self.__changed_options = {} self.__pruned = {} # override base class self.__sections = self._ConfigParser__sections self._ConfigParser__read = self.__read # configuration defaults self.vi = ": " self.lock_vi = False self.record_original_values = True def remove_option(self, section, option): # c.f. ConfigParser.remove_option() existed = ConfigParser.remove_option(self, section, option) if existed: for sect, opt in self.__data.keys(): if sect == section and opt == option: del self.__data[(sect, opt)] self.__pruned[sect] = opt return existed def remove_section(self, section): # c.f. ConfigParser.remove_section() existed = ConfigParser.remove_section(self, section) if existed: for sect, opt in self.__data.keys(): if sect == section: del self.__data[(sect, opt)] self.__pruned[sect] = None return existed def set(self, section, option, value): # c.f. ConfigParser.set() ConfigParser.set(self, section, option, value) if self.record_original_values == False: for file, sect in self.__data.items(): if sect == section: sectdict = {} optdict = {} if self.__changed_options.has_key(file): sectdict = self.__changed_options[file] if sectdict.has_key(section): optdict = sectdict[section] optdict[option] = value sectdict[section] = optdict self.__changed_options[file] = sectdict def __read(self, fp, fpname): # c.f. ConfigParser.__read() cursect = None # None, or a dictionary optname = None lineno = 0 e = None # None, or an exception while True: line = fp.readline() if not line: break lineno = lineno + 1 # comment or blank line? if line.strip() == '' or line[0] in '#;': continue if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR": # no leading whitespace continue # continuation line? if line[0].isspace() and cursect is not None and optname: value = line.strip() if value: cursect[optname] = "%s\n%s" % (cursect[optname], value) # a section header or option header? else: # is it a section header? mo = self.SECTCRE.match(line) if mo: sectname = mo.group('header') if sectname in self.__sections: cursect = self.__sections[sectname] elif sectname == DEFAULTSECT: cursect = self.__defaults else: cursect = {'__name__': sectname} self.__sections[sectname] = cursect # So sections can't start with a continuation line optname = None # no section header in the file? elif cursect is None: raise MissingSectionHeaderError(fpname, lineno, `line`) # an option line? else: mo = self.OPTCRE.match(line) if mo: optname, vi, optval = mo.group('option', 'vi', 'value') if self.lock_vi == False: self.vi = vi if vi in ('=', ':') and ';' in optval: # ';' is a comment delimiter only if it follows # a spacing character pos = optval.find(';') if pos != -1 and optval[pos-1].isspace(): optval = optval[:pos] optval = optval.strip() # allow empty values if optval == '""': optval = '' optname = self.optionxform(optname.rstrip()) cursect[optname] = optval sectname = cursect['__name__'] self.__updateData(fpname, sectname, optname, optval) else: # a non-fatal parsing error occurred. set up the # exception but keep going. the exception will be # raised at the end of the file and will contain a # list of all bogus lines if not e: e = ParsingError(fpname) e.append(lineno, `line`) # if any parsing errors occurred, raise an exception if e: raise e def __updateData(self, filename, sectname, optname, value): if filename == "": return if self.record_original_values == True: self.__updateDataIncludingOriginalValue(filename, sectname, optname, value) else: self.__updateDataNoOriginalValue(filename, sectname, optname) def __updateDataIncludingOriginalValue(self, filename, sectname, optname, value): if self.__data.has_key((sectname, optname)): existing_files, original = self.__data[(sectname, optname)] if filename not in existing_files: existing_files += filename, self.__data[(sectname, optname)] = (existing_files, value) else: self.__data[(sectname, optname)] = ((filename,), value) def __updateDataNoOriginalValue(self, filename, sectname, optname): if self.__data.has_key((sectname, optname)): existing_data = self.__data[(sectname, optname)] if filename not in existing_files: existing_files += filename, self.__data[(sectname, optname)] = existing_files else: self.__data[(sectname, optname)] = (filename,) def changed_options(self): """Return any options that have changed since reading or updating.""" if self.record_original_values == False: return self.__changed_options else: files_to_update = {} for sectname, sectdict in self.__sections.items(): if sectname == '__name__': continue for optname, optvalue in sectdict.items(): if optname == '__name__': continue if self.__data.has_key((sectname, optname)): source_files, original = self.__data[(sectname, optname)] if optvalue != original: for file in source_files: if files_to_update.has_key(file): # we are already updating this file section_names = files_to_update[file] if section_names.has_key(sectname): # this section is already being updated option_names = section_names[sectname] else: option_names = {} else: section_names = {} option_names = {} option_names[optname] = optvalue section_names[sectname] = option_names files_to_update[file] = section_names return files_to_update def restore_option(section, option): """ restore an option to the value in the source file""" if self.__data.has_key((section, option)): if self.record_original_values: file, original = self.__data[(section, option)] else: file = self.__data[(section, option)] c = ConfigParser() c.read(file) if c.has_option(section, option): original = c.get(section, option) else: raise NoOptionError(section, option) self.set(section, option, original) else: raise NoOptionError(section, option) def update(self, prune=False, add_missing=False): """Write any updates to the appropriate file(s).""" files_to_update = self.changed_options() for file, info in files_to_update.items(): old_cfg = open(file, "r") self.__updateFile(old_cfg, info, prune, add_missing) old_cfg.close() def update_file(self, fp, prune=False, add_missing=False): """Write any updates to the appropriate file.""" files_to_update = self.changed_options() if files_to_update.has_key(fp.name): self.__updateFile(fp, files_to_update[fp.name], prune, add_missing) def update_files(self, files, prune=False, add_missing=False): """Write any updates to the appropriate files.""" files_to_update = self.changed_options() for file in files: if files_to_update.has_key(file): old_cfg = open(file, "r") self.__updateFile(old_cfg, files_to_update[file], prune, add_missing) old_cfg.close() def write_updated(self, fp): """Write all changed options to the specified file.""" files_to_update = self.changed_options() c = ConfigParser() for file, info in files_to_update.items(): for sectname, optdict in info.items(): if sectname != "__name__": if not c.has_section(sectname): c.add_section(sectname) for key, value in optdict.items(): if key != "__name__": c.set(sectname, key, str(value)) c.write(fp) def __updateFile(self, old_cfg, info, prune=False, add_missing=False): temp_name = tempnam() new_cfg = open(temp_name, "w") current_section = None update_section = None new_value = None lineno = 0 e = None # None, or an exception while True: line = old_cfg.readline() if not line: break lineno = lineno + 1 # comment or blank line? if line.strip() == '' or line[0] in '#;': new_cfg.write(line) continue if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR": # no leading whitespace new_cfg.write(line) continue # XXX we need to handle continuation lines # a section header or option header? else: # is it a section header? mo = self.SECTCRE.match(line) if mo: if add_missing == True: # we might have options to add from the previous section if update_section is not None: while len(update_section) > 0: optname, optval = update_section.values()[0] new_cfg.write(optname + self.vi + optval + '\n') del update_section[optname] del info[current_section] new_cfg.write(line) current_section = mo.group('header') if info.has_key(current_section): update_section = info[current_section] else: update_section = None # an option line? else: if current_section is None: # we don't care new_cfg.write(line) continue mo = self.OPTCRE.match(line) if mo: optname, vi, optval = mo.group('option', 'vi', 'value') if self.lock_vi == False: self.vi = vi optname = optname.rstrip().lower() if self.__pruned.has_key(current_section): opt = self.__pruned[current_section] if opt is None or opt == optname: continue if update_section is not None and \ update_section.has_key(optname): new_cfg.write(optname + ' ' + vi + ' ' + \ update_section[optname] + '\n') self.__updateData(old_cfg.name, current_section, \ optname, update_section[optname]) del update_section[optname] else: new_cfg.write(line) else: # a non-fatal parsing error occurred. set up the # exception but keep going. the exception will be # raised at the end of the file and will contain a # list of all bogus lines if not e: e = ParsingError(file) e.append(lineno, `line`) # if any parsing errors occurred, raise an exception if e: raise e if add_missing == True: # add any new sections while len(info) > 0: sectname, optdict = info.values()[0] new_cfg.write('[' + sectname + "]\n") while len(optdict) > 0: optname, optval = optdict.values()[0] new_cfg.write(optname + vi + optval + '\n') del optdict[optname] del info[sectname] new_cfg.close() old_cfg.close() try: rename(temp_name, old_cfg.name) except OSError: try: remove(old_cfg.name) rename(temp_name, old_cfg.name) except OSError: print "Warning: Could not complete config " \ "update of %s" % old_cfg.name # the caller expects old_cfg to be an open reference to the # config file since this is the state on calling, so we return # an open reference to the *new* config file, even though # the caller will probably just close this old_cfg = open(old_cfg.name, "r") Index: OptionConfig.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** OptionConfig.py 13 Mar 2003 03:24:57 -0000 1.8 --- OptionConfig.py 13 Mar 2003 04:29:52 -0000 1.9 *************** *** 37,43 **** from spambayes import Dibbler, PyMeldLite ! from spambayes.Options import options, optionsPathname import sys ! import ConfigParser # This control dictionary maps http request parameters and template fields --- 37,44 ---- from spambayes import Dibbler, PyMeldLite ! from spambayes.Options import options, optionsPathname, defaults import sys ! from ConfigParser import NoSectionError, ConfigParser ! from StringIO import StringIO # This control dictionary maps http request parameters and template fields *************** *** 287,304 **** def onConfig(self): - # start with the options config file, add bayescustomize.ini to it - bcini = ConfigParser.ConfigParser() - - # this is a pain... - for sect in options._config.sections(): - for opt in options._config.options(sect): - try: - bcini.set(sect, opt, options._config.get(sect, opt)) - except ConfigParser.NoSectionError: - bcini.add_section(sect) - bcini.set(sect, opt, options._config.get(sect, opt)) - - bcini.read(optionsPathname) - # Start with an empty config form then add the sections. html = self.html.clone() --- 288,291 ---- *************** *** 333,337 **** isFirstRow = True for name, label, fldtype, validInput, unusedHelp in values: ! currentValue = bcini.get(parm_ini_map[name][PIMapSect], \ parm_ini_map[name][PIMapOpt]) --- 320,324 ---- isFirstRow = True for name, label, fldtype, validInput, unusedHelp in values: ! currentValue = options._config.get(parm_ini_map[name][PIMapSect], \ parm_ini_map[name][PIMapOpt]) *************** *** 410,414 **** return ! updateIniFile(parms) self.proxyUI.reReadOptions() --- 397,409 ---- return ! for name, value in parms.items(): ! if name in parm_ini_map.keys(): ! options._config.set(parm_ini_map[name][PIMapSect], \ ! parm_ini_map[name][PIMapOpt], value) ! ! op = open(optionsPathname, "r") ! options._config.update_file(op) ! options._update() ! op.close() self.proxyUI.reReadOptions() *************** *** 596,635 **** return errmsg - def updateIniFile(parms): - - # Get the pathname of the ini file as discovered by the Options module. - inipath = optionsPathname - - bcini = ConfigParser.ConfigParser() - bcini.read(inipath) - - for httpParm in parm_ini_map: - map = parm_ini_map[httpParm] - sect = map[PIMapSect] - opt = map[PIMapOpt] - - try: - val = parms[httpParm] - except KeyError: - continue - - try: - bcini.add_section(sect) - except ConfigParser.DuplicateSectionError: - pass - - bcini.set(sect, opt, val) - - o = open(inipath, 'wt') - bcini.write(o) - o.close() - def restoreIniDefaults(): - # Get the pathname of the ini file as discovered by the Options module. inipath = optionsPathname ! bcini = ConfigParser.ConfigParser() ! bcini.read(inipath) # Only restore the settings that appear on the form. --- 591,605 ---- return errmsg def restoreIniDefaults(): # Get the pathname of the ini file as discovered by the Options module. + # note that the behaviour of this function has subtly changed + # previously options were removed from the config file, now the config + # file is updated to match the defaults inipath = optionsPathname ! c = ConfigParser() ! d = StringIO(defaults) ! c.readfp(d) ! del d # Only restore the settings that appear on the form. *************** *** 637,645 **** if option not in noRestore: try: ! bcini.remove_option(section, option) ! except ConfigParser.NoSectionError: pass # Already missing. ! ! o = open(inipath, 'wt') ! bcini.write(o) ! o.close() --- 607,616 ---- if option not in noRestore: try: ! options._config.set(section, option, ! c.get(section,option)) ! except NoSectionError: pass # Already missing. ! op = open(inipath) ! options._config.update_file(op) ! options._update() ! op.close() Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.22 retrieving revision 1.23 diff -C2 -d -r1.22 -r1.23 *** Options.py 11 Mar 2003 02:48:30 -0000 1.22 --- Options.py 13 Mar 2003 04:29:55 -0000 1.23 *************** *** 9,13 **** except ImportError: import StringIO ! import ConfigParser try: from sets import Set --- 9,13 ---- except ImportError: import StringIO ! import UpdatableConfigParser try: from sets import Set *************** *** 547,551 **** class OptionsClass(object): def __init__(self): ! self._config = ConfigParser.ConfigParser() def mergefiles(self, fnamelist): --- 547,551 ---- class OptionsClass(object): def __init__(self): ! self._config = UpdatableConfigParser.UpdatableConfigParser() def mergefiles(self, fnamelist): From sjoerd at acm.org Thu Mar 13 14:38:04 2003 From: sjoerd at acm.org (Sjoerd Mullender) Date: Thu Mar 13 08:38:25 2003 Subject: [Spambayes-checkins] spambayes/spambayes UpdatableConfigParser.py,NONE,1.1 OptionConfig.py,1.8,1.9 Options.py,1.22,1.23 In-Reply-To: References: Message-ID: <20030313133805.3FDBB74EB0@indus.ins.cwi.nl> On Wed, Mar 12 2003 "Tony Meyer" wrote: > Update of /cvsroot/spambayes/spambayes/spambayes > In directory sc8-pr-cvs1:/tmp/cvs-serv28646/spambayes > > Modified Files: > OptionConfig.py Options.py > Added Files: > UpdatableConfigParser.py > Log Message: > Adds UpdatableConfigParser which extends ConfigParser. Reading and writing are not changed, but additional updating functions are provided, which preserve the original layout of config files. > > Modifies Options.py to use UpdatableConfigParser rather than ConfigParser. As a result, modifies OptionConfig.py to use the update functions in UpdatableConfigParser. > > --- NEW FILE: UpdatableConfigParser.py --- [...] > self.__sections = self._ConfigParser__sections > self._ConfigParser__read = self.__read This is a gross hack. Private attributes should be private. And aprt from that, my (up-to-date) ConfigParser doesn't have __sections and __read private variables, so it crashes too. Or shouldn't I be using the ConfigParser from the Python CVS? -- Sjoerd Mullender From anthony at interlink.com.au Fri Mar 14 00:47:34 2003 From: anthony at interlink.com.au (Anthony Baxter) Date: Thu Mar 13 08:48:22 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py,1.22,1.23 In-Reply-To: <20030313133805.3FDBB74EB0@indus.ins.cwi.nl> Message-ID: <200303131347.h2DDlYV19909@localhost.localdomain> >>> Sjoerd Mullender wrote > > self.__sections = self._ConfigParser__sections > > self._ConfigParser__read = self.__read > > This is a gross hack. Private attributes should be private. > > And aprt from that, my (up-to-date) ConfigParser doesn't have > __sections and __read private variables, so it crashes too. > > Or shouldn't I be using the ConfigParser from the Python CVS? No. The code shouldn't be poking into internal interfaces like this. It's a portability nightmare - docutils did this too, and broke when the underlying ConfigParser implementation changed. From T.A.Meyer at massey.ac.nz Fri Mar 14 14:59:07 2003 From: T.A.Meyer at massey.ac.nz (Meyer, Tony) Date: Thu Mar 13 21:02:19 2003 Subject: [Spambayes-checkins] spambayes/spambayes UpdatableConfigParser.py,NONE,1.1 OptionConfig.py,1.8,1.9 Options.py,1.22,1.23 Message-ID: <1ED4ECF91CDED24C8D012BCF2B034F13C8C8CD@its-xchg4.massey.ac.nz> > > self.__sections = self._ConfigParser__sections > > self._ConfigParser__read = self.__read > > This is a gross hack. Private attributes should be private. Well, that depends, really. If private attributes are meant to be private, then they shouldn't be accessable. From what I have read, Python is designed to allow them to be non-private, if necessary. Essentially, they aren't private, they're hidden. Two other points: * If ConfigParser was well written, IMO, then the read section should be protected, not private. There is nothing about it that suggests that it shouldn't be overwritten by a subclass, and, in fact, it is probably the most likely candidate. __sections shouldn't have had to be updated - if the subclass didn't have __sections, then the base class's should have been found - again, this should be a protected attribute, *not* private. There's no point writing classes that can't usefully be extended. * The other option was to simply include all the ConfigParser code in UpdatableConfigParser, which would have worked, have resulted in nicer code in many places, and have avoided this. It seemed a waste though, and against OOP principles. > And apart from that, my (up-to-date) ConfigParser doesn't have > __sections and __read private variables, so it crashes too. > Or shouldn't I be using the ConfigParser from the Python CVS? Well, it's not that you shouldn't, it's that I'm not. Issues like this are the ones that I can't test, which is why I commited it - so that other's would (we are using non-released versions of alpha software!). Anyway, I'm regressing Options and OptionConfig to use ConfigParser again, until I resolve this. Thanks for pointing out where the problem was though - like I said, I wouldn't have found this myself, until Python 2.3 is released. =Tony Meyer From anadelonbrin at users.sourceforge.net Thu Mar 13 18:14:39 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Thu Mar 13 21:14:44 2003 Subject: [Spambayes-checkins] spambayes/spambayes OptionConfig.py,1.9,1.10 Options.py,1.23,1.24 Message-ID: Update of /cvsroot/spambayes/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv11375/spambayes Modified Files: OptionConfig.py Options.py Log Message: Revert to ConfigParser rather than UpdatableConfigParser. See check-in list for details. Index: OptionConfig.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/OptionConfig.py,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** OptionConfig.py 13 Mar 2003 04:29:52 -0000 1.9 --- OptionConfig.py 14 Mar 2003 02:14:36 -0000 1.10 *************** *** 37,44 **** from spambayes import Dibbler, PyMeldLite ! from spambayes.Options import options, optionsPathname, defaults import sys ! from ConfigParser import NoSectionError, ConfigParser ! from StringIO import StringIO # This control dictionary maps http request parameters and template fields --- 37,43 ---- from spambayes import Dibbler, PyMeldLite ! from spambayes.Options import options, optionsPathname import sys ! import ConfigParser # This control dictionary maps http request parameters and template fields *************** *** 288,291 **** --- 287,304 ---- def onConfig(self): + # start with the options config file, add bayescustomize.ini to it + bcini = ConfigParser.ConfigParser() + + # this is a pain... + for sect in options._config.sections(): + for opt in options._config.options(sect): + try: + bcini.set(sect, opt, options._config.get(sect, opt)) + except ConfigParser.NoSectionError: + bcini.add_section(sect) + bcini.set(sect, opt, options._config.get(sect, opt)) + + bcini.read(optionsPathname) + # Start with an empty config form then add the sections. html = self.html.clone() *************** *** 320,324 **** isFirstRow = True for name, label, fldtype, validInput, unusedHelp in values: ! currentValue = options._config.get(parm_ini_map[name][PIMapSect], \ parm_ini_map[name][PIMapOpt]) --- 333,337 ---- isFirstRow = True for name, label, fldtype, validInput, unusedHelp in values: ! currentValue = bcini.get(parm_ini_map[name][PIMapSect], \ parm_ini_map[name][PIMapOpt]) *************** *** 397,409 **** return ! for name, value in parms.items(): ! if name in parm_ini_map.keys(): ! options._config.set(parm_ini_map[name][PIMapSect], \ ! parm_ini_map[name][PIMapOpt], value) ! ! op = open(optionsPathname, "r") ! options._config.update_file(op) ! options._update() ! op.close() self.proxyUI.reReadOptions() --- 410,414 ---- return ! updateIniFile(parms) self.proxyUI.reReadOptions() *************** *** 591,605 **** return errmsg def restoreIniDefaults(): # Get the pathname of the ini file as discovered by the Options module. - # note that the behaviour of this function has subtly changed - # previously options were removed from the config file, now the config - # file is updated to match the defaults inipath = optionsPathname ! c = ConfigParser() ! d = StringIO(defaults) ! c.readfp(d) ! del d # Only restore the settings that appear on the form. --- 596,635 ---- return errmsg + def updateIniFile(parms): + + # Get the pathname of the ini file as discovered by the Options module. + inipath = optionsPathname + + bcini = ConfigParser.ConfigParser() + bcini.read(inipath) + + for httpParm in parm_ini_map: + map = parm_ini_map[httpParm] + sect = map[PIMapSect] + opt = map[PIMapOpt] + + try: + val = parms[httpParm] + except KeyError: + continue + + try: + bcini.add_section(sect) + except ConfigParser.DuplicateSectionError: + pass + + bcini.set(sect, opt, val) + + o = open(inipath, 'wt') + bcini.write(o) + o.close() + def restoreIniDefaults(): + # Get the pathname of the ini file as discovered by the Options module. inipath = optionsPathname ! bcini = ConfigParser.ConfigParser() ! bcini.read(inipath) # Only restore the settings that appear on the form. *************** *** 607,616 **** if option not in noRestore: try: ! options._config.set(section, option, ! c.get(section,option)) ! except NoSectionError: pass # Already missing. ! op = open(inipath) ! options._config.update_file(op) ! options._update() ! op.close() --- 637,645 ---- if option not in noRestore: try: ! bcini.remove_option(section, option) ! except ConfigParser.NoSectionError: pass # Already missing. ! ! o = open(inipath, 'wt') ! bcini.write(o) ! o.close() Index: Options.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/spambayes/Options.py,v retrieving revision 1.23 retrieving revision 1.24 diff -C2 -d -r1.23 -r1.24 *** Options.py 13 Mar 2003 04:29:55 -0000 1.23 --- Options.py 14 Mar 2003 02:14:37 -0000 1.24 *************** *** 9,13 **** except ImportError: import StringIO ! import UpdatableConfigParser try: from sets import Set --- 9,13 ---- except ImportError: import StringIO ! import ConfigParser try: from sets import Set *************** *** 547,551 **** class OptionsClass(object): def __init__(self): ! self._config = UpdatableConfigParser.UpdatableConfigParser() def mergefiles(self, fnamelist): --- 547,551 ---- class OptionsClass(object): def __init__(self): ! self._config = ConfigParser.ConfigParser() def mergefiles(self, fnamelist): From anthony at interlink.com.au Fri Mar 14 14:03:28 2003 From: anthony at interlink.com.au (Anthony Baxter) Date: Thu Mar 13 22:04:51 2003 Subject: [Spambayes-checkins] spambayes/spambayes Options.py,1.22,1.23 In-Reply-To: <1ED4ECF91CDED24C8D012BCF2B034F13C8C8CD@its-xchg4.massey.ac.nz> Message-ID: <200303140303.h2E33Sm12845@localhost.localdomain> [Tony - please do something with your mailer to keep lines under 80 columns] >>> "Meyer, Tony" wrote > > > self.__sections = self._ConfigParser__sections > > > self._ConfigParser__read = self.__read > > > > This is a gross hack. Private attributes should be private. > > Well, that depends, really. If private attributes are meant to be > private, then they shouldn't be accessable. From what I have read, > Python is designed to allow them to be non-private, if necessary. > Essentially, they aren't private, they're hidden. The python name-mangling is meant to indicate "private implementation specific details". Messing with the internal implementation details of a class results in code that breaks if the other class changes. The basic python rule is something like "we're all grownups", and this is why it doesn't enforce data hiding. The flipside to this is that you shouldn't be messing with internals. If a class doesn't expose the interface you need (particularly if it's in the python std library), consider posting a patch to the python bug/patch tracker to get this fixed. > * If ConfigParser was well written, IMO, then the read section should > be protected, not private. There is nothing about it that suggests > that it shouldn't be overwritten by a subclass, and, in fact, it is > probably the most likely candidate. __sections shouldn't have had to > be updated - if the subclass didn't have __sections, then the base > class's should have been found - again, this should be a protected > attribute, *not* private. There's no point writing classes that can't > usefully be extended. See above - that the ConfigParser didn't expose this interface is probably just an indication that no-one had needed to do this before. There's been a lot of changes in it for Python2.3, so it seems like you're not the first person to run into this. In a perfect world, the python std lib would be, well, perfect. Alas, it is coded by mere humans, so we have to gradually approach perfection . > * The other option was to simply include all the ConfigParser code in > UpdatableConfigParser, which would have worked, have resulted in nicer > code in many places, and have avoided this. It seemed a waste though, > and against OOP principles. If the 2.3 ConfigParser class is better, there's nothing saying we can't include it in the package (we already do this with the sets and heapq module). Anthony -- Anthony Baxter It's never too late to have a happy childhood. From mhammond at users.sourceforge.net Sun Mar 16 17:21:02 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:21:05 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs - New directory Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv18812/docs Log Message: Directory /cvsroot/spambayes/spambayes/Outlook2000/docs added to the repository From mhammond at users.sourceforge.net Sun Mar 16 17:21:41 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:21:45 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs/images - New directory Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs/images In directory sc8-pr-cvs1:/tmp/cvs-serv19018/images Log Message: Directory /cvsroot/spambayes/spambayes/Outlook2000/docs/images added to the repository From mhammond at users.sourceforge.net Sun Mar 16 17:22:38 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:22:42 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs/images field_chooser_after.jpg,NONE,1.1 field_chooser_new_field.jpg,NONE,1.1 manager.jpg,NONE,1.1 training.jpg,NONE,1.1 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs/images In directory sc8-pr-cvs1:/tmp/cvs-serv19190 Added Files: field_chooser_after.jpg field_chooser_new_field.jpg manager.jpg training.jpg Log Message: Images for the documentation. --- NEW FILE: field_chooser_after.jpg --- (This appears to be a binary file; contents omitted.) --- NEW FILE: field_chooser_new_field.jpg --- (This appears to be a binary file; contents omitted.) --- NEW FILE: manager.jpg --- (This appears to be a binary file; contents omitted.) --- NEW FILE: training.jpg --- (This appears to be a binary file; contents omitted.) From mhammond at users.sourceforge.net Sun Mar 16 17:23:17 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:23:21 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 about.html,1.6,1.7 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv19516 Modified Files: about.html Log Message: Trying to improve the docs. Index: about.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/about.html,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** about.html 7 Mar 2003 06:42:57 -0000 1.6 --- about.html 17 Mar 2003 01:23:15 -0000 1.7 *************** *** 5,106 **** !

    SpamBayes Outlook Plugin
    !

    ! NOTE: This is very very early code. !  If you are looking at this, you have probably been told about it ! against our better judgement <wink>.  Stuff doesnt work ! correctly.  If you want something known to work well today for alot ! of people, this is not for you.  That said, this plug-in ! works amazingly well! So welcome aboard.
    !

    ! This spam filter uses Bayesian analysis to filter spam.  Unlike ! other spam detection systems, Bayesian systems actually "learn" about ! what you consider spam, and continually adapt as both your regular email ! and spam patterns change. The source code is maintained at SourceForge.

    ! Here you can find information on:
    ! !

    Training

    ! Due to the nature of the system, it must be trained before it can be ! effective.  Although the system does learn over time, when first ! installed it has no knowledge of either spam or good email.
    !

    Initial Training

    ! When first installed, it is recommended you perform the following steps:
      !
    • Create two folders - one for "Spam", and one for "Possible Spam"
    • !
    • Go through your Inbox and Deleted Items, and move as much spam as ! you can find to the "Spam" folder.  Try and get as much Spam out of ! your inbox as possible.
    • !
    • Select the Training ! dialog.  Nominate your Spam folder for spam, and your Inbox for ! good messages, and start training.
    ! To see how effective your Inbox cleanup was, you may like to try:
      !
    • Go to the Filter Now ! dialog.
    • !
    • Select your Inbox as the folder to filter.
    • !
    • Select Score messages, but ! dont perform filter action.
    • !
    • Clear both checkboxes so all messages will be scored.
    • !
    • Start the score operation.
    ! You can then look at and sort by the Spam field in your Inbox - this is ! likely to find hidden spam that you missed from your inbox cleanup. !

    Incremental Training

    ! When you drag a message to your Spam folder, it will be automatically ! trained as spam.  Thus, as the classifier misses spam (or is unsure ! about them), it learns as you correct it.
    ! If messages are dropped back into a folder being watched, they are trained as good - ! thus, the system learns what good messages look like should it ! incorrectly classify it as spam or possible spam.
    ! You will also notice a "Delete as Spam" button (in all folders except ! the Spam folder) and a "Recover from Spam" button in the Spam and Unsure ! folders.  These buttons have the same effect as the drags above. !  (Note that currently the "Recover from Spam" option will also restore the message to ! the folder from which it was originally filtered.
    !

    Viewing the Spam Score Field

    A custom property named Spam ! is added to all Outlook messages scored. This is a percentage indicating ! the likelihood of the message being spam (ie, 0% is "certain" ham; 100% ! if "certain" spam). You can teach Outlook to display this field as a ! column in any table view, like the standard Messages view.

    This takes some work, and has to be done again for every folder in ! which you want to display a Spam column:

    !
      !
    • While looking at an Outlook table view (like Messages), right-click on the line with column headers (From, Subject, To, Received, ...). In the context menu that pops up, click on Field ! Chooser. A box with title Field Chooser pops up.
    • !
    • In the drop-down list at the top of the Field Chooser window, select User Defined Fields
    • !
    • Below the drop-down, you should see a rectangular button ! with a Spam label . This should be automatically created for all folders managed by the system, but if it does not appear, you will need to add it yourself.  To do this, perform the following steps
    • !
        !
      • In the lower left corner of the Field Chooser box, click ! New.... A box with title New Field pops up.
      • !
      • In the Name: box, type Spam.
      • !
      • In the Type: dropdown list, select Percent. This is the third choice in the dropdown list. Do not select any other format -- it won't work.
      • !
      • The Format: select the first entry in the list - ! "Rounded"
      • !
      • Click OK in the New Field box. Now you're back in the Field ! Chooser box, with a new Spam ! button shown.
      • !
      !
    • Use your mouse to drag the Spam ! button to the column header position where you want to see the ! Spam column. You don't have to be precise here -- you can ! rearrange or resize the column later just by dragging it around.
    • !
    • You're done! Close the Field Chooser box.
    Outlook's standard Automatic Formatting features can also be taught how --- 5,220 ---- !

    SpamBayes Outlook Plugin


    ! This plugin provides a spam filter based on statistical analysis of ! your personal mail.  Unlike other spam detection systems, SpamBayes ! actually learns what you consider spam, and continually adapts as both ! your regular email and spam patterns change.  There are no builtin ! rules, so anything you consider spam will be treated as spam by this ! system, even if it does not conform to the traditional definitions of ! spam.
    !
    ! Some time must be spent configuring the system, and performing initial ! training.  Once this has been completed, you will find the filter ! amazingly effective and making very few errors.
    !
    ! Bear in mind that this is an early version, and there are plenty of ! things to improve, particularly related to the initial configuration and ! training process.  Please bear with us.  This is free ! software, so your help is needed - please see the main project page for ! information.
    !
    ! If you want a better picture of how the plugin actually operates, see using the plugin.  Also here you ! will find information on:
    !

    Using the plugin
    !

    ! As messages arrive, they are given a "spam score".  This score is ! the measure of how "spammy" the system has decided a mail is, with 100% ! being certain spam, and 0% meaning the message is certainly not ! spam.  The SpamBayes addin uses three different spam categories ! based on these scores to classify mail into one of three categories - ! certain spam, unsure, and good messages.  Good messages are often ! known in the anti-spam community as ham.
    ! Mail that is classified as good is never touched by this plugin - they ! will remain in your inbox, or be filtered normally by Outlook's builtin ! rules.  Mail that is classified as either unsure, or certain spam ! is moved into different folders for future review.
    ! There are three ways in which the system can get things wrong:
      !
    • A spam stays in your inbox.  This is known as a false negative.  In this case ! you can either drag the message to the Spam folder or click on the Delete as Spam button on the Outlook ! toolbar.  In both cases, the message will be trained as spam and ! will be moved to the spam folder.
    • !
    • Any message is moved to the unsure folder.  In this case, ! the system is simply unsure about the message, and moves it to the ! possible spam folder for human review.  All unsure messages should ! be manually classified; good messages can either be dragged back to the ! inbox, or have the Recover from Spam ! toolbar button clicked, while spam messages can either be dragged to the ! Spam folder or have the Delete as Spam ! toobar button clicked.  In all cases, the system will automatically ! be trained appropriately.
    • !
    • A wanted (ham) message is moved to the Spam folder. This is known ! as a false positive. In ! this case you can either drag the message back to the folder from which ! it came (generally the inbox), or click on the Recover from Spam button.  In ! both cases the message will be trained as good, and moved back to the ! original folder.
    ! Note that in all cases, as you take corrective action on the mail, the ! system is also trained.  This makes it less likely that another ! similar mail will be incorrectly classified in the future.
    !
    !

    Installing and Configuring ! the plugin

    ! If you download the installer version of this plugin (ie, you ! downloaded a .EXE file you use to install), then everthing is installed ! and just needs to be configured.  If you downloaded the sourcecode ! version of the plugin, please view README.txt for installation ! instructions.
    ! Configuring the plugin requires the following steps:
    !
    Label ! !       !

    (Help text goes here.)

    !
    ! Label:  ! ! True !    ! False !       !

    (Help text goes here.)

    !
    ! Label:  ! !
    ! ! Value Label  !
         
    ! ! ! ! ! ! !
    !
      !
    1. Confirm the plugin is installed:
      ! Start Outlook, and look at the standard Outlook toolbar.  On the ! right-hand side, you should see an Anti-Spam dropdown button.  If ! this button appears, then the plugin is correctly installed and ready to ! use.  If the button does not appear, please see XXXXXXXXXX
    2. !
    3. Create Spam and Usure folders
      ! Use the normal Outlook functions to create two new folders.  These ! can be named anything you like, and can appear anywhere in the folder ! hierarchy you choose - however, it is recommended you call them ! something like Spam and Possible Spam.
    4. !
    5. Manually create initial training data
      ! Go through your Inbox, Deleted Items ! and any other folders likely to have spam, and move as much as possible ! to the new Spam folder you just created.  Try and get as much Spam ! out of your inbox as possible, as your inbox will form the initial set ! of good messages we train on.  Don't worry too much about missing ! one or two spam - the system is likely to find them for you as we ! rescore your existing email.
    6. !
    7. Configure and Train SpamBayes
      ! Now we have our folders setup with some initial training data, we can ! configure the plugin.  From the Anti-Spam ! button on the toolbar select Anti-Spam ! Manager...  This will display the main dialog, as shown to ! the right.
    8. !
    9. Select Train Now, and the training dialog, also shown to ! the right will appear.  The Inbox will be the default for good ! messages, so you need to select your new Spam folder as the source of ! junk messages.  Ensure that Score ! messages after training is selected, so we can see how effective ! our Inbox cleanup was.
      !
    10. !
    11. Click on the Train Now button, and a progress indicator ! will be displayed as your messages are trained, and another progress ! indicator as they are scored.  Close the training window to return ! to the Anti-Spam window.
    12. !
    !
    Anti-Spam manager
    !
    The Anti-Spam Manager dialog
    !
    !
    !
    !
    The training dialog
    !
    !
    !
    !
    !

    Viewing and Using the Spam Score Field

    A custom property named Spam ! is added to all Outlook messages scored.  You can teach Outlook to ! display this field as a column in any table view, like the standard ! Messages view.

    This takes some work, and has to be done again for every folder in ! which you want to display a Spam column - typically this will be all ! folders you are filtering, and your Spam and Unsure folders.  ! Perform the following steps
    !

    ! ! ! ! ! ! ! !
    !
      !
    • While looking at an Outlook table view (like Messages), right-click on the line with column headers (From, Subject, To, Received, ...). In the context menu that pops up, click on Field ! Chooser. A box with title Field Chooser pops up.
    • !
    • In the drop-down list at the top of the Field Chooser window, select User Defined Fields
    • !
    • Below the drop-down, you should see a rectangular ! button with a Spam label . This should be automatically created for all folders managed by the system, but if it does not appear, you will need to add it yourself.  To do this, perform the following steps
    • !
        !
      • In the lower left corner of the Field Chooser box, ! click New.... A box with title New Field pops up.
      • !
      • In the Name: box, type Spam.
      • !
      • In the Type: dropdown list, select Percent. This is the third choice in the dropdown list. Do not select any other format -- it won't work.
      • !
      • The Format: select the first entry in the list - ! "Rounded".  The Field Chooser should now look like the first image ! on the right.
        !
      • !
      • Click OK in the New Field box. Now you're back in ! the Field Chooser box, with a new Spam button shown, as the second ! figure shows.
        !
      • !
      !
    • Use your mouse to drag the Spam button to the column header ! position where you want to see the Spam column. You don't have to be ! precise here -- you can rearrange or resize the column later ! just by dragging it around.
    • !
    • You're done! Close the Field Chooser box.
    • !
    !

    !
    Creating the new field
    !
    !
    ! The Field Chooser after creating the field.
    !
    !
    !
    Outlook's standard Automatic Formatting features can also be taught how *************** *** 109,114 **** for whatever reason, the Outlook Rules Wizard does not allow creating rules based on user-defined fields. That's why this addin supplies its ! own filtering rules. !

    Contributions to this documentation are welcome!

    --- 223,240 ---- for whatever reason, the Outlook Rules Wizard does not allow creating rules based on user-defined fields. That's why this addin supplies its ! own filtering rules.
    !

    Configuring the plugin for filtering

    ! Once the system is trained an configured, you are ready to enable the ! filtering capabilities.  From the Anti-Spam manager, select Filters.  From this dialog you ! can select all the folders that you wish to watch for spam.  You ! also configure the folder where you want the certain spam and unsure ! messages to be moved to.
    ! Once this is done, you simply enable filtering from the main dialog, ! and sit back and wait for spam!  See using ! the plugin for details further details.
    !

    Your help is needed!

    !

    This is free software.  Please offer any help you are able ! to.  In particular, contributions to this documentation are welcome!

    From mhammond at users.sourceforge.net Sun Mar 16 17:23:56 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:23:59 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs TrainingDialog.py,1.11,1.12 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs In directory sc8-pr-cvs1:/tmp/cvs-serv19685 Modified Files: TrainingDialog.py Log Message: Add "recore messages" option when training. Index: TrainingDialog.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/TrainingDialog.py,v retrieving revision 1.11 retrieving revision 1.12 diff -C2 -d -r1.11 -r1.12 *** TrainingDialog.py 14 Feb 2003 01:24:22 -0000 1.11 --- TrainingDialog.py 17 Mar 2003 01:23:54 -0000 1.12 *************** *** 21,24 **** --- 21,25 ---- IDC_BROWSE_SPAM = 1004 IDC_BUT_REBUILD = 1005 + IDC_BUT_RESCORE = 1006 from AsyncDialog import IDC_START, IDC_PROGRESS, IDC_PROGRESS_TEXT, AsyncDialogBase *************** *** 33,37 **** dt = [ # Dialog itself. ! ["Training", (0, 0, 241, 130), style, None, (8, "MS Sans Serif")], # Children [STATIC, ham_title, -1, ( 7, 6, 131, 11), cs ], --- 34,38 ---- dt = [ # Dialog itself. ! ["Training", (0, 0, 241, 140), style, None, (8, "MS Sans Serif")], # Children [STATIC, ham_title, -1, ( 7, 6, 131, 11), cs ], *************** *** 43,55 **** [BUTTON, 'Brow&se', IDC_BROWSE_SPAM, (184, 47, 50, 14), cs | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP], [BUTTON, 'Rebuild entire database',IDC_BUT_REBUILD, ( 7, 67, 174, 10), cs | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP], ! [BUTTON, process_start_text, IDC_START, ( 7, 109, 50, 14), cs | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP], ! ["msctls_progress32", '', IDC_PROGRESS, ( 7, 82, 166, 11), cs | win32con.WS_BORDER], ! [STATIC, '', IDC_PROGRESS_TEXT, ( 7, 98, 227, 10), cs ], ! [BUTTON, 'Close', win32con.IDOK, (184, 109, 50, 14), cs | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP], ] ! disable_while_running_ids = [IDC_BROWSE_HAM, IDC_BROWSE_SPAM, IDC_BUT_REBUILD, win32con.IDOK] def __init__ (self, mgr, trainer): --- 44,61 ---- [BUTTON, 'Brow&se', IDC_BROWSE_SPAM, (184, 47, 50, 14), cs | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP], [BUTTON, 'Rebuild entire database',IDC_BUT_REBUILD, ( 7, 67, 174, 10), cs | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP], + [BUTTON, 'Score messages after training',IDC_BUT_RESCORE,( 7, 77, 174, 10), cs | win32con.BS_AUTOCHECKBOX | win32con.WS_TABSTOP], ! [BUTTON, process_start_text, IDC_START, ( 7, 119, 50, 14), cs | win32con.BS_DEFPUSHBUTTON | win32con.WS_TABSTOP], ! ["msctls_progress32", '', IDC_PROGRESS, ( 7, 92, 166, 11), cs | win32con.WS_BORDER], ! [STATIC, '', IDC_PROGRESS_TEXT, ( 7, 108, 227, 10), cs ], ! [BUTTON, 'Close', win32con.IDOK, (184, 119, 50, 14), cs | win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP], ] ! disable_while_running_ids = [IDC_BROWSE_HAM, ! IDC_BROWSE_SPAM, ! IDC_BUT_REBUILD, ! IDC_BUT_RESCORE, ! win32con.IDOK] def __init__ (self, mgr, trainer): *************** *** 95,98 **** --- 101,108 ---- names.append(name) self.SetDlgItemText(IDC_STATIC_SPAM, "; ".join(names)) + if self.config.rescore: + self.GetDlgItem(IDC_BUT_RESCORE).SetCheck(1) + else: + self.GetDlgItem(IDC_BUT_RESCORE).SetCheck(0) def OnBrowse(self, id, code): *************** *** 114,117 **** --- 124,129 ---- def StartProcess(self): self.rebuild = self.GetDlgItem(IDC_BUT_REBUILD).GetCheck() != 0 + self.rescore = self.GetDlgItem(IDC_BUT_RESCORE).GetCheck() != 0 + self.config.rescore = self.rescore return AsyncDialogBase.StartProcess(self) *************** *** 119,123 **** self.mgr.WorkerThreadStarting() try: ! self.trainer(self.mgr, self.progress, self.rebuild) finally: self.mgr.WorkerThreadEnding() --- 131,135 ---- self.mgr.WorkerThreadStarting() try: ! self.trainer(self.mgr, self.progress, self.rebuild, self.rescore) finally: self.mgr.WorkerThreadEnding() From mhammond at users.sourceforge.net Sun Mar 16 17:24:31 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:24:34 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 train.py,1.24,1.25 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv19880 Modified Files: train.py Log Message: Allow messages to be scored after training. Index: train.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/train.py,v retrieving revision 1.24 retrieving revision 1.25 diff -C2 -d -r1.24 -r1.25 *** train.py 4 Feb 2003 02:19:05 -0000 1.24 --- train.py 17 Mar 2003 01:24:28 -0000 1.25 *************** *** 98,102 **** # Called back from the dialog to do the actual training. ! def trainer(mgr, progress, rebuild): config = mgr.config if rebuild: --- 98,102 ---- # Called back from the dialog to do the actual training. ! def trainer(mgr, progress, rebuild, rescore = True): config = mgr.config if rebuild: *************** *** 133,136 **** --- 133,146 ---- return progress.set_status("Completed training with %d spam and %d good messages" % (bayes.nspam, bayes.nham)) + if rescore: + # Setup the "filter now" config to what we want. + config = mgr.config.filter_now + config.only_unread = False + config.only_unseen = False + config.action_all = False + config.folder_ids = mgr.config.training.ham_folder_ids + mgr.config.training.spam_folder_ids + config.include_sub = mgr.config.training.ham_include_sub or mgr.config.training.spam_include_sub + import filter + filter.filterer(mgr, progress) def main(): From mhammond at users.sourceforge.net Sun Mar 16 17:24:57 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 16 20:25:00 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py,1.18,1.19 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv19981 Modified Files: filter.py Log Message: The report of the filter operation was only reporting the most recent folder filtered, not the total. Index: filter.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v retrieving revision 1.18 retrieving revision 1.19 diff -C2 -d -r1.18 -r1.19 *** filter.py 4 Mar 2003 05:50:31 -0000 1.18 --- filter.py 17 Mar 2003 01:24:54 -0000 1.19 *************** *** 99,103 **** progress.set_status("Filtering folder '%s'" % (f.name)) this_dispositions = filter_folder(f, mgr, progress) ! dispositions.update(this_dispositions) if progress.stop_requested(): return --- 99,104 ---- progress.set_status("Filtering folder '%s'" % (f.name)) this_dispositions = filter_folder(f, mgr, progress) ! for key, val in this_dispositions.items(): ! dispositions[key] = dispositions.get(key, 0) + val if progress.stop_requested(): return From anadelonbrin at users.sourceforge.net Sun Mar 16 17:49:14 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Sun Mar 16 20:49:16 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 about.html,1.7,1.8 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv28043/Outlook2000 Modified Files: about.html Log Message: Fix incorrect URL for image in documentation. Index: about.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/about.html,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** about.html 17 Mar 2003 01:23:15 -0000 1.7 --- about.html 17 Mar 2003 01:49:11 -0000 1.8 *************** *** 135,139 **** Anti-Spam manager
    The Anti-Spam Manager dialog
    --- 135,139 ---- Anti-Spam manager
    The Anti-Spam Manager dialog
    From mhammond at users.sourceforge.net Mon Mar 17 13:44:25 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Mar 17 16:44:32 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs FolderSelector.py,1.14,1.15 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs In directory sc8-pr-cvs1:/tmp/cvs-serv22566 Modified Files: FolderSelector.py Log Message: Warn, but ignore errors walking the folder tree. Index: FolderSelector.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/FolderSelector.py,v retrieving revision 1.14 retrieving revision 1.15 diff -C2 -d -r1.14 -r1.15 *** FolderSelector.py 14 Feb 2003 01:24:22 -0000 1.14 --- FolderSelector.py 17 Mar 2003 21:44:22 -0000 1.15 *************** *** 51,54 **** --- 51,55 ---- from win32com.mapi import mapi from win32com.mapi.mapitags import * + import pythoncom def _BuildFoldersMAPI(manager, folder_id): *************** *** 87,95 **** (eid_tag, eid), (name_tag, name) = row hex_eid = mapi.HexFromBin(eid) ! msgstore = session.OpenMsgStore(0, eid, None, mapi.MDB_NO_MAIL | ! mapi.MAPI_DEFERRED_ERRORS) ! hr, data = msgstore.GetProps((PR_IPM_SUBTREE_ENTRYID,), 0) ! subtree_eid = data[0][1] ! folder = msgstore.OpenEntry(subtree_eid, None, mapi.MAPI_DEFERRED_ERRORS) folder_id = hex_eid, mapi.HexFromBin(subtree_eid) spec = FolderSpec(folder_id, name) --- 88,103 ---- (eid_tag, eid), (name_tag, name) = row hex_eid = mapi.HexFromBin(eid) ! try: ! msgstore = session.OpenMsgStore(0, eid, None, mapi.MDB_NO_MAIL | ! mapi.MAPI_DEFERRED_ERRORS) ! hr, data = msgstore.GetProps((PR_IPM_SUBTREE_ENTRYID,), 0) ! subtree_eid = data[0][1] ! folder = msgstore.OpenEntry(subtree_eid, None, mapi.MAPI_DEFERRED_ERRORS) ! except pythoncom.com_error, details: ! # Some weired error opening a folder tree ! # Just print a warning and ignore the tree. ! print "Failed to open a folder for the FolderSelector dialog" ! print "Exception details:", details ! continue folder_id = hex_eid, mapi.HexFromBin(subtree_eid) spec = FolderSpec(folder_id, name) From anadelonbrin at users.sourceforge.net Mon Mar 17 16:03:28 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Mar 17 19:03:32 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 config.py,1.6,1.7 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv24815/Outlook2000 Modified Files: config.py Log Message: Fix for SF bug #704921. Added 'rescore' to training section of config container. Index: config.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/config.py,v retrieving revision 1.6 retrieving revision 1.7 diff -C2 -d -r1.6 -r1.7 *** config.py 5 Feb 2003 03:22:38 -0000 1.6 --- config.py 18 Mar 2003 00:03:26 -0000 1.7 *************** *** 55,58 **** --- 55,59 ---- # Train as stuff dragged into spam folders. train_manual_spam = True, + rescore = False, ) filter = _ConfigurationContainer( From anadelonbrin at users.sourceforge.net Mon Mar 17 16:03:29 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Mar 17 19:03:33 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs FolderSelector.py,1.15,1.16 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs In directory sc8-pr-cvs1:/tmp/cvs-serv24815/Outlook2000/dialogs Modified Files: FolderSelector.py Log Message: Fix for SF bug #704921. Added 'rescore' to training section of config container. Index: FolderSelector.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/FolderSelector.py,v retrieving revision 1.15 retrieving revision 1.16 diff -C2 -d -r1.15 -r1.16 *** FolderSelector.py 17 Mar 2003 21:44:22 -0000 1.15 --- FolderSelector.py 18 Mar 2003 00:03:26 -0000 1.16 *************** *** 95,99 **** folder = msgstore.OpenEntry(subtree_eid, None, mapi.MAPI_DEFERRED_ERRORS) except pythoncom.com_error, details: ! # Some weired error opening a folder tree # Just print a warning and ignore the tree. print "Failed to open a folder for the FolderSelector dialog" --- 95,99 ---- folder = msgstore.OpenEntry(subtree_eid, None, mapi.MAPI_DEFERRED_ERRORS) except pythoncom.com_error, details: ! # Some weird error opening a folder tree # Just print a warning and ignore the tree. print "Failed to open a folder for the FolderSelector dialog" From mhammond at users.sourceforge.net Mon Mar 17 18:50:06 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Mar 17 21:50:09 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 config.py,1.7,1.8 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv26985 Modified Files: config.py Log Message: Oops - forgot to check this one in - thanks Tony. However, I want the default for this to be True - it *should* only be used for the intial training run, and as per the documentation, I suggest that people leave "rescore" checked, and re-check their training folders for missed spam/ham. Index: config.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/config.py,v retrieving revision 1.7 retrieving revision 1.8 diff -C2 -d -r1.7 -r1.8 *** config.py 18 Mar 2003 00:03:26 -0000 1.7 --- config.py 18 Mar 2003 02:50:02 -0000 1.8 *************** *** 55,59 **** # Train as stuff dragged into spam folders. train_manual_spam = True, ! rescore = False, ) filter = _ConfigurationContainer( --- 55,60 ---- # Train as stuff dragged into spam folders. train_manual_spam = True, ! # Rescore all messages after training? ! rescore = True, ) filter = _ConfigurationContainer( From mhammond at users.sourceforge.net Mon Mar 17 19:09:06 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Mon Mar 17 22:09:08 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 filter.py,1.19,1.20 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv5215 Modified Files: filter.py Log Message: Prevent a single error filtering a message from stopping the whole filter process. Index: filter.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/filter.py,v retrieving revision 1.19 retrieving revision 1.20 diff -C2 -d -r1.19 -r1.20 *** filter.py 17 Mar 2003 01:24:54 -0000 1.19 --- filter.py 18 Mar 2003 03:09:03 -0000 1.20 *************** *** 78,82 **** only_unseen and message.GetField(mgr.config.field_score_name) is not None: continue ! disposition = filter_message(message, mgr, all_actions) dispositions[disposition] = dispositions.get(disposition, 0) + 1 --- 78,89 ---- only_unseen and message.GetField(mgr.config.field_score_name) is not None: continue ! try: ! disposition = filter_message(message, mgr, all_actions) ! except: ! import traceback ! print "Error filtering message '%s'" % (message,) ! traceback.print_exc() ! disposition = "Error" ! dispositions[disposition] = dispositions.get(disposition, 0) + 1 From mhammond at users.sourceforge.net Mon Mar 17 21:31:31 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 00:31:35 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/dialogs FilterDialog.py,1.16,1.17 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/dialogs In directory sc8-pr-cvs1:/tmp/cvs-serv17697 Modified Files: FilterDialog.py Log Message: Not all buttons were grayed during the filter. Index: FilterDialog.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/dialogs/FilterDialog.py,v retrieving revision 1.16 retrieving revision 1.17 diff -C2 -d -r1.16 -r1.17 *** FilterDialog.py 14 Feb 2003 01:24:22 -0000 1.16 --- FilterDialog.py 18 Mar 2003 05:31:28 -0000 1.17 *************** *** 305,309 **** disable_while_running_ids = [IDC_BUT_UNSEEN, IDC_BUT_UNREAD, IDC_BROWSE, win32con.IDCANCEL, ! IDC_BUT_ACT_SCORE, IDC_BUT_ACT_SCORE] def __init__(self, mgr, filterer): --- 305,309 ---- disable_while_running_ids = [IDC_BUT_UNSEEN, IDC_BUT_UNREAD, IDC_BROWSE, win32con.IDCANCEL, ! IDC_BUT_ACT_SCORE, IDC_BUT_ACT_ALL] def __init__(self, mgr, filterer): From mhammond at users.sourceforge.net Mon Mar 17 21:32:47 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 00:32:52 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 addin.py,1.52,1.53 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv17967 Modified Files: addin.py Log Message: When running from a binary installer, redirect all output to a log file in the temp directory, rolling over the previous 3 logs. Index: addin.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/addin.py,v retrieving revision 1.52 retrieving revision 1.53 diff -C2 -d -r1.52 -r1.53 *** addin.py 9 Mar 2003 01:12:58 -0000 1.52 --- addin.py 18 Mar 2003 05:32:45 -0000 1.53 *************** *** 32,39 **** win32api.GetConsoleTitle() except win32api.error: ! # No console - redirect ! import win32traceutil ! print "Outlook Spam Addin module loading" ! # Attempt to catch the most common errors - COM objects not installed. --- 32,56 ---- win32api.GetConsoleTitle() except win32api.error: ! # No console - if we are running from Python sources, ! # redirect to win32traceutil, but if running from a binary ! # install, redirect to a log file. ! # Want to move to logging module later, so for now, we ! # hack together a simple logging strategy. ! if hasattr(sys, "frozen"): ! dir = win32api.GetTempPath() ! for i in range(3,0,-1): ! try: os.unlink(os.path.join(dir, "spambayes%d.log" % (i+1))) ! except os.error: pass ! try: ! os.rename( ! os.path.join(dir, "spambayes%d.log" % i), ! os.path.join(dir, "spambayes%d.log" % (i+1)) ! ) ! except os.error: pass ! # Open this log, as unbuffered so crashes still get written. ! sys.stdout = open(os.path.join(dir,"spambayes1.log"), "wt", 0) ! sys.stderr = sys.stdout ! else: ! import win32traceutil # Attempt to catch the most common errors - COM objects not installed. From mhammond at users.sourceforge.net Mon Mar 17 21:35:31 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 00:35:39 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html,NONE,1.1 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv18698/docs Added Files: troubleshooting.html Log Message: More docs, and a new troubleshooting guide. --- NEW FILE: troubleshooting.html --- Troubleshooting the SpamBayes Outlook plugin

    Troubleshooting the SpamBayes Outlook addin

    This is a list of common problems, and hopefully their solutions.  Please feel free to suggest additional topics.  Currently, we have the following problems listed:

    Addin doesn't load

    When you start Outlook, there is no Anti-Spam item in the toolbar.  To resolve this:
    • Check the log file
    • If a log file for this session exists, then it almost certainly contains the error, so you can report a bug.
    • If no log file for this session exists, then determine your installation type.
    • If you are running from source code, please re-register the addin, as per the README.txt file.
    • If you are running a binary version, then perform the following steps:
      1. Start Outlook, and select Tools->Options to display the main Options dialog.
      2. Select the tab labelled Other, then click on the Advanced button.
      3. Click on the COM Add-Ins button.
      4. If the SpamBayes addin is not listed, then SpamBayes should be reinstalled (Note that running regsvr32.exe SpamBayesAddin.DLL from the SpamBayes directory may also solve this problem)
      5. If the SpamBayes addin is listed but not checked, then simply check it and close the dialog.
      6. If the SpamBayes addin is listed and checked, but still not working and still not creating log files, then I am stumped!

    Messages fail to filter

    This is when messages arrive, but have no Spam field.  Note that this is different from a message having an incorrect or unexpected Spam value - that case is covered next.  This is for messages that have a completely blank spam score.  To resolve this:
    • Check that filtering is enabled.  Select the Anti-Spam manager, then ensure the button Enable Filtering is checked.  If you are unable to select this button due to insufficient training information, please review the initial configuration documentation for information on training.
    • If only the occasional message fails to filter, then it is likely that the message is in a format we don't understand.  There is almost certainly an error listed in the log file.  Please report a bug, attaching both the log file and the message that caused the error.
    • If all messages fail to filter, we have a more serious problem, but again, please report a bug, attaching the log file.

    Messages have incorrect or unexpected Spam values

    This is when filtering appears to work OK, except that the spam values are wrong.  To resolve this
    • If the messages are all scoring as "unsure", with a score of 0.5, then you may have lost your training database.  From the Anti-Spam manager dropdown, check how many spam and ham have been loaded by the system.  If this number is very low (like zero!) then you probably need to perform a full re-train of your database.
    • If the messages have apparently random, but unexpected scores, then there are two possibilities; either SpamBayes is simply behaving what appears to be strangely, but really is correctly, or that some of the spam payload is invisible to SpamBayes.  In both cases, perform the following:
      • Ensure the message is selected in the Outlook preview pane, and from the Anti-Spam manager dropdown, select Show Spam clues for current message.  This should open a new mail message with the clues.
      • Part of the clues shows the body of the spam message.  If this message correctly shows the spam text, then it is likely SpamBayes is behaving correctly.  In this case, you may wish to mail the clues to the SpamBayes mailing list for help in decoding the clues, but it is likely that SpamBayes is behaving correctly given your current training data.
      • If it appears that part of the spam payload is missing, then you have probably stumbled across a bug - please mail the clues to the mailing list.

    All other problems

    If you are simply usnure about what SpamBayes is doing, please send a mail to the SpamBayes mailing list with as much information as possible.  If you are fairly sure you have struck a bug, then please report it.  Please do not mail any of the contributors directly

    Process Descriptions

    This explains some of the processes above in more detail.

    Determine your installation type.

    If you are running from Python source code, and installed Python, plus SpamBayes as seperate components, then you are running the source code version.  If you downloaded an installer .EXE file, then you are running the binary version.

    Check the log file

    Determine your installation type.  If you are running the source code version, then please see README.txt in the Outlook2000 directory.
    If you are running the binary version, then the SpamBayes addin writes a log in your Windows temp directory.  This directory is generally \WINDOWS\TEMP for Windows 9x, or \Documents and Settings\{username}\Local Settings\Temp for Windows 2000/XP.  The log file for the most recent execution of Outlook is named spambayes1.log, the second most recent is named spambayes2.log, and so on for the four previous runs.
    You can view this file with notepad.  Usually, you will simply see messages which indicate that SpamBayes is doing its job.  However, in some cases there will be errors in this file.
    If SpamBayes fails to perform correctly, please locate the log file and report a bug.

    Report a bug

    All SpamBayes bugs are maintained in this page at sourceforge.  Please have a check of the bugs already reported to see if your bug has already been reported.  If not, open a new bug, making sure to set the Category to Outlook.  Please ensure you attach the log file to the bug.
    If you are unsure about the bug, or need any assistance, please send a mail to the SpamBayes mailing list.


    From mhammond at users.sourceforge.net Mon Mar 17 21:35:30 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 00:35:41 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 about.html,1.8,1.9 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv18698 Modified Files: about.html Log Message: More docs, and a new troubleshooting guide. Index: about.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/about.html,v retrieving revision 1.8 retrieving revision 1.9 diff -C2 -d -r1.8 -r1.9 *** about.html 17 Mar 2003 01:49:11 -0000 1.8 --- about.html 18 Mar 2003 05:35:28 -0000 1.9 *************** *** 6,10 ****

    SpamBayes Outlook Plugin

    -
    This plugin provides a spam filter based on statistical analysis of your personal mail.  Unlike other spam detection systems, SpamBayes --- 6,9 ---- *************** *** 21,28 **** Bear in mind that this is an early version, and there are plenty of things to improve, particularly related to the initial configuration and ! training process.  Please bear with us.  This is free ! software, so your help is needed - please see the main project page for ! information.

    If you want a better picture of how the plugin actually operates, see troubleshooting guide should you ! have problems..  This is free software, so your help is needed - ! please see the main project ! page for information.

    If you want a better picture of how the plugin actually operates, see Configuring the plugin for filtering
  • How you can help improve this free ! software.
    !
  • Using the plugin

    --- 35,42 ----
  • Configuring the plugin for filtering
  • How you can help improve this free ! software.
  • + If you have any problems, please see the troubleshooting guide.

    Using the plugin

    *************** *** 135,140 **** Anti-Spam manager
    The Anti-Spam Manager dialog

    --- 136,141 ---- Anti-Spam manager
    The Anti-Spam Manager dialog

    *************** *** 198,203 **** style="font-style: italic;">Spam button to the column header position where you want to see the Spam column. You don't have to be ! precise here -- you can rearrange or resize the column later ! just by dragging it around.
  • You're done! Close the Field Chooser box.
  • --- 199,204 ---- style="font-style: italic;">Spam button to the column header position where you want to see the Spam column. You don't have to be ! precise here -- you can rearrange or resize the column later just by ! dragging it around.
  • You're done! Close the Field Chooser box.
  • From mhammond at users.sourceforge.net Mon Mar 17 22:27:02 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 01:27:05 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 about.html,1.9,1.10 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv31874 Modified Files: about.html Log Message: Couple of fixes. Index: about.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/about.html,v retrieving revision 1.9 retrieving revision 1.10 diff -C2 -d -r1.9 -r1.10 *** about.html 18 Mar 2003 05:35:28 -0000 1.9 --- about.html 18 Mar 2003 06:27:00 -0000 1.10 *************** *** 100,104 **** right-hand side, you should see an Anti-Spam dropdown button.  If this button appears, then the plugin is correctly installed and ready to ! use.  If the button does not appear, please see XXXXXXXXXX
  • Create Spam and Usure folders
    --- 100,105 ---- right-hand side, you should see an Anti-Spam dropdown button.  If this button appears, then the plugin is correctly installed and ready to ! use.  If the button does not appear, please see the troubleshooting guide.
  • Create Spam and Usure folders
    From mhammond at users.sourceforge.net Mon Mar 17 22:27:02 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 01:27:07 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv31874/docs Modified Files: troubleshooting.html Log Message: Couple of fixes. Index: troubleshooting.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** troubleshooting.html 18 Mar 2003 05:35:28 -0000 1.1 --- troubleshooting.html 18 Mar 2003 06:27:00 -0000 1.2 *************** *** 46,50 ****
  • If the SpamBayes addin is not listed, then SpamBayes should be reinstalled (Note that running regsvr32.exe ! SpamBayesAddin.DLL from the SpamBayes directory may also solve this problem)
  • If the SpamBayes addin is listed but not checked, then simply --- 46,50 ----
  • 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 *************** *** 88,98 **** style="font-style: italic;">Anti-Spam manager dropdown, check how many spam and ham have been loaded by the system.  If this ! number is very low (like zero!) then you probably need to perform a ! full re-train of your database.
  • If the messages have apparently random, but unexpected scores, then there are two possibilities; either SpamBayes is simply behaving what appears to be strangely, but really is correctly, or that some of ! the spam payload is invisible to SpamBayes.  In both cases, ! perform the following:
    • Ensure the message is selected in the Outlook preview pane, and --- 88,98 ---- style="font-style: italic;">Anti-Spam manager dropdown, check how many spam and ham have been loaded by the system.  If this ! number is very low (like zero!) then you probably need to perform a full ! re-train of your database.
    • If the messages have apparently random, but unexpected scores, then there are two possibilities; either SpamBayes is simply behaving what appears to be strangely, but really is correctly, or that some of ! the spam payload is invisible to SpamBayes.  In both cases, perform ! the following:
      • Ensure the message is selected in the Outlook preview pane, and *************** *** 115,121 ****

        All other problems

        If you are simply usnure about what SpamBayes is doing, please send a ! mail to the SpamBayes mailing ! list with as much information as possible.  If you are fairly ! sure you have struck a bug, then please report it.  Please do not mail any of the contributors directly

        --- 115,121 ----

        All other problems

        If you are simply usnure about what SpamBayes is doing, please send a ! mail to the SpamBayes mailing list ! with as much information as possible.  If you are fairly sure you ! have struck a bug, then please report it.  Please do not mail any of the contributors directly

        *************** *** 156,161 **** attach the log file to the bug.
        If you are unsure about the bug, or need any assistance, please send a ! mail to the SpamBayes mailing ! list.


        --- 156,160 ---- attach the log file to the bug.
        If you are unsure about the bug, or need any assistance, please send a ! mail to the SpamBayes mailing list.


        From mhammond at users.sourceforge.net Mon Mar 17 22:27:47 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 01:27:50 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/installer spambayes_addin.spec,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/installer In directory sc8-pr-cvs1:/tmp/cvs-serv32122 Modified Files: spambayes_addin.spec Log Message: Include the new docs, and avoid some "compat" modules. Index: spambayes_addin.spec =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/installer/spambayes_addin.spec,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** spambayes_addin.spec 20 Feb 2003 08:16:31 -0000 1.1 --- spambayes_addin.spec 18 Mar 2003 06:27:45 -0000 1.2 *************** *** 19,26 **** # docs extras.append( ("about.html", join(PROJECT_ROOT, "about.html"), 'DATA') ) # config extras.append( ("default_bayes_customize.ini", join(PROJECT_ROOT, "default_bayes_customize.ini"), 'DATA') ) ! excludes = ['timer', 'dde', 'win32help'] a = Analysis([INSTALLER_ROOT+'/support/_mountzlib.py', --- 19,32 ---- # docs extras.append( ("about.html", join(PROJECT_ROOT, "about.html"), 'DATA') ) + for fname in glob.glob(PROJECT_ROOT + "/docs/*"): + if os.path.isfile(fname): + extras.append( ("docs/"+basename(fname), abspath(fname), 'DATA') ) + for fname in glob.glob(PROJECT_ROOT + "/docs/images/*"): + if os.path.isfile(fname): + extras.append( ("docs/images/"+basename(fname), abspath(fname), 'DATA') ) # config extras.append( ("default_bayes_customize.ini", join(PROJECT_ROOT, "default_bayes_customize.ini"), 'DATA') ) ! excludes = ['timer', 'dde', 'win32help', 'compatsets'] a = Analysis([INSTALLER_ROOT+'/support/_mountzlib.py', From mhammond at users.sourceforge.net Mon Mar 17 22:34:18 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 01:34:24 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/installer spambayes_addin.spec,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/installer In directory sc8-pr-cvs1:/tmp/cvs-serv1705 Modified Files: spambayes_addin.spec Log Message: Excluded the wrong module! Index: spambayes_addin.spec =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/installer/spambayes_addin.spec,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** spambayes_addin.spec 18 Mar 2003 06:27:45 -0000 1.2 --- spambayes_addin.spec 18 Mar 2003 06:34:16 -0000 1.3 *************** *** 28,32 **** extras.append( ("default_bayes_customize.ini", join(PROJECT_ROOT, "default_bayes_customize.ini"), 'DATA') ) ! excludes = ['timer', 'dde', 'win32help', 'compatsets'] a = Analysis([INSTALLER_ROOT+'/support/_mountzlib.py', --- 28,32 ---- extras.append( ("default_bayes_customize.ini", join(PROJECT_ROOT, "default_bayes_customize.ini"), 'DATA') ) ! excludes = ['timer', 'dde', 'win32help', 'spambayes.compatsets'] a = Analysis([INSTALLER_ROOT+'/support/_mountzlib.py', From mhammond at users.sourceforge.net Mon Mar 17 23:37:35 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Tue Mar 18 02:37:38 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/installer spambayes_addin.iss,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/installer In directory sc8-pr-cvs1:/tmp/cvs-serv20864 Modified Files: spambayes_addin.iss Log Message: Version 0.0.2 of the binary, and offer to display about.html. Index: spambayes_addin.iss =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/installer/spambayes_addin.iss,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** spambayes_addin.iss 20 Feb 2003 08:16:31 -0000 1.1 --- spambayes_addin.iss 18 Mar 2003 07:37:32 -0000 1.2 *************** *** 5,10 **** [Setup] AppName=Spambayes Outlook Addin ! AppVerName=Spambayes Outlook Addin 0.0.1 ! AppVersion=0.0.1 DefaultDirName={pf}\Spambayes Outlook Addin DefaultGroupName=Spambayes Outlook Addin --- 5,10 ---- [Setup] AppName=Spambayes Outlook Addin ! AppVerName=Spambayes Outlook Addin 0.0.2 ! AppVersion=0.0.2 DefaultDirName={pf}\Spambayes Outlook Addin DefaultGroupName=Spambayes Outlook Addin *************** *** 15,18 **** --- 15,19 ---- Source: "dist\spambayes_addin.dll"; DestDir: "{app}"; Flags: ignoreversion regserver Source: "dist\*.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs + Source: "dist\about.html"; DestDir: "{app}"; Flags: isreadme [UninstallDelete] From anadelonbrin at users.sourceforge.net Tue Mar 18 16:50:09 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Tue Mar 18 19:50:13 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv20365/Outlook2000/docs Modified Files: troubleshooting.html Log Message: Fix incorrect URLS in documentation. Index: troubleshooting.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** troubleshooting.html 18 Mar 2003 06:27:00 -0000 1.2 --- troubleshooting.html 19 Mar 2003 00:50:07 -0000 1.3 *************** *** 74,80 ****
      • If all messages fail to filter, we have a more serious problem, but again, please report a bug, attaching the log file.
      --- 74,80 ----
    • If all messages fail to filter, we have a more serious problem, but again, please report a bug, attaching the log file.
    *************** *** 118,122 **** with as much information as possible.  If you are fairly sure you have struck a bug, then please report it.  ! Please do not mail any of the contributors directly

    Process Descriptions
    --- 118,122 ---- with as much information as possible.  If you are fairly sure you have struck a bug, then please report it.  ! Please do not mail any of the contributors directly.

    Process Descriptions
    From mhammond at users.sourceforge.net Thu Mar 20 04:08:01 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Thu Mar 20 07:08:06 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000 tester.py,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000 In directory sc8-pr-cvs1:/tmp/cvs-serv6184 Modified Files: tester.py Log Message: Was getting the wrong end of the sorted list! Also ensure db saved before test. Index: tester.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/tester.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** tester.py 4 Mar 2003 05:49:54 -0000 1.5 --- tester.py 20 Mar 2003 12:07:59 -0000 1.6 *************** *** 72,75 **** --- 72,76 ---- items.append((info.hamcount, word, info)) items.sort() + items.reverse() # Throw an error if we don't have enough tokens - otherwise # the test itself may fail, which will be more confusing than this. *************** *** 300,303 **** --- 301,305 ---- # Run the tests - called from our plugin. driver = Driver(manager) + manager.Save() # necessary after a full retrain assert driver.manager.config.filter.enabled, "Filtering must be enabled for these tests" assert driver.manager.config.training.train_recovered_spam and \ From timstone4 at users.sourceforge.net Thu Mar 20 17:33:21 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Thu Mar 20 20:33:25 2003 Subject: [Spambayes-checkins] spambayes mboxtrain.py,1.5,1.6 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv10891 Modified Files: mboxtrain.py Log Message: Added -n option to train mailboxes in "new" directory, satisfying feature request 703283. Please note that the current behavior of training only "cur" should renamin unaltered. If, at some point, everyone agrees that "new" should always be trained, it will be simple to make that behavior the default. I do not have the ability to test this feature, so I'd appreciate it if someone could give it a spin. Index: mboxtrain.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/mboxtrain.py,v retrieving revision 1.5 retrieving revision 1.6 diff -C2 -d -r1.5 -r1.6 *** mboxtrain.py 28 Feb 2003 18:06:45 -0000 1.5 --- mboxtrain.py 21 Mar 2003 01:33:19 -0000 1.6 *************** *** 33,36 **** --- 33,39 ---- -q quiet mode; no output + + -n train mail residing in "new" directory, in addition to "cur" directory, + which is always trained """ *************** *** 93,99 **** trained = 0 ! for fn in os.listdir(os.path.join(path, "cur")): counter += 1 ! cfn = os.path.join(path, "cur", fn) tfn = os.path.join(path, "tmp", "%d.%d_%d.%s" % (time.time(), pid, --- 96,102 ---- trained = 0 ! for fn in os.listdir(path): counter += 1 ! cfn = os.path.join(path, fn) tfn = os.path.join(path, "tmp", "%d.%d_%d.%s" % (time.time(), pid, *************** *** 209,213 **** mbox_train(h, path, is_spam, force) elif os.path.isdir(os.path.join(path, "cur")): ! maildir_train(h, path, is_spam, force) elif os.path.isdir(path): mhdir_train(h, path, is_spam, force) --- 212,218 ---- mbox_train(h, path, is_spam, force) elif os.path.isdir(os.path.join(path, "cur")): ! maildir_train(h, os.path.join(path, "cur"), is_spam, force) ! elif trainnew and os.path.isdir(os.path.join(path, "new")): ! maildir_train(h, os.path.join(path, "new"), is_spam, force) elif os.path.isdir(path): mhdir_train(h, path, is_spam, force) *************** *** 230,234 **** try: ! opts, args = getopt.getopt(sys.argv[1:], 'hfqd:D:g:s:') except getopt.error, msg: usage(2, msg) --- 235,239 ---- try: ! opts, args = getopt.getopt(sys.argv[1:], 'hfqnd:D:g:s:') except getopt.error, msg: usage(2, msg) *************** *** 240,243 **** --- 245,249 ---- usedb = None force = False + trainnew = False good = [] spam = [] *************** *** 247,250 **** --- 253,258 ---- elif opt == "-f": force = True + elif opt == "-n": + trainnew = True elif opt == "-q": loud = False From mhammond at users.sourceforge.net Sat Mar 22 23:21:55 2003 From: mhammond at users.sourceforge.net (Mark Hammond) Date: Sun Mar 23 02:21:58 2003 Subject: [Spambayes-checkins] spambayes/windows pop3proxy_service.py,1.2,1.3 Message-ID: Update of /cvsroot/spambayes/spambayes/windows In directory sc8-pr-cvs1:/tmp/cvs-serv19274 Modified Files: pop3proxy_service.py Log Message: Fix [ 707491 ] Pop3 proxy service code for Windows doesn't work... __file__ doesn't exist for __main__ pre Python 2.3. Index: pop3proxy_service.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/windows/pop3proxy_service.py,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** pop3proxy_service.py 12 Mar 2003 13:27:51 -0000 1.2 --- pop3proxy_service.py 23 Mar 2003 07:21:52 -0000 1.3 *************** *** 21,25 **** # need the parent on sys.path, so 'spambayes.spambayes' is a package, # and 'pop3proxy' is a module ! sb_dir = os.path.dirname(os.path.dirname(__file__)) sys.path.insert(0, sb_dir) --- 21,35 ---- # need the parent on sys.path, so 'spambayes.spambayes' is a package, # and 'pop3proxy' is a module ! try: ! # module imported by service manager, or 2.3 (in which __main__ ! # exists, *and* sys.argv[0] is always already absolute) ! this_filename=__file__ ! except NameError: ! # Python 2.3 __main__ ! # patch up sys.argv, as our cwd will confuse service registration code ! sys.argv[0] = os.path.abspath(sys.argv[0]) ! this_filename = sys.argv[0] ! ! sb_dir = os.path.dirname(os.path.dirname(this_filename)) sys.path.insert(0, sb_dir) From timstone4 at users.sourceforge.net Wed Mar 26 09:03:50 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Wed Mar 26 12:03:55 2003 Subject: [Spambayes-checkins] spambayes POP3PROXY.txt,NONE,1.1 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv27447 Added Files: POP3PROXY.txt Log Message: Start of a set of pop3proxy installation and configuration instructions, by Remi. --- NEW FILE: POP3PROXY.txt ---

    For MacOS 9

    before anything

    Due to MacOS multitasking, the popproxy does not work very fast. On a Cube or a G4 400, I found it usable but not much. YMMV. To handle network connection to localhost it is easier to add a host file. If you don't have one already, create one with any text editor.

    It name must be "hosts". It should be located in the "Preference" foder. It content should be similar to:

    localhost CNAME fbgmac.intranet.teleprosoft.com
    fbgmac.intranet.teleprosoft.com A 127.0.0.1
    

    The localhost and 127.0.0.1 values must be exactly like this. If you don't know the right value to use for fbgmac.intranet.teleprosoft.com put anything looking like this one. It have to be exactly the same for end of first line and biggining of second line.

    When this file is created, go to "TCP/IP" control panel. Set user level to Administrator. Click on "Use a host file" and select this file. Save your changes.

    On the Mac, you can transform a Python script into a double clickable applet. Just Drag & Drop the pop3proxy.py script onto the BuildApplet application. You'll get a double clickable pop3proxy application.

    Create or modify the "bayescustomize.ini" file in the Spambayes folder.

    be sur you have these lines:

    [pop3proxy]
    pop3proxy_servers: pop.videotron.ca:110,mail.ulaval.ca:110
    pop3proxy_ports: 110, 6111
    

    Configuring Entourage

    Go to the Tools menu and choose Accounts.

    Click on New and choose POP.

    Fill in the various fields. For the POP server field, put "localhost".

    For the videotron account, you are done.

    For the ulaval account, in the "Advance receive option" windows click on the "Ignore the default POP port" check box and type in 6111.

    Filtering with Entourage

    The rule can be:

    If
        Specific header: X-Spambayes-Classification Contains ham
    then    
        do nothing
    If
        Specific header: X-Spambayes-Classification Contains spam
    then    
        Move message to folder Spam
    If
        Specific header: X-Spambayes-Classification Contains unsure
    then    
        Move message to folder Unsure
    

    Configuring Eudora

    In Eudora, you will be able to reach only one pop server, since you can configure only one port number for POP. But on this server, you can access more than one account.

    Go to the Tool menu and choose Personalities.

    Create an new personality with the POP server as "localhost".

    With the proposed "bayescustomize.ini" you will be able to talk onlu to the videotron server.

    Filtering with Eudora

    The rule can be:

    Match
        Header: X-Spambayes-Classification contains ham
    Action    
        do nothing
    Match
        Header: X-Spambayes-Classification contains spam
    Action    
        Transfer To Spam
    Match
        Header: X-Spambayes-Classification contains unsure
    Action    
        Transfer To Unsure
    
    From anadelonbrin at users.sourceforge.net Wed Mar 26 20:19:43 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Wed Mar 26 23:19:46 2003 Subject: [Spambayes-checkins] spambayes POP3PROXY.txt,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv4321 Modified Files: POP3PROXY.txt Log Message: Minor fixes of the documentation - a *lot* more is needed. Note that this file needs to either be renamed to pop3proxy.html, or have all the html removed. Index: POP3PROXY.txt =================================================================== RCS file: /cvsroot/spambayes/spambayes/POP3PROXY.txt,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** POP3PROXY.txt 26 Mar 2003 17:03:47 -0000 1.1 --- POP3PROXY.txt 27 Mar 2003 04:19:41 -0000 1.2 *************** *** 5,40 **** or a G4 400, I found it usable but not much. YMMV. To handle network connection to localhost it is easier to add a ! host file. If you don't have one already, create one with any text editor. !

    It name must be "hosts". It should be located in the "Preference" foder. ! It content should be similar to:

    ! localhost CNAME fbgmac.intranet.teleprosoft.com
    ! fbgmac.intranet.teleprosoft.com A 127.0.0.1
    ! 

    The localhost and 127.0.0.1 values must be exactly like this. If you don't ! know the right value to use for fbgmac.intranet.teleprosoft.com put ! anything looking like this one. It have to be exactly the same for end of ! first line and biggining of second line. !

    When this file is created, go to "TCP/IP" control panel. Set user level to Administrator. Click on "Use a host file" and select this file. Save ! your changes. !

    On the Mac, you can transform a Python script into a double clickable ! applet. Just Drag & Drop the pop3proxy.py script onto the BuildApplet ! application. You'll get a double clickable pop3proxy application. !

    Create or modify the "bayescustomize.ini" file in the Spambayes folder. !

    be sur you have these lines:

    - [pop3proxy]
      pop3proxy_servers: pop.videotron.ca:110,mail.ulaval.ca:110
      pop3proxy_ports: 110, 6111
      

    Configuring Entourage

    !

    Go to the Tools menu and choose Accounts. !

    Click on New and choose POP. !

    Fill in the various fields. For the POP server field, put "localhost". !

    For the videotron account, you are done. !

    For the ulaval account, in the "Advance receive option" windows click on ! the "Ignore the default POP port" check box and type in 6111.

    Filtering with Entourage

    The rule can be: --- 5,47 ---- or a G4 400, I found it usable but not much. YMMV. To handle network connection to localhost it is easier to add a ! host file. If you don't have one already, create one with any text editor.

    !

    Its name must be "hosts". It should be located in the "Preference" folder. ! Its content should be similar to:

    ! localhost CNAME yourmac.example.com
    ! yourmac.example.com A 127.0.0.1
    ! 

    The localhost and 127.0.0.1 values must be exactly like this. If you don't ! know the right value to use for yourmac.example.com, put ! anything that looks like this. The end of the first line must be the same as ! the beginning of the second line.

    !

    When this file is created, go to the "TCP/IP" control panel. Set the user level to Administrator. Click on "Use a host file" and select this file. Save ! your changes.

    !

    On the Mac, you can transform a Python script into a double-clickable ! applet. Just drag & drop the pop3proxy.py script onto the BuildApplet ! application. You'll get a double-clickable pop3proxy application.

    !

    Start pop3proxy and open up a web browser to http://localhost:8880. ! Click on the Configuration link.

    !

    Ensure that the servers line looks like:

      pop3proxy_servers: pop.videotron.ca:110,mail.ulaval.ca:110
    + 
    + And that the ports line looks like: +
      pop3proxy_ports: 110, 6111
      

    Configuring Entourage

    !

    !

      !
    • Go to the Tools menu and choose Accounts.
    • !
    • Click on New and choose POP.
    • !
    • Fill in the various fields. For the POP server field, put "localhost".
    • !
    • For the videotron account, you are done.
    • !
    • For the ulaval account, in the "Advance receive option" windows click on ! the "Ignore the default POP port" check box and type in 6111.
    • !
    !

    Filtering with Entourage

    The rule can be: *************** *** 54,66 **** -

    Configuring Eudora

    In Eudora, you will be able to reach only one pop server, since you can configure only one port number for POP. But on this server, you can access ! more than one account. !

    Go to the Tool menu and choose Personalities. !

    Create an new personality with the POP server as "localhost". !

    With the proposed "bayescustomize.ini" you will be able to talk onlu to ! the videotron server.

    Filtering with Eudora

    The rule can be: --- 61,72 ----

    Configuring Eudora

    In Eudora, you will be able to reach only one pop server, since you can configure only one port number for POP. But on this server, you can access ! more than one account.

    !

    Go to the Tool menu and choose Personalities.

    !

    Create an new personality with the POP server as "localhost".

    !

    With the proposed "bayescustomize.ini" you will be able to talk only to ! the videotron server.

    Filtering with Eudora

    The rule can be: From timstone4 at users.sourceforge.net Fri Mar 28 05:57:55 2003 From: timstone4 at users.sourceforge.net (Tim Stone) Date: Fri Mar 28 08:58:01 2003 Subject: [Spambayes-checkins] spambayes notesfilter.py,1.1,1.2 Message-ID: Update of /cvsroot/spambayes/spambayes In directory sc8-pr-cvs1:/tmp/cvs-serv28054 Modified Files: notesfilter.py Log Message: A unicode print error fix Index: notesfilter.py =================================================================== RCS file: /cvsroot/spambayes/spambayes/notesfilter.py,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** notesfilter.py 25 Feb 2003 18:25:12 -0000 1.1 --- notesfilter.py 28 Mar 2003 13:57:52 -0000 1.2 *************** *** 17,26 **** Train as Spam Train as Ham ! It classifies mail that is in the inbox. Mail that is classified ! as spam is moved to the Spam folder. 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 ham. After training, spam ! is moved to the Spam folder and ham is moved to the Ham folder. Because there is no programmatic way to determine if a particular --- 17,68 ---- Train as Spam Train as Ham + + Depending on the execution parameters, it will do any or all of the + following steps, in the order given. + + 1. Train Spam from the Train as Spam folder (-t option) + 2. Train Ham from the Train as Ham folder (-t option) + 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 ! ham. After training, spam is moved to the Spam folder and ham is ! moved to the Ham folder. ! ! Replication takes place if a remote server has been specified. ! This step may take a long time, depending on replication ! parameters and how much information there is to download, as well ! as line speed and server load. Please be patient if you run with ! replication. There is currently no progress bar or anything like ! that to tell you that it's working, but it is and will complete ! eventually. There is also no mechanism for notifying you that the ! replication failed. If it did, there is no harm done, and the program ! will continue execution. ! ! Mail that is classified as Spam is moved from the inbox to the ! Train as Spam folder. You should occasionally review your Spam ! folder for Ham that has mistakenly been classified as Spam. If ! there is any there, move it to the Train as Ham folder, so ! Spambayes will be less likely to make this mistake again. ! ! Mail that is classified as Ham or Unsure is left in the inbox. ! There is currently no means of telling if a mail was classified as ! Ham or Unsure. ! ! You should occasionally select some Ham and move it to the Train ! as Ham folder, so Spambayes can tell the difference between Spam ! and Ham. The goal is to maintain a relative balance between the ! number of Spam and the number of Ham that have been trained into ! the database. These numbers are reported every time this program ! executes. However, if the amount of Spam you receive far exceeds ! the amount of Ham you receive, it may be very difficult to ! maintain this balance. This is not a matter of great concern. ! Spambayes will still make very few mistakes in this circumstance. ! But, if this is the case, you should review your Spam folder for ! falsely classified Ham, and retrain those that you find, on a ! regular basis. This will prevent statistical error accumulation, ! 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 *************** *** 28,37 **** it keeps a pickled dictionary of notes mail ids, so that once a mail has been classified, it will not be classified again. The ! non-existence of is index file, named .'sbindex', ! indicates to the system that this is the first time it has been ! run. Rather than classify the inbox in this case, the contents of ! the inbox are placed in the index to note the 'starting point' of ! the system. After that, any new messages in the inbox are ! eligible for classification. Usage: --- 70,79 ---- it keeps a pickled dictionary of notes mail ids, so that once a mail has been classified, it will not be classified again. The ! non-existence of is index file, named .sbindex, ! indicates to the system that this is an initialization execution. ! Rather than classify the inbox in this case, the contents of the ! inbox are placed in the index to note the 'starting point' of the ! system. After that, any new messages in the inbox are eligible ! for classification. Usage: *************** *** 40,44 **** note: option values with spaces in them must be enclosed in double quotes ! options: -d dbname : pickled training database filename --- 82,86 ---- note: option values with spaces in them must be enclosed in double quotes ! options: -d dbname : pickled training database filename *************** *** 57,60 **** --- 99,106 ---- -c : classify inbox -h : help + -p : prompt "Press Enter to end" before ending + This is useful for automated executions where the + statistics output would otherwise be lost when the + window closes. Examples: *************** *** 71,78 **** To Do: o Dump/purge notesindex file - o Show h:s ratio, make recommendations o Create correct folders if they do not exist o Options for some of this stuff? o pop3proxy style training/configuration interface? o Suggestions? ''' --- 117,124 ---- To Do: o Dump/purge notesindex file o Create correct folders if they do not exist o Options for some of this stuff? o pop3proxy style training/configuration interface? + o parameter to retrain? o Suggestions? ''' *************** *** 83,87 **** __author__ = "Tim Stone " ! __credits__ = "Mark Hammond, for his remarkable win32 module." from __future__ import generators --- 129,133 ---- __author__ = "Tim Stone " ! __credits__ = "Mark Hammond, for his remarkable win32 modules." from __future__ import generators *************** *** 101,124 **** import errno import win32com.client import getopt ! def classifyInbox(v, vmoveto, bayes, ldbname): # the notesindex hash ensures that a message is looked at only once ! try: ! fp = open("%s.sbindex" % (ldbname), 'rb') ! except IOError, e: ! if e.errno != errno.ENOENT: raise ! notesindex = {} ! print "notesindex file not found, this is a first time run" ! print "No classification will be performed" firsttime = 1 else: - notesindex = pickle.load(fp) - fp.close() firsttime = 0 ! docstomove = [] numham = 0 --- 147,163 ---- import errno import win32com.client + import pywintypes import getopt ! def classifyInbox(v, vmoveto, bayes, ldbname, notesindex): # the notesindex hash ensures that a message is looked at only once ! if len(notesindex.keys()) == 0: firsttime = 1 else: firsttime = 0 ! docstomove = [] numham = 0 *************** *** 126,129 **** --- 165,169 ---- numuns = 0 numdocs = 0 + doc = v.GetFirstDocument() while doc: *************** *** 135,138 **** --- 175,185 ---- numdocs += 1 + + # Notes returns strings in unicode, and the Python + # uni-decoder has trouble with these strings when + # you try to print them. So don't... + + # The com interface returns basic data types as tuples + # only, thus the subscript on GetItemValue try: *************** *** 146,152 **** body = 'No Body' ! message = "Subject: %s\r\n%s" % (subj, body) ! # generate_long_skips = True blows up on occ. options.generate_long_skips = False tokens = tokenizer.tokenize(message) --- 193,200 ---- body = 'No Body' ! message = "Subject: %s\r\n\r\n%s" % (subj, body) ! # generate_long_skips = True blows up on occasion, ! # probably due to this unicode problem. options.generate_long_skips = False tokens = tokenizer.tokenize(message) *************** *** 164,171 **** numuns += 1 ! notesindex[nid] = disposition doc = v.GetNextDocument(doc) for doc in docstomove: doc.RemoveFromFolder(v.Name) --- 212,225 ---- numuns += 1 ! notesindex[nid] = 'classified' ! try: ! print "%s spamprob is %s" % (subj[:30], prob) ! except UnicodeError: ! print " spamprob is %s" % (prob) doc = v.GetNextDocument(doc) + # docstomove list is built because moving documents in the middle of + # the classification loop looses the iterator position for doc in docstomove: doc.RemoveFromFolder(v.Name) *************** *** 177,190 **** print " %s classified as unsure" % (numuns) - fp = open("timstone.nsf.sbindex", 'wb') - pickle.dump(notesindex, fp) - fp.close() ! def processAndTrain(v, vmoveto, bayes, is_spam): if is_spam: ! str = "spam" else: ! str = "ham" print "Training %s" % (str) --- 231,241 ---- print " %s classified as unsure" % (numuns) ! def processAndTrain(v, vmoveto, bayes, is_spam, notesindex): if is_spam: ! str = options.header_spam_string else: ! str = options.header_ham_string print "Training %s" % (str) *************** *** 207,214 **** options.generate_long_skips = False tokens = tokenizer.tokenize(message) bayes.learn(tokens, is_spam) docstomove += [doc] - doc = v.GetNextDocument(doc) --- 258,276 ---- options.generate_long_skips = False tokens = tokenizer.tokenize(message) + + nid = doc.NOTEID + if notesindex.has_key(nid): + trainedas = notesindex[nid] + if trainedas == options.header_spam_string and not is_spam: + # msg is trained as spam, is to be retrained as ham + bayes.unlearn(tokens, True) + elif trainedas == options.header_ham_string and is_spam: + # msg is trained as ham, is to be retrained as spam + bayes.unlearn(tokens, False) + bayes.learn(tokens, is_spam) + notesindex[nid] = str docstomove += [doc] doc = v.GetNextDocument(doc) *************** *** 218,221 **** --- 280,284 ---- print "%s documents trained" % (len(docstomove)) + def run(bdbname, useDBM, ldbname, rdbname, foldname, doTrain, doClassify): *************** *** 225,231 **** else: bayes = storage.PickledClassifier(bdbname) ! sess = win32com.client.Dispatch("Lotus.NotesSession") ! sess.initialize() db = sess.GetDatabase("",ldbname) --- 288,311 ---- else: bayes = storage.PickledClassifier(bdbname) ! ! try: ! fp = open("%s.sbindex" % (ldbname), 'rb') ! except IOError, e: ! if e.errno != errno.ENOENT: raise ! notesindex = {} ! print "%s.sbindex file not found, this is a first time run" \ ! % (ldbname) ! print "No classification will be performed" ! else: ! notesindex = pickle.load(fp) ! fp.close() ! sess = win32com.client.Dispatch("Lotus.NotesSession") ! try: ! sess.initialize() ! except pywintypes.com_error: ! print "Session aborted" ! sys.exit() ! db = sess.GetDatabase("",ldbname) *************** *** 236,239 **** --- 316,324 ---- 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..." *************** *** 241,258 **** print "Done" - if doTrain: - processAndTrain(vtrainspam, vspam, bayes, True) - # for some reason, using inbox as a target here loses the mail - processAndTrain(vtrainham, vham, bayes, False) - if doClassify: ! classifyInbox(vinbox, vspam, bayes, ldbname) ! bayes.store() if __name__ == '__main__': try: ! opts, args = getopt.getopt(sys.argv[1:], 'htcd:D:l:r:f:') except getopt.error, msg: print >>sys.stderr, str(msg) + '\n\n' + __doc__ --- 326,346 ---- print "Done" if doClassify: ! classifyInbox(vinbox, vtrainspam, bayes, ldbname, notesindex) ! ! print "The Spambayes database currently has %s Spam and %s Ham" \ ! % (bayes.nspam, bayes.nham) ! bayes.store() + fp = open("%s.sbindex" % (ldbname), 'wb') + pickle.dump(notesindex, fp) + fp.close() + + if __name__ == '__main__': try: ! opts, args = getopt.getopt(sys.argv[1:], 'htcpd:D:l:r:f:') except getopt.error, msg: print >>sys.stderr, str(msg) + '\n\n' + __doc__ *************** *** 265,268 **** --- 353,357 ---- doTrain = False doClassify = False + doPrompt = False for opt, arg in opts: *************** *** 286,293 **** --- 375,390 ---- elif opt == '-c': doClassify = True + elif opt == '-p': + doPrompt = True if (bdbname and ldbname and sbfname and (doTrain or doClassify)): run(bdbname, useDBM, ldbname, rdbname, \ sbfname, doTrain, doClassify) + + if doPrompt: + try: + key = input("Press Enter to end") + except SyntaxError: + pass else: print >>sys.stderr, __doc__ From anadelonbrin at users.sourceforge.net Sun Mar 30 21:59:58 2003 From: anadelonbrin at users.sourceforge.net (Tony Meyer) Date: Mon Mar 31 01:00:03 2003 Subject: [Spambayes-checkins] spambayes/Outlook2000/docs troubleshooting.html,1.3,1.4 Message-ID: Update of /cvsroot/spambayes/spambayes/Outlook2000/docs In directory sc8-pr-cvs1:/tmp/cvs-serv16256/Outlook2000/docs Modified Files: troubleshooting.html Log Message: Added some more troubleshooting tips. Index: troubleshooting.html =================================================================== RCS file: /cvsroot/spambayes/spambayes/Outlook2000/docs/troubleshooting.html,v retrieving revision 1.3 retrieving revision 1.4 diff -C2 -d -r1.3 -r1.4 *** troubleshooting.html 19 Mar 2003 00:50:07 -0000 1.3 --- troubleshooting.html 31 Mar 2003 05:59:56 -0000 1.4 *************** *** 25,28 **** --- 25,38 ----

  • Check the log file
  • +
  • Make sure that you are displaying the 'Standard' toolbar
  • +
  • Try resetting the toolbars: +
    1. Select View-> + Toolbars-> + Customize to display the toolbar options dialog
    2. +
    3. Selectg the tab labelled Toolbars, + select the 'Standard' toolbar, + and click Reset
    4. +
    5. Restart Outlook.
    6. +
  • If a log file for this session exists, then it almost certainly contains the error, so you can report a bug.