⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sb_server.py

📁 用python实现的邮件过滤器
💻 PY
📖 第 1 页 / 共 3 页
字号:
#!/usr/bin/env python"""The primary server for SpamBayes.Currently serves the web interface, and any configured POP3 and SMTPproxies.The POP3 proxy works with classifier.py, and adds a simpleX-Spambayes-Classification header (ham/spam/unsure) to each incomingemail.  You point the proxy at your POP3 server, and configure youremail client to collect mail from the proxy then filter on the addedheader.  Usage:    sb_server.py [options] [<server> [<server port>]]        <server> is the name of your real POP3 server        <port>   is the port number of your real POP3 server, which                 defaults to 110.        options:            -h      : Displays this help message.            -d FILE : use the named DBM database file            -p FILE : the the named Pickle database file            -l port : proxy listens on this port number (default 110)            -u port : User interface listens on this port number                      (default 8880; Browse http://localhost:8880/)            -b      : Launch a web browser showing the user interface.            -o section:option:value :                      set [section, option] in the options database                      to value        All command line arguments and switches take their default        values from the [pop3proxy] and [html_ui] sections of        bayescustomize.ini.For safety, and to help debugging, the whole POP3 conversation iswritten out to _pop3proxy.log for each run, ifoptions["globals", "verbose"] is True.To make rebuilding the database easier, uploaded messages are appendedto _pop3proxyham.mbox and _pop3proxyspam.mbox."""# 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.__author__ = "Richie Hindle <richie@entrian.com>"__credits__ = "Tim Peters, Neale Pickett, Tim Stone, all the Spambayes folk."try:    True, Falseexcept NameError:    # Maintain compatibility with Python 2.2    True, False = 1, 0try:    reversedexcept NameError:    # Maintain compatibility with Python 2.2 and 2.3    def reversed(seq):        seq = list(seq[:])        seq.reverse()        return iter(seq)todo = """Web training interface:User interface improvements: o Once the pieces are on separate pages, make the paste box bigger. o Deployment: Windows executable?  atlaxwin and ctypes?  Or just   webbrowser? o "Reload database" button.New features: o Online manual. o Links to project homepage, mailing list, etc. o List of words with stats (it would have to be paged!) a la SpamSieve.Code quality: o Cope with the email client timing out and closing the connection.Info: o Slightly-wordy index page; intro paragraph for each page. o In both stats and training results, report nham and nspam. o "Links" section (on homepage?) to project homepage, mailing list,   etc.Gimmicks: o Classify a web page given a URL. o Graphs.  Of something.  Who cares what? o NNTP proxy."""import os, sys, re, errno, getopt, time, traceback, socket, cStringIO, emailfrom thread import start_new_threadfrom email.Header import Headerimport spambayes.messagefrom spambayes import i18nfrom spambayes import Statsfrom spambayes import Dibblerfrom spambayes import storagefrom spambayes.FileCorpus import FileCorpus, ExpiryFileCorpusfrom spambayes.FileCorpus import FileMessageFactory, GzipFileMessageFactoryfrom spambayes.Options import options, get_pathname_option, _from spambayes.UserInterface import UserInterfaceServerfrom spambayes.ProxyUI import ProxyUserInterfacefrom spambayes.Version import get_current_version# Increase the stack size on MacOS X.  Stolen from Lib/test/regrtest.pyif sys.platform == 'darwin':    try:        import resource    except ImportError:        pass    else:        soft, hard = resource.getrlimit(resource.RLIMIT_STACK)        newsoft = min(hard, max(soft, 1024*2048))        resource.setrlimit(resource.RLIMIT_STACK, (newsoft, hard))# exception may be raised if we are already running and check such things.class AlreadyRunningException(Exception):    pass# number to add to STAT length for each msg to fudge for spambayes headersHEADER_SIZE_FUDGE_FACTOR = 512class ServerLineReader(Dibbler.BrighterAsyncChat):    """An async socket that reads lines from a remote server and    simply calls a callback with the data.  The BayesProxy object    can't connect to the real POP3 server and talk to it    synchronously, because that would block the process."""    def __init__(self, serverName, serverPort, lineCallback, ssl=False,                 map=None):        Dibbler.BrighterAsyncChat.__init__(self, map=map)        self.lineCallback = lineCallback        self.handled_exception = False        self.request = ''        self.set_terminator('\r\n')        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)        # create_socket creates a non-blocking socket.  This is not great,        # because then socket.connect() will return errno 10035, because        # connect takes time.  We then don't know if the connect call        # succeeded or not.  With Python 2.4, this means that we will move        # into asyncore.loop(), and if the connect does fail, have a        # loop something like 'while True: log(error)', which fills up        # stdout very fast.  Non-blocking is also a problem for ssl sockets.        self.socket.setblocking(1)        try:            self.connect((serverName, serverPort))        except socket.error, e:            error = "Can't connect to %s:%d: %s" % (serverName, serverPort, e)            # Some people have their system setup to check mail very            # frequently, but without being clever enough to check whether            # the network is available.  If we continually print the            # "can't connect" error, we use up lots of CPU and disk space.            # To avoid this, if not verbose only print each distinct error            # once per hour.            # See also: [ 1113863 ] sb_tray eats all cpu time            now = time.time()            then = time.time() - 3600            if error not in state.reported_errors or \               options["globals", "verbose"] or \               state.reported_errors[error] < then:                print >>sys.stderr, error                # Record this error in the list of ones we have seen this                # session.                state.reported_errors[error] = now            self.lineCallback('-ERR %s\r\n' % error)            self.lineCallback('')   # "The socket's been closed."            self.close()        else:            if ssl:                try:                    self.ssl_socket = socket.ssl(self.socket)                except socket.sslerror, why:                    if why[0] == 1: # error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol'                        # Probably not SSL after all.                        print >>sys.stderr, "Can't use SSL"                    else:                        raise                else:                    self.send = self.send_ssl                    self.recv = self.recv_ssl            self.socket.setblocking(0)                def send_ssl(self, data):        return self.ssl_socket.write(data)    def handle_expt(self):        # Python 2.4's system of continuously pumping error messages        # is stupid.  Print an error once, and then ignore.        if not self.handled_exception:            print >> sys.stderr, "Unhandled exception in ServerLineReader"            self.handled_exception = True    def recv_ssl(self, buffer_size):        try:            data = self.ssl_socket.read(buffer_size)            if not data:                # a closed connection is indicated by signaling                # a read condition, and having recv() return 0.                self.handle_close()                return ''            else:                return data        except socket.sslerror, why:            if why[0] == 6: # 'TLS/SSL connection has been closed'                self.handle_close()                return ''            elif why[0] == 2: # 'The operation did not complete (read)'                return ''            else:                raise    def collect_incoming_data(self, data):        self.request = self.request + data    def found_terminator(self):        self.lineCallback(self.request + '\r\n')        self.request = ''    def handle_close(self):        self.lineCallback('')        self.close()        try:            del self.ssl_socket        except AttributeError:            passclass POP3ProxyBase(Dibbler.BrighterAsyncChat):    """An async dispatcher that understands POP3 and proxies to a POP3    server, calling `self.onTransaction(request, response)` for each    transaction. Responses are not un-byte-stuffed before reaching    self.onTransaction() (they probably should be for a totally generic    POP3ProxyBase class, but BayesProxy doesn't need it and it would    mean re-stuffing them afterwards).  self.onTransaction() should    return the response to pass back to the email client - the response    can be the verbatim response or a processed version of it.  The    special command 'KILL' kills it (passing a 'QUIT' command to the    server).    """    def __init__(self, clientSocket, serverName, serverPort,                 ssl=False, map=Dibbler._defaultContext._map):        Dibbler.BrighterAsyncChat.__init__(self, clientSocket)        self.request = ''        self.response = ''        self.set_terminator('\r\n')        self.command = ''           # The POP3 command being processed...        self.args = []              # ...and its arguments        self.isClosing = False      # Has the server closed the socket?        self.seenAllHeaders = False # For the current RETR or TOP        self.startTime = 0          # (ditto)        if not self.onIncomingConnection(clientSocket):            # We must refuse this connection, so pass an error back            # to the mail client.            self.push("-ERR Connection not allowed\r\n")            self.close_when_done()            return        self.serverSocket = ServerLineReader(serverName, serverPort,                                             self.onServerLine, ssl, map)    def onIncomingConnection(self, clientSocket):        """Checks the security settings."""        # Stolen from UserInterface.py        remoteIP = clientSocket.getpeername()[0]        trustedIPs = options["pop3proxy", "allow_remote_connections"]        if trustedIPs == "*" or remoteIP == clientSocket.getsockname()[0]:            return True        trustedIPs = trustedIPs.replace('.', '\.').replace('*', '([01]?\d\d?|2[0-4]\d|25[0-5])')        for trusted in trustedIPs.split(','):            if re.search("^" + trusted + "$", remoteIP):                return True        return False    def onTransaction(self, command, args, response):        """Overide this.  Takes the raw request and the response, and        returns the (possibly processed) response to pass back to the        email client.        """        raise NotImplementedError    def onServerLine(self, line):        """A line of response has been received from the POP3 server."""        isFirstLine = not self.response        self.response = self.response + line        # Is this the line that terminates a set of headers?        self.seenAllHeaders = self.seenAllHeaders or line in ['\r\n', '\n']        # Has the server closed its end of the socket?        if not line:            self.isClosing = True        # If we're not processing a command, just echo the response.        if not self.command:            self.push(self.response)            self.response = ''        # Time out after some seconds (30 by default) for message-retrieval        # commands if all the headers are down.  The rest of the message        # will proxy straight through.        # See also [ 870524 ] Make the message-proxy timeout configurable        if self.command in ['TOP', 'RETR'] and \           self.seenAllHeaders and time.time() > \           self.startTime + options["pop3proxy", "retrieval_timeout"]:            self.onResponse()            self.response = ''        # If that's a complete response, handle it.        elif not self.isMultiline() or line == '.\r\n' or \           (isFirstLine and line.startswith('-ERR')):            self.onResponse()            self.response = ''    def isMultiline(self):        """Returns True if the request should get a multiline        response (assuming the response is positive).        """        if self.command in ['USER', 'PASS', 'APOP', 'QUIT',                            'STAT', 'DELE', 'NOOP', 'RSET', 'KILL']:            return False        elif self.command in ['RETR', 'TOP', 'CAPA']:            return True        elif self.command in ['LIST', 'UIDL']:            return len(self.args) == 0        else:            # Assume that an unknown command will get a single-line            # response.  This should work for errors and for POP-AUTH,            # and is harmless even for multiline responses - the first            # line will be passed to onTransaction and ignored, then the            # rest will be proxied straight through.            return False    def collect_incoming_data(self, data):        """Asynchat override."""        self.request = self.request + data    def found_terminator(self):        """Asynchat override."""        verb = self.request.strip().upper()        if verb == 'KILL':            self.socket.shutdown(2)            self.close()            raise SystemExit        elif verb == 'CRASH':            # For testing            x = 0

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -