#!/usr/bin/python # -*- coding: utf-8 -*- """ Project: Network News Transport Protocol Server Program Description: 基于数据库的新闻组,实现BBS前端使用NNTP协议来访问贴子 Reference: NNTP协议: http://www.mibsoftware.com/userkt/0099.htm 正则表达式: http://wiki.woodpecker.org.cn/moin/RegExpInPython#head-2358765384844ed72f01658cbcde24613d941e9d
------------------------------------------------------------------------- python-chinese Post: send [EMAIL PROTECTED] Subscribe: send subscribe to [EMAIL PROTECTED] Unsubscribe: send unsubscribe to [EMAIL PROTECTED] Detail Info: http://python.cn/mailman/listinfo/python-chinese ------------------------------------------------------------------------- """ import sys import socket import threading import time import asyncore, asynchat import string, StringIO, re #from netkiller import * #import testMessages from messages import Messages #print Messages.banner class nntp_server (asyncore.dispatcher): channel_counter = 0 def __init__ (self, host, port): asyncore.dispatcher.__init__ (self) self.create_socket (socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() self.there = (host, port) self.bind (self.there) self.listen (5) def handle_connect(self): pass def handle_accept (self): conn, addr = self.accept() nntp_receiver (self, (conn, addr)) def handle_error(self): pass def handle_close (self): self.close() class nntp_receiver (asynchat.async_chat): def __init__ (self, server, (conn, addr)): asynchat.async_chat.__init__ (self, conn) self.set_terminator ('\r\n') self.server = server self.server.channel_counter = self.server.channel_counter + 1 self.id = self.server.channel_counter #self.sender = nntp_sender (self, server.there) #self.sender.id = self.id self.buffer = '' self.handle_connect() self.current_group = group_selected(None) def handle_connect (self): if self.connected : self.push(Messages.banner) def collect_incoming_data (self, data): self.buffer = self.buffer + data def found_terminator (self): data = self.buffer self.buffer = '' if self.get_terminator() == '\r\n': parse = self.isCommand(data) if parse: parse[0] = parse[0].lower() self.log('command:'+parse[0]) usenet(self,parse,self.current_group) if parse[0] == 'post': self.set_terminator ('.\r\n') if parse[0] == 'quit': self.handle_close() else: usenet(self,['post',data],self.current_group) self.set_terminator ('\r\n') message = '<== (%d) %s' % (self.server.channel_counter, repr(data)) self.log(message) #self.log('current:'+self.current_group.get_group()) #self.sender.push (data + '\n') def close_when_done(): pass def handle_close (self): self.log('Closing') self.server.channel_counter = self.server.channel_counter - 1 #self.sender.close() self.close() def isCommand(self,data): rcommand = ( r'^mode [reader|stream]', r'^list$',r'^list [active|active.times|newsgroups|subscriptions]', r'^xover [0-9]+-[0-9]+', r'^newgroups [0-9]+ [0-9]+ ', r'^group .+', r'^newgroups [0-9]+ [0-9]+ [a-zA-Z]', r'^head [0-9]+', r'^body [0-9]+', r'^article [0-9]+', r'^post$',r'^next$',r'^last$', r'^ihave$',r'^slave$', r'^help$',r'^quit$' ) parse = [] for command in rcommand: digs = re.compile(command,re.IGNORECASE) if digs.match(data): parse = data.split(' ') return parse self.push("500 command not recognized\r\n") return None """ class nntp_sender (asynchat.async_chat): def __init__ (self, receiver, address): asynchat.async_chat.__init__ (self) self.receiver = receiver self.set_terminator (None) self.create_socket (socket.AF_INET, socket.SOCK_STREAM) self.buffer = '' self.set_terminator ('\n') self.connect (address) def handle_connect (self): print 'Connected' def collect_incoming_data (self, data): self.buffer = self.buffer + data def found_terminator (self): data = self.buffer self.buffer = '' print '==> (%d) %s' % (self.id, repr(data)) self.receiver.push (data + '\n') def handle_close (self): self.receiver.close() self.close() """ class group_selected: #current = None def __init__(self,value): self.set_group(value) def set_group(self,value): self.current = value def get_group(self): return self.current class usenet: #threading.Thread conn = None addr = None buffer = None msg = None conn = None current_group = None HELP = """\ HELP \r\n 100 Legal commands \r\n authinfo user Name|pass Password \r\n article [MessageID|Number] \r\n body [MessageID|Number] \r\n check MessageID \r\n date \r\n group newsgroup \r\n head [MessageID|Number] \r\n help \r\n ihave \r\n last \r\n list [active|active.times|newsgroups|subscriptions] \r\n listgroup newsgroup \r\n mode stream \r\n mode reader \r\n newgroups yymmdd hhmmss [GMT] [<distributions>] \r\n newnews newsgroups yymmdd hhmmss [GMT] [<distributions>] \r\n next \r\n post \r\n slave \r\n stat [MessageID|Number] \r\n takethis MessageID \r\n xgtitle [group_pattern] \r\n xhdr header [range|MessageID] \r\n xover [range] \r\n xpat header range|MessageID pat [morepat...] \r\n . \r\n""" def __init__(self, conn,command,current_group): self.conn = conn self.buffer = buffer self.msg = Messages() self.current_group = current_group command[0] = command[0].lower() self.commands(command) def welcome(self): self.conn.send(self.msg.banner) def commands(self, command): if not buffer: return True if command[0] == 'mode': self.conn.push(self.msg.banner) elif command[0] == 'list': lists = self.msg.list() length = str(len(lists)) self.conn.send("215 list of newsgroups follows\r\n") for group in lists: self.conn.push(group+"\r\n") self.conn.push(".\r\n") elif command[0] == 'group': current = command[1] isNone = self.msg.group(current) if not isNone: Responses(self.conn,411) else: self.current_group.set_group(current) number,first,last,group = isNone self.conn.push("211 " +number+' '+first+' '+last+' '+group+ " selected\r\n") elif command[0] == 'newgroups': #newgroups = self.msg.newgroups(950803,192708,'GMT') #length = len(newgroups) self.conn.push("231 list of new newsgroups follows.\r\n") #for name in newgroups: # self.conn.push(name+"\r\n") self.conn.push(".\r\n") elif command[0] =='article': MessageID = command[1] text = self.msg.article(MessageID) self.conn.push("220 "+MessageID+" <[EMAIL PROTECTED]> article retrieved - head and body follows\r\n") self.conn.push(text) self.conn.push(".\r\n") elif command[0] =='head': MessageID = command[1] self.conn.push("221 "+MessageID+" <[EMAIL PROTECTED]>\r\n") self.conn.push(".\r\n") elif command[0] =='body': MessageID = command[0] self.conn.push("222 "+MessageID+" <[EMAIL PROTECTED]>\r\n") self.conn.push(".\r\n") elif command[0] == 'xover': if not self.current_group.get_group(): Responses(self.conn,412) return xover = command[1] first, last = xover.split('-') xovers = self.msg.xover(first,last) #length = len(xovers) self.conn.push("224 xover information follows\r\n") """ 412 no newsgroup has been selected """ for xover in xovers: self.conn.push( xover + "\r\n") self.conn.push(".\r\n") elif command[0] =='xhdr': xhdr = command[1] subject, range = xhdr.split(' ') first, last = range.split('-') self.msg.xhdr('subject',first,last) #length = len(xover_list) self.conn.push("221 "+subject+" field follows\r\n") for xo in xover_list: self.conn.send( xo + "\r\n") self.conn.send(".\r\n") elif command[0] == 'post': if len(command) == 1: self.conn.push("340 send article\r\n") elif len(command) == 2: data = command[1] self.msg.post(data) Responses(self.conn,240) elif command[0] == 'rset': self.conn.push("250 OK\r\n") elif command[0] =='help': self.conn.push(self.HELP) elif command[0] =='quit': self.conn.push("205 closing connection - goodbye!\r\n") return True def close(self): self.conn.close() self.buffer = None self.conn = None self.addr = None class Responses: conn = None def __init__(self,conn,status): self.conn = conn if status == 205: pass elif status == 224: self.conn.push('224 Overview information follows\r\n') elif status == 240: self.conn.push('240 article posted ok\r\n') elif status == 340: self.conn.push('340 send article to be posted. End with <CR-LF>.<CR-LF>\r\n') elif status == 411: self.conn.push('411 no such news group\r\n') elif status == 412: self.conn.push('412 No news group current selected\r\n') elif status == 420: self.conn.push('420 No article(s) selected\r\n') elif status == 440: self.conn.push('440 posting not allowed\r\n') elif status == 441: self.conn.push('441 posting failed\r\n') elif status == 502: self.conn.push('502 no permission\r\n') def main(): try: nntpd = nntp_server('127.0.0.1',119) asyncore.loop() except KeyboardInterrupt: print "Crtl+C pressed. Shutting down." if __name__ == '__main__': main() -- http://mail.python.org/mailman/listinfo/python-list