基于数据库的新闻组系统,Python实现

基于数据库的新闻组系统,Python实现

usenet.py

#!/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/mo ... 58cbcde24613d941e9d

-------------------------------------------------------------------------
python-chinese
Post: send python-chinese@lists.python.cn
Subscribe: send subscribe to python-chinese-request@lists.python.cn
Unsubscribe: send unsubscribe to  python-chinese-request@lists.python.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 email
from messages import Messages

#print Messages.banner

# Line terminators (we always output CRLF, but accept any of CRLF, CR, LF)
CRLF = '\r\n'

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.\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 ',r'^listgroup (.+)',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 list(self):
        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")
    def listgroup(self,group):
        group = command[1]
        numbers = self.msg.listgroup(group)
        if not numbers:
            Responses(self.conn,412)
        else:
            self.conn.send("211 Article list follows\r\n")
            for number in numbers:
                self.conn.push(number+"\r\n")
            self.conn.push(".\r\n")
    def xover(self, range):   
        current = self.current_group.get_group()
        if not current:
            Responses(self.conn,412)
        else:
            first, last = range.split('-')
            xovers = self.msg.xover(current,first,last)
            self.conn.push("224 xover information follows\r\n")
            for xover in xovers:
                self.conn.push( xover + "\r\n")
            self.conn.push(".\r\n")
    def article(self,part,MessageID):
        current = self.current_group.get_group()
        if not current:
            Responses(self.conn,412)
        else:
            text = self.msg.article(current,MessageID)
            if not text:
                Responses(self.conn,423)
            else:               
                if part == 'head':
                    message = email.message_from_string(text).__dict__
                    _headers = message['_headers']
                    self.conn.push("221 "+MessageID+" <"+current+'.'+MessageID+"@lists.netkiller.org> article retrieved - head follows\r\n")
                    for key, value in _headers:
                        self.conn.push(key+': '+value+CRLF)
                elif part == 'body':
                    message = email.message_from_string(text).__dict__
                    _payload = message['_payload']
                    self.conn.push("222 "+MessageID+" <"+current+'.'+MessageID+"@lists.netkiller.org> article retrieved - body follows\r\n")
                    self.conn.push(_payload)
                elif part == 'article':
                    self.conn.push("220 "+MessageID+" <"+current+'.'+MessageID+"@lists.netkiller.org> article retrieved - head and body follows\r\n")
                    self.conn.push(text)
                self.conn.push(CRLF)
                self.conn.push(".\r\n")
               
    def commands(self, command):
        if not command: return True
        if command[0] == 'mode':
            self.conn.push(self.msg.banner)
        elif command[0] == 'list':
            self.list()
        elif command[0] == 'listgroup':
            group = command[1]
            self.listgroup(group)
        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]
            self.article('article',MessageID)
        elif command[0] =='head':
            MessageID = command[1]
            self.article('head',MessageID)
        elif command[0] =='body':
            MessageID = command[1]
            self.article('body',MessageID)
        elif command[0] == 'xover':
            range = command[1]
            self.xover(range)
        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]
                post_msg = email.message_from_string(data)
                group = post_msg['Newsgroups']
                MessageID = '<'+group+'@list.netkiller.org>'
                data = data.replace( '\r\n\r\n', '\r\n'+'Message-ID: '+MessageID+'\r\n\r\n', 1 )
                data += '\r\n'
                self.msg.post(group,MessageID,data)
                Responses(self.conn,240)
        elif command[0] == 'rset':
            self.conn.push("250 OK\r\n")
        elif command[0] =='help':
            self.conn.push('100 help text follows\r\n')
            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
    # Response numbers that are followed by additional text (e.g. article)
    LONGRESP = ['100', '215', '220', '221', '222', '224', '230', '231', '282']
    status_dict = {
        211 : '211 list of article numbers follow',
        224 : '224 Overview information follows',
        240 : '240 article posted ok',
        340 : '340 send article to be posted. End with <CR-LF>.<CR-LF>',
        
        411 : '411 no such news group',
        412 : '412 No news group current selected',
        420 : '420 No article(s) selected',
        423 : '423 no such article number in this group',

        440 : '440 posting not allowed',
        441 : '441 posting failed',
        
        0412 : '412 Not currently in newsgroup',
        502 : '502 no permission'
    }
    """
       200 server ready - posting allowed
       201 server ready - no posting allowed
       202 slave status noted
       205 closing connection - goodbye!
       211 n f l s group selected
       215 list of newsgroups follows
       220 n <a> article retrieved - head and body follow 221 n <a> article
       retrieved - head follows
       222 n <a> article retrieved - body follows
       223 n <a> article retrieved - request text separately 230 list of new
       articles by message-id follows
       231 list of new newsgroups follows
       235 article transferred ok
       240 article posted ok
   
       335 send article to be transferred.  End with <CR-LF>.<CR-LF>
       340 send article to be posted. End with <CR-LF>.<CR-LF>
   
       400 service discontinued
       411 no such news group
       412 no newsgroup has been selected
       420 no current article has been selected
       421 no next article in this group
       422 no previous article in this group
       423 no such article number in this group
       430 no such article found
       435 article not wanted - do not send it
       436 transfer failed - try again later
       437 article rejected - do not try again.
       440 posting not allowed
       441 posting failed
   
       500 command not recognized
       501 command syntax error
       502 access restriction or permission denied
       503 program fault - command not performed   
    """
    def __init__(self,conn,status):
        self.conn = conn
        if status == 205:
            pass
        if status in self.status_dict.keys():
            result = self.status_dict[status]
            if result:
                self.conn.push(result+'\r\n')
            
class Config:
    config_dict = {
        'home' : ' ',
        'host' : ' ',
        'user' : ' ',
        'passwd' : ' ',
        'banner' : ' '
    }
    def __init__(self,key):
        if not key:
            pass
        if key in self.config_dict.keys():
            result = self.config_dict[key]
            if result:
                return result
                             
def main():
    import os
    try:
        if os.name == 'nt':
            nntpd = nntp_server('127.0.0.1',119)
            asyncore.loop()
        elif os.name == 'posix':
            print 'posix'
    except KeyboardInterrupt:
        print "Crtl+C pressed. Shutting down."


if __name__ == '__main__':
    main()
messages.py

import sys,re
import MySQLdb

class Database:
    conn = None
    cursor = None
    def __init__(self):
        pass
        #self.connect()
    def connect(self):
        try:
            self.conn = MySQLdb.connect (host = "localhost",
                               user = "root",
                               passwd = "chen",
                               db = "usenet")
        except MySQLdb.Error, e:
            print "Error %d: %s" % (e.args[0], e.args[1])
            sys.exit (1)
    def fetchone(self,sql):
        self.connect()
        self.cursor = self.conn.cursor ()
        self.cursor.execute (sql)
        one = self.cursor.fetchone ()
        self.close()
        return one
    def fetchall(self,sql):
        self.connect()
        self.cursor = self.conn.cursor ()
        self.cursor.execute (sql)
        all = self.cursor.fetchall ()
        self.close()
        return all
    def execute(self,sql):
        self.connect()
        self.cursor = self.conn.cursor ()
        self.cursor.execute (sql)
        self.conn.commit()
        self.close()        
        return self.cursor.rowcount
    def close(self):
        self.cursor.close ()
        self.conn.close()
        
"""        
class abstract:
    subject = None
    mail_from = None
    rcpt_to = None
    data = None

    group = ""

    grouplist = (
        ("cn.comp.linux", 5, 2, "y"),
        ("cn.comp.freebsd", 3, 2, "y"),
        ("cn.comp.dos", 10, 4, "y"),
        ("cn.test", 5, 2, "y"),
        ("comp.lang.python", 5, 2, "y")
        )
    def welcome(self):
        return 'Welcome'
    def list (self):
        lists = []
        for name, last, first, mode in self.grouplist:
            lists.append(name+ " " + str(last) + " " + str(first) +" "+ mode)
        return lists
    def group(self,groupname):
        group_rang = ""
        for name, last, first, mode in self.grouplist:
            if name == groupname:
                group_rang = str(last - first)+" "+ str(first)+" "+str(last)
                break
        return group_rang
    def xover(self,first,last):
        xover_tmp = []
        xover_tmp.append("2       Mozilla programmer needed for children's learning program       "John Fodor, PhD" <fodor@ilt-inc.com>   Mon, 23 Jan 2006 12:01:09 -0500 <mailman.349.1138141614.8933.jobs@lists.mozilla.org>            10532   276     Xref: number1.nntp.dca.giganews.com mozilla.jobs:2")
        xover_tmp.append("3       mozilla expert needed   "Paul Sponagl" <sponagl@7val.com>       Tue, 7 Mar 2006 20:00:52 +0100  <mailman.1692.1141758057.18700.jobs@lists.mozilla.org>        2913    41      Xref: number1.nntp.dca.giganews.com mozilla.jobs:10")
        return xover_tmp
    def head(self):
        self.xover(first,last)
    def xhdr(self,first,last):
        xover_tmp = []
        xover_tmp.append('1       HI              "NNTP.HK" <admin@nntp.hk>               14 Jun 2006 14:43:05 +0800      <448faff9$1@news.nntp.hk>               1151    31      Xref: news.nntp.hk vip.cicefans:1\r\n')
        return xover_tmp
    def newgroups(self,data,time,gmt):
        lists = []
        lists.append("cn.test.os")
        lists.append("cn.test.qa")
        return lists
    (abstract)
"""

class Messages:
    banner = '200 \"Welcome to Netkiler News server\"\r\n'
    db= None
    def __init__(self):
        self.db = Database()
        #print self.db.fetchone(sql)        
    def list (self):
        sql = """SELECT id,`group`,(select max(Number) from article) as last, (select min(Number) from article) as first, p FROM list"""
        #print sql
        result = self.db.fetchall(sql)
        group = []
        for id, name, last, first, mode, in result:
            group.append(name+ " " + str(last) + " " + str(first) +" "+ mode)
        return group
    def listgroup(self,group):
        sql = "select Number from article where Newsgroups = '%s'" % (group)
        #print sql
        result = self.db.fetchall(sql)
        number = []
        for num in result:
            number.append(str(num[0]))
        return number
        
    def group(self,newsgroup):
        sql = "SELECT `group`,(select count(Number) from article) as number, (select max(Number) from article) as last, (select min(Number) from article) as first FROM list where `group` = '%s' limit 1" % (newsgroup)
        #print sql
        result=self.db.fetchone(sql)
        name, number, last, first = result
        return (str(number),str(first),str(last),name)
    def xover(self,group,first,last):
        sql = "select * from article where Newsgroups='%s' and Number BETWEEN %s AND %s" % (group,first,last)
        #print sql
        result=self.db.fetchall(sql)
        xovers = []
        for Number, MessageID, Newsgroups, Body, Date in result:
            parse = self.parseArticle('head',Body)
            if 'Xref' in set(parse):
                xref = '\t'+parse['Xref']
            else:
                xref = ''
            if 'Lines' in set(parse):
                lines = '\t'+parse['Lines']
            else:
                lines = ''
            xovers.append(str(Number)+'\t'+parse['Subject']+'\t'+parse['From']+'\t'+parse['Date']+'\t<3dc0af03@netkiller.hikz.com>\t'+str(len(Body))+'\t'+lines+xref)
        return xovers
    def post(self, Newsgroups, MessageID, text):
        sql = "insert into article( MessageID, Newsgroups, body, `date`) values('%s','%s','%s',now())" % (MessageID,Newsgroups,text)
        #print sql
        rowcount = self.db.execute(sql)
        return rowcount
    def article(self,group,id):
        sql = "select body from article where  Newsgroups='%s' and Number='%s'" % (group,id)
        #print sql
        result=self.db.fetchone(sql)
        if result:
            result = result[0]
        return result
    """
    def head(self,id):
    def body(self,id):
    """
    def parseArticle(self,position,data):
        #[a-zA-z0-9]+@[a-zA-z0-9]+.[a-zA-z]+
        head_matchs = (
            ('From', r'From: (.+ <.+>)\r\n'),
            ('Newsgroups', r'Newsgroups: (.+)\r\n'),
            ('Subject', r'Subject: (.+)\r\n'),
            ('Date', r'Date: (.+)\r\n'),
            ('Lines', r'Lines: (.+)\r\n'),
            ('Message-ID', r'Message-ID: (.+)\r\n'),
            ('MIME-Version', r'MIME-Version: (.+)\r\n'),  
            ('User-Agent', r'User-Agent: (.+)\r\n'),            
            ('Content-Type', r'Content-Type: (.+)\r\n'),  
            ('Content-Transfer-Encoding', r'Content-Transfer-Encoding: (.+)\r\n'),  
            ('Xref', r'Xref: (.+)\r\n')
        )
        """
            ('X-MSMail-Priority', r'X-MSMail-Priority: (.+)\r\n'),
            ('X-Newsreader', r'X-Newsreader: (.+)\r\n'),     
            ('X-MimeOLE', r'X-MimeOLE: (.+)\r\n'),  
            ('X-RFC2646', r'X-RFC2646: (.+)\r\n'),
        """
        body_matchs = (
            ('Body', r'\r\n\r\n(.+)\r\n')            
        )
        article_matchs = (
            ('From', r'From: (.+ <.+>)\r\n'),
            ('Newsgroups', r'Newsgroups: (.+)\r\n'),
            ('Subject', r'Subject: (.+)\r\n'),
            ('Date', r'Date: (.+)\r\n'),
            ('Lines', r'Lines: (.+)\r\n'),
            ('X-Priority', r'X-Priority: (.+)\r\n'),
            ('X-MSMail-Priority', r'X-MSMail-Priority: (.+)\r\n'),
            ('X-Newsreader', r'X-Newsreader: (.+)\r\n'),     
            ('X-MimeOLE', r'X-MimeOLE: (.+)\r\n'),  
            ('X-RFC2646', r'X-RFC2646: (.+)\r\n'),
            ('MIME-Version', r'MIME-Version: (.+)\r\n'),  
            ('User-Agent', r'User-Agent: (.+)\r\n'),
            ('Content-Type', r'Content-Type: (.+)\r\n'),  
            ('Content-Transfer-Encoding', r'Content-Transfer-Encoding: (.+)\r\n'),  
            ('Xref', r'Xref: (.+)\r\n'),
            
            ('Body', r'\r\n\r\n(.+)\r\n')
        )
        parse = {}
        if position == 'head':
            matchs = head_matchs
        elif position == 'body':
            matchs = body_matchs
        else:
            matchs = article_matchs
        for head, value in matchs:
            digs = re.compile(value,re.IGNORECASE)
            for test in digs.findall(data):
                parse[head] = test
        return parse
def main():
    test = Messages()
    #print msg.banner
    print test.list()
    print '---------------------'
    print test.listgroup('cn.test')
    print '---------------------'
    print test.group('cn.test')
    print '---------------------'
    print test.xover('cn.test','50', '100')
    print '---------------------'
    #nntp.post('test')
    #delete from article;
    text = test.article('cn.test','70')
    #print repr(text)
    print text
    print '---------------------'
    #from email.MIMEText import MIMEText
    #print MIMEText(text)['Form']
    #import email
    #msg = email.message_from_string(myString).__dict__
    #print msg['_headers']
    #print msg['_payload']
   
    #print test.parseArticle(text)
    sql = 'select * from list'
   
    db = Database()
    print db.fetchone(sql)
    print db.fetchall(sql)
   

if __name__ == '__main__':
    main()
usenet.sql

-- phpMyAdmin SQL Dump
-- version 2.7.0-rc1
-- http://www.phpmyadmin.net
--
-- 主机: localhost
-- 生成日期: 2006 年 07 月 05 日 16:24
-- 服务器版本: 4.1.18
-- PHP 版本: 5.0.5
--
-- 数据库: `usenet`
--

-- --------------------------------------------------------

--
-- 表的结构 `article`
--

DROP TABLE IF EXISTS `article`;
CREATE TABLE IF NOT EXISTS `article` (
  `Number` int(10) unsigned NOT NULL auto_increment,
  `MessageID` varchar(255) NOT NULL default '',
  `Newsgroups` varchar(255) NOT NULL default '',
  `body` text NOT NULL,
  `Date` datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (`Number`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=82 ;

--
-- 导出表中的数据 `article`
--

INSERT INTO `article` VALUES (71, '<cn.comp.os@list.netkiller.org>', 'cn.comp.os', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.comp.os\r\nSubject: ttttttttttttttt\r\nDate: Tue, 4 Jul 2006 18:38:31 +0800\r\nLines: 3\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original<cn.comp.os@list.netkiller.org>ttttttttttttttt \r\n\r\n\r\n', '2006-07-04 18:38:31');
INSERT INTO `article` VALUES (72, '<cn.comp.os@list.netkiller.org>', 'cn.comp.os', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.comp.os\r\nSubject: Re: ttttttttttttttt\r\nDate: Tue, 4 Jul 2006 18:40:31 +0800\r\nLines: 7\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Response\r\nMessage-ID: <cn.comp.os@list.netkiller.org>\r\n\r\nsssssssssssssssssss\r\n"NEO" <openunix@163.com> wrote in message news:...\r\n>\r\n>\r\n> \r\n\r\n\r\n', '2006-07-04 18:40:31');
INSERT INTO `article` VALUES (73, '<cn.test@list.netkiller.org>', 'cn.test', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.test\r\nSubject: tttttttttttt\r\nDate: Wed, 5 Jul 2006 10:32:36 +0800\r\nLines: 3\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original\r\nMessage-ID: <cn.test@list.netkiller.org>\r\n\r\ntttttttttttttt \r\n\r\n\r\n', '2006-07-05 10:32:36');
INSERT INTO `article` VALUES (74, '<cn.test@list.netkiller.org>', 'cn.test', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.test\r\nSubject: eeeeeeeeeee\r\nDate: Wed, 5 Jul 2006 11:41:52 +0800\r\nLines: 3\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original\r\nMessage-ID: <cn.test@list.netkiller.org>\r\n\r\neeeeeeee \r\n\r\n\r\n', '2006-07-05 11:41:52');
INSERT INTO `article` VALUES (75, '<cn.test@list.netkiller.org>', 'cn.test', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.test\r\nSubject: tttttttttttt\r\nDate: Wed, 5 Jul 2006 11:57:57 +0800\r\nLines: 3\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original\r\nMessage-ID: <cn.test@list.netkiller.org>\r\n\r\nttttttttttttttt \r\n\r\n\r\n', '2006-07-05 11:57:58');
INSERT INTO `article` VALUES (76, '<cn.comp.os@list.netkiller.org>', 'cn.comp.os', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.comp.os\r\nSubject: oooooooooooooo\r\nDate: Wed, 5 Jul 2006 11:58:03 +0800\r\nLines: 3\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original\r\nMessage-ID: <cn.comp.os@list.netkiller.org>\r\n\r\nooooooooooooooo \r\n\r\n\r\n', '2006-07-05 11:58:03');
INSERT INTO `article` VALUES (77, '<cn.comp.lang.python@list.netkiller.org>', 'cn.comp.lang.python', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.comp.lang.python\r\nSubject: ppppppppppppppp\r\nDate: Wed, 5 Jul 2006 11:58:09 +0800\r\nLines: 3\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original\r\nMessage-ID: <cn.comp.lang.python@list.netkiller.org>\r\n\r\npppppppppppppp \r\n\r\n\r\n', '2006-07-05 11:58:09');
INSERT INTO `article` VALUES (78, '<cn.comp.lang.python@list.netkiller.org>', 'cn.comp.lang.python', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.comp.lang.python\r\nReferences: <cn.comp.lang.python@list.netkiller.org>\r\nSubject: Re: ppppppppppppppp\r\nDate: Wed, 5 Jul 2006 13:28:17 +0800\r\nLines: 9\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Response\r\nMessage-ID: <cn.comp.lang.python@list.netkiller.org>\r\n\r\n\r\n"NEO" <openunix@163.com> wrote in message \r\nnews:cn.comp.lang.python@list.netkiller.org...\r\n> pppppppppppppp\r\n>\r\n>\r\n> \r\n\r\n\r\n', '2006-07-05 13:28:17');
INSERT INTO `article` VALUES (79, '<cn.comp.lang.python@list.netkiller.org>', 'cn.comp.lang.python', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.comp.lang.python\r\nReferences: <cn.comp.lang.python@list.netkiller.org> <cn.comp.lang.python@list.netkiller.org>\r\nSubject: Re: ppppppppppppppp\r\nDate: Wed, 5 Jul 2006 13:36:39 +0800\r\nLines: 16\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Response\r\nMessage-ID: <cn.comp.lang.python@list.netkiller.org>\r\n\r\n\r\n"NEO" <openunix@163.com> wrote in message \r\nnews:cn.comp.lang.python@list.netkiller.org...\r\n>\r\n> "NEO" <openunix@163.com> wrote in message \r\n> news:cn.comp.lang.python@list.netkiller.org...\r\n>> pppppppppppppp\r\n>>\r\n>>\r\n>>\r\n>\r\n>\r\n>\r\n> \r\n\r\n\r\n', '2006-07-05 13:36:39');
INSERT INTO `article` VALUES (80, '<cn.test@list.netkiller.org>', 'cn.test', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.test\r\nSubject: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\nDate: Wed, 5 Jul 2006 15:41:45 +0800\r\nLines: 37\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Original\r\nMessage-ID: <cn.test@list.netkiller.org>\r\n\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \r\n\r\n\r\n', '2006-07-05 15:41:45');
INSERT INTO `article` VALUES (81, '<cn.test@list.netkiller.org>', 'cn.test', 'From: "NEO" <openunix@163.com>\r\nNewsgroups: cn.test\r\nReferences: <cn.test@list.netkiller.org>\r\nSubject: Re: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\nDate: Wed, 5 Jul 2006 15:41:57 +0800\r\nLines: 42\r\nX-Priority: 3\r\nX-MSMail-Priority: Normal\r\nX-Newsreader: Microsoft Outlook Express 6.00.2900.2180\r\nX-MimeOLE: Produced By Microsoft MimeOLE V6.00.2900.2180\r\nX-RFC2646: Format=Flowed; Response\r\nMessage-ID: <cn.test@list.netkiller.org>\r\n\r\n\r\n"NEO" <openunix@163.com> wrote in message news:cn.test@list.netkiller.org...\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r\n>\r\n>\r\n> \r\n\r\n\r\n', '2006-07-05 15:41:57');

-- --------------------------------------------------------

--
-- 表的结构 `list`
--

DROP TABLE IF EXISTS `list`;
CREATE TABLE IF NOT EXISTS `list` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `group` varchar(255) NOT NULL default '',
  `last` varchar(10) NOT NULL default '',
  `first` varchar(10) NOT NULL default '',
  `p` char(1) default 'y',
  `date` datetime NOT NULL default '0000-00-00 00:00:00',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='RFC977 3.6.1. LIST' AUTO_INCREMENT=4 ;

--
-- 导出表中的数据 `list`
--

INSERT INTO `list` VALUES (1, 'cn.test', '10', '5', 'y', '0000-00-00 00:00:00');
INSERT INTO `list` VALUES (2, 'cn.comp.os', '10', '5', 'y', '2006-06-27 00:00:00');
INSERT INTO `list` VALUES (3, 'cn.comp.lang.python', '', '', 'y', '2006-07-05 00:00:00');
so good!