#!/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/R...de24613d941e9d
-------------------------------------------------------------------------
python-chinese
Post: send py************@ lists.python.cn
Subscribe: send subscribe to py************* *******@lists.p ython.cn
Unsubscribe: send unsubscribe to
py************* *******@lists.p ython.cn
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.dispa tcher):
channel_counter = 0
def __init__ (self, host, port):
asyncore.dispat cher.__init__ (self)
self.create_soc ket (socket.AF_INET , socket.SOCK_STR EAM)
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(se lf):
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_termin ator ('\r\n')
self.server = server
self.server.cha nnel_counter = self.server.cha nnel_counter + 1
self.id = self.server.cha nnel_counter
#self.sender = nntp_sender (self, server.there)
#self.sender.id = self.id
self.buffer = ''
self.handle_con nect()
self.current_gr oup = group_selected( None)
def handle_connect (self):
if self.connected :
self.push(Messa ges.banner)
def collect_incomin g_data (self, data):
self.buffer = self.buffer + data
def found_terminato r (self):
data = self.buffer
self.buffer = ''
if self.get_termin ator() == '\r\n':
parse = self.isCommand( data)
if parse:
parse[0] = parse[0].lower()
self.log('comma nd:'+parse[0])
usenet(self,par se,self.current _group)
if parse[0] == 'post':
self.set_termin ator ('.\r\n')
if parse[0] == 'quit':
self.handle_clo se()
else:
usenet(self,['post',data],self.current_g roup)
self.set_termin ator ('\r\n')
message = '<== (%d) %s' % (self.server.ch annel_counter,
repr(data))
self.log(messag e)
#self.log('curr ent:'+self.curr ent_group.get_g roup())
#self.sender.pu sh (data + '\n')
def close_when_done ():
pass
def handle_close (self):
self.log('Closi ng')
self.server.cha nnel_counter = self.server.cha nnel_counter - 1
#self.sender.cl ose()
self.close()
def isCommand(self, data):
rcommand = (
r'^mode [reader|stream]',
r'^list$',r'^li st
[active|active.t imes|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'^ne xt$',r'^last$',
r'^ihave$',r'^s lave$',
r'^help$',r'^qu it$'
)
parse = []
for command in rcommand:
digs = re.compile(comm and,re.IGNORECA SE)
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_termin ator (None)
self.create_soc ket (socket.AF_INET , socket.SOCK_STR EAM)
self.buffer = ''
self.set_termin ator ('\n')
self.connect (address)
def handle_connect (self):
print 'Connected'
def collect_incomin g_data (self, data):
self.buffer = self.buffer + data
def found_terminato r (self):
data = self.buffer
self.buffer = ''
print '==(%d) %s' % (self.id, repr(data))
self.receiver.p ush (data + '\n')
def handle_close (self):
self.receiver.c lose()
self.close()
"""
class group_selected:
#current = None
def __init__(self,v alue):
self.set_group( value)
def set_group(self, value):
self.current = value
def get_group(self) :
return self.current
class usenet:
#threading.Thre ad
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|Numbe r] \r\n
body [MessageID|Numbe r] \r\n
check MessageID \r\n
date \r\n
group newsgroup \r\n
head [MessageID|Numbe r] \r\n
help \r\n
ihave \r\n
last \r\n
list [active|active.t imes|newsgroups |subscriptions] \r\n
listgroup newsgroup \r\n
mode stream \r\n
mode reader \r\n
newgroups yymmdd hhmmss [GMT] [<distribution s>] \r\n
newnews newsgroups yymmdd hhmmss [GMT] [<distribution s>] \r\n
next \r\n
post \r\n
slave \r\n
stat [MessageID|Numbe r] \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,cu rrent_group):
self.conn = conn
self.buffer = buffer
self.msg = Messages()
self.current_gr oup = current_group
command[0] = command[0].lower()
self.commands(c ommand)
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_gr oup.set_group(c urrent)
number,first,la st,group = isNone
self.conn.push( "211 " +number+' '+first+' '+last+'
'+group+ " selected\r\n")
elif command[0] == 'newgroups':
#newgroups = self.msg.newgro ups(950803,1927 08,'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.articl e(MessageID)
self.conn.push( "220 "+MessageID +"
<ma**********@l ists.mozilla.or garticle 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 +"
<ma**********@l ists.mozilla.or g>\r\n")
self.conn.push( ".\r\n")
elif command[0] =='body':
MessageID = command[0]
self.conn.push( "222 "+MessageID +"
<ma**********@l ists.mozilla.or g>\r\n")
self.conn.push( ".\r\n")
elif command[0] == 'xover':
if not self.current_gr oup.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(d ata)
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,c onn,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('12 7.0.0.1',119)
asyncore.loop()
except KeyboardInterru pt:
print "Crtl+C pressed. Shutting down."
if __name__ == '__main__':
main()