📄 sb_imapfilter.py
字号:
#!/usr/bin/env python"""An IMAP filter. An IMAP message box is scanned and all non-scoredmessages are scored and (where necessary) filtered.Usage: sb_imapfilter [options] note: option values with spaces in them must be enclosed in double quotes options: -p dbname : pickled training database filename -d dbname : dbm training database filename -t : train contents of spam folder and ham folder -c : classify inbox -h : display this message -v : verbose mode -P : security option to prompt for imap password, rather than look in options["imap", "password"] -e y/n : expunge/purge messages on exit (y) or not (n) -i debuglvl : a somewhat mysterious imaplib debugging level (4 is a good level, and suitable for bug reports) -l minutes : period of time between filtering operations -b : Launch a web browser showing the user interface. -o section:option:value : set [section, option] in the options database to valueExamples: Classify inbox, with dbm database sb_imapfilter -c -d bayes.db Train Spam and Ham, then classify inbox, with dbm database sb_imapfilter -t -c -d bayes.db Train Spam and Ham only, with pickled database sb_imapfilter -t -p bayes.dbWarnings: o We never delete mail, unless you use the -e/purge option, but we do mark a lot as deleted, and your mail client might remove that for you. We try to only mark as deleted once the moved/altered message is correctly saved, but things might go wrong. We *strongly* recommend that you try this script out on mail that you can recover from somewhere else, at least at first."""from __future__ import generatorstodo = """ o IMAP supports authentication via other methods than the plain-text password method that we are using at the moment. Neither of the servers I have access to offer any alternative method, however. If someone's does, then it would be nice to offer this. Thanks to #1169939 we now support CRAM_MD5 if available. It'd still be good to support others, though. o Usernames should be able to be literals as well as quoted strings. This might help if the username/password has special characters like accented characters. o Suggestions?"""# This module is part of the SpamBayes project, which is Copyright 2002-5# The Python Software Foundation and is covered by the Python Software# Foundation license.__author__ = "Tony Meyer <ta-meyer@ihug.co.nz>, Tim Stone"__credits__ = "All the SpamBayes folk. The original filter design owed " \ "much to isbg by Roger Binns (http://www.rogerbinns.com/isbg)."try: True, Falseexcept NameError: # Maintain compatibility with Python 2.2 True, False = 1, 0# If we are running as a frozen application, then chances are that# output is just lost. We'd rather log this, like sb_server and Oulook# log, so that the user can pull up the output if possible. We could just# rely on the user piping the output appropriately, but would rather have# more control. The sb_server tray application only does this if not# running in a console window, but we do it whenever we are frozen.import osimport sysif hasattr(sys, "frozen"): # We want to move to logging module later, so for now, we # hack together a simple logging strategy. try: import win32api except ImportError: if sys.platform == "win32": # Fall back to CWD, but warn user. status = "Warning: your log is stored in the current " \ "working directory. We recommend installing " \ "the pywin32 extensions, so that the log is " \ "stored in the Windows temp directory." temp_dir = os.getcwd() else: # Try for a /tmp directory. if os.path.isdir("/tmp"): temp_dir = "/tmp" status = "Log file opened in /tmp" else: status = "Warning: your log is stored in the current " \ "working directory. If this does not suit you " \ "please let the spambayes@python.org crowd know " \ "so that an alternative can be arranged." else: temp_dir = win32api.GetTempPath() status = "Log file opened in " + temp_dir for i in range(3,0,-1): try: os.unlink(os.path.join(temp_dir, "SpamBayesIMAP%d.log" % (i+1))) except os.error: pass try: os.rename( os.path.join(temp_dir, "SpamBayesIMAP%d.log" % i), os.path.join(temp_dir, "SpamBayesIMAP%d.log" % (i+1)) ) except os.error: pass # Open this log, as unbuffered, so crashes still get written. sys.stdout = open(os.path.join(temp_dir,"SpamBayesIMAP1.log"), "wt", 0) sys.stderr = sys.stdoutimport socketimport reimport timeimport getoptimport typesimport threadimport tracebackimport emailimport email.Parserfrom getpass import getpassfrom email.Utils import parsedatetry: import cStringIO as StringIOexcept ImportError: import StringIOfrom spambayes import Statsfrom spambayes import messagefrom spambayes.Options import options, get_pathname_option, optionsPathnamefrom spambayes import tokenizer, storage, Dibblerfrom spambayes.UserInterface import UserInterfaceServerfrom spambayes.ImapUI import IMAPUserInterface, LoginFailurefrom spambayes.Version import get_current_versionfrom imaplib import IMAP4from imaplib import Time2Internaldatetry: if options["imap", "use_ssl"]: from imaplib import IMAP4_SSL as BaseIMAP else: from imaplib import IMAP4 as BaseIMAPexcept ImportError: from imaplib import IMAP4 as BaseIMAPclass BadIMAPResponseError(Exception): """An IMAP command returned a non-"OK" response.""" def __init__(self, command, response): self.command = command self.response = response def __str__(self): return "The command '%s' failed to give an OK response.\n%s" % \ (self.command, self.response)class IMAPSession(BaseIMAP): '''A class extending the IMAP4 class, with a few optimizations''' timeout = 60 # seconds def __init__(self, server, port, debug=0, do_expunge=False): # There's a tricky situation where if use_ssl is False, but we # try to connect to a IMAP over SSL server, we will just hang # forever, waiting for a response that will never come. To # get past this, just for the welcome message, we install a # timeout on the connection. Normal service is then returned. # This only applies when we are not using SSL. if not hasattr(self, "ssl"): readline = self.readline self.readline = self.readline_timeout try: BaseIMAP.__init__(self, server, port) except (BaseIMAP.error, socket.gaierror, socket.error): print "Cannot connect to server %s on port %s" % (server, port) if not hasattr(self, "ssl"): print "If you are connecting to an SSL server, please " \ "ensure that you have the 'Use SSL' option enabled." self.connected = False else: self.connected = True if not hasattr(self, "ssl"): self.readline = readline self.debug = debug self.do_expunge = do_expunge self.server = server self.port = port self.logged_in = False # For efficiency, we remember which folder we are currently # in, and only send a select command to the IMAP server if # we want to *change* folders. This functionality is used by # both IMAPMessage and IMAPFolder. self.current_folder = None # We override the base read so that we only read a certain amount # of data at a time. OS X and Python has problems with getting # large amounts of memory at a time, so maybe this will be a way we # can work around that (I don't know, and don't have a mac to test, # but we need to try something). self._read = self.read self.read = self.safe_read def readline_timeout(self): """Read line from remote, possibly timing out.""" st_time = time.time() self.sock.setblocking(False) buffer = [] while True: if (time.time() - st_time) > self.timeout: if options["globals", "verbose"]: print >> sys.stderr, "IMAP Timing out" break try: data = self.sock.recv(1) except socket.error, e: if e[0] == 10035: # Nothing to receive, keep going. continue raise if not data: break if data == '\n': break buffer.append(data) self.sock.setblocking(True) return "".join(buffer) def login(self, username, pwd): """Log in to the IMAP server, catching invalid username/password.""" assert self.connected, "Must be connected before logging in." if 'AUTH=CRAM-MD5' in self.capabilities: login_func = self.login_cram_md5 args = (username, pwd) description = "MD5" else: login_func = BaseIMAP.login # superclass login args = (self, username, pwd) description = "plain-text" try: login_func(*args) except BaseIMAP.error, e: msg = "The username (%s) and/or password (sent in %s) may " \ "be incorrect." % (username, description) raise LoginFailure(msg) self.logged_in = True def logout(self): """Log off from the IMAP server, possibly expunging. Note that most, if not all, of the expunging is probably done in SelectFolder, rather than here, for purposes of speed.""" # We may never have logged in, in which case we do nothing. if self.connected and self.logged_in and self.do_expunge: # Expunge messages from the ham, spam and unsure folders. for fol in ["spam_folder", "unsure_folder", "ham_folder"]: folder_name = options["imap", fol] if folder_name: self.select(folder_name) self.expunge() # Expunge messages from the ham and spam training folders. for fol_list in ["ham_train_folders", "spam_train_folders",]: for fol in options["imap", fol_list]: self.select(fol) self.expunge() BaseIMAP.logout(self) # superclass logout def check_response(self, command, IMAP_response): """A utility function to check the response from IMAP commands. Raises BadIMAPResponseError if the response is not OK. Returns the data segment of the response otherwise.""" response, data = IMAP_response if response != "OK": raise BadIMAPResponseError(command, IMAP_response) return data def SelectFolder(self, folder): """A method to point ensuing IMAP operations at a target folder. This is essentially a wrapper around the IMAP select command, which ignores the command if the folder is already selected.""" if self.current_folder != folder: if self.current_folder != None and self.do_expunge: # It is faster to do close() than a single # expunge when we log out (because expunge returns # a list of all the deleted messages which we don't do # anything with). self.close() self.current_folder = None if folder == "": # This is Python bug #845560 - if the empty string is # passed, we get a traceback, not just an 'invalid folder' # error, so raise our own error. raise BadIMAPResponseError("select", "Cannot have empty string as " "folder name in select") # We *always* use SELECT and not EXAMINE, because this # speeds things up considerably. response = self.select(folder, None)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -