keep a copy of needed library in same cvs, just in case
This commit is contained in:
parent
1d7c082ddf
commit
f74e5ddd9d
1 changed files with 319 additions and 0 deletions
319
trunk/ripnews/net/nntp.rb
Normal file
319
trunk/ripnews/net/nntp.rb
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
#################################
|
||||
#
|
||||
# nntp.rb - an NNTP client implementing RFC 977
|
||||
# ported from the Python code by Jefferson Heard
|
||||
# this software is released under the terms of the GNU Library General Public License
|
||||
# (C) 2001, Jefferson Heard
|
||||
#
|
||||
# Contributors: Jefferson Heard, Ward Wouts
|
||||
#
|
||||
# Release History
|
||||
# 0.1: 11.7.2001 - Initial revision.
|
||||
# 0.2: 11-9-2001 - fixed regexp bugs,
|
||||
# fixed XHDR bugs,
|
||||
# made internal methods private,
|
||||
# changed constructor default arg
|
||||
# 0.3: 11-14-2001 - Fixed numerous bugs and made things a little cleaner
|
||||
# as per the suggestions of Ward Wouts
|
||||
# 0.4: 11-15-2001 - Fixed statcmd bug - Ward Wouts
|
||||
# 0.5: 12-06-2001 - Fixed post buf - Ozawa, Sakuro
|
||||
#################################
|
||||
|
||||
require 'socket'
|
||||
require 'net/protocol'
|
||||
|
||||
module Net
|
||||
|
||||
# Exceptions raised by NNTP
|
||||
|
||||
class NNTPError < RuntimeError; end
|
||||
class NNTPReplyError < NNTPError; end
|
||||
class NNTPTemporaryError < NNTPError; end
|
||||
class NNTPPermanentError < NNTPError; end
|
||||
class NNTPDataError < NNTPError; end
|
||||
|
||||
class NNTP
|
||||
NNTP_PORT = 119
|
||||
LONGRESP = ['100', '215', '220', '221', '222', '224', '230', '231', '282']
|
||||
CRLF = "\r\n"
|
||||
|
||||
def initialize(host, port=NNTP_PORT, user=nil, password=nil, readermode=nil)
|
||||
@debuglevel = 2
|
||||
@host = host
|
||||
if port then @port = port else @port = NNTP_PORT end
|
||||
@socket = TCPSocket.new @host, @port
|
||||
@welcome = getresp
|
||||
readermode_afterauth = false
|
||||
|
||||
if readermode
|
||||
begin
|
||||
@welcome = shortcmd('mode reader')
|
||||
rescue NNTPPermanentError
|
||||
rescue NNTPTemporaryError
|
||||
if user and $!.response[0...3] == '480'
|
||||
readermode_afterauth = true
|
||||
else
|
||||
raise
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if user
|
||||
resp = shortcmd "authinfo user #{user}"
|
||||
if resp[0...3] == '381' # then we need a password
|
||||
raise NNTPReplyError, resp, caller unless password
|
||||
resp = shortcmd "authinfo pass #{password}"
|
||||
raise NNTPPermanentError, resp, caller unless resp[0...3] == '281'
|
||||
end
|
||||
end
|
||||
|
||||
if readermode_afterauth
|
||||
begin
|
||||
@welcome = shortcmd('mode reader')
|
||||
rescue NNTPPermanentError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def welcome
|
||||
puts "*welcome*, #{@welcome}" if @debuglevel > 0
|
||||
return @welcome
|
||||
end
|
||||
|
||||
attr_writer :debuglevel
|
||||
|
||||
def putline(line)
|
||||
puts '*put* '+line+'\r\n' if @debuglevel > 1
|
||||
@socket.send "#{line}\r\n", 0
|
||||
end
|
||||
|
||||
def putcmd(cmd)
|
||||
puts "*cmd* #{cmd}" if @debuglevel > 0
|
||||
putline cmd
|
||||
end
|
||||
|
||||
def getline
|
||||
line = ''
|
||||
line.concat @socket.recv 1 until line.length > 2 and line[-1] == "\n" or line[-2..-1] == "\r\n"
|
||||
puts '*getline* '+line if @debuglevel > 0
|
||||
line = line[0...-2] if line[-2..-1] == "\r\n"
|
||||
line = line[0...-1] if "\r\n".include? line[-1].to_s
|
||||
return line
|
||||
end
|
||||
|
||||
def getresp
|
||||
resp = getline
|
||||
puts "*getresp* #{resp}" if @debuglevel > 0
|
||||
c = resp[0]
|
||||
case c
|
||||
when c == '4' then raise NNTPTemporaryError, resp, caller
|
||||
when c == '5' then raise NNTPPermanentError, resp, caller
|
||||
when '123'.include?(c) then raise NNTPProtocolError, resp, caller
|
||||
end
|
||||
return resp
|
||||
end
|
||||
|
||||
def getlongresp
|
||||
resp = getresp
|
||||
raise NNTPReplyError, resp, caller unless LONGRESP.include? resp[0...3]
|
||||
list = []
|
||||
while true
|
||||
line = getline
|
||||
break if line == '.'
|
||||
line = line[1..-1] if line.to_s[0...2] == '..'
|
||||
list << line
|
||||
end
|
||||
return resp, list
|
||||
end
|
||||
|
||||
def shortcmd(line)
|
||||
putcmd line
|
||||
return getresp
|
||||
end
|
||||
|
||||
def longcmd(line)
|
||||
putcmd line
|
||||
return getlongresp
|
||||
end
|
||||
|
||||
def newgroups(date, time)
|
||||
return longcmd "NEWGROUPS #{date.to_s} #{time.to_s}"
|
||||
end
|
||||
|
||||
def newnews(group, date, time)
|
||||
return longcmd "NEWNEWS #{group} #{date.to_s} #{time.to_s}"
|
||||
end
|
||||
|
||||
def list
|
||||
resp, list = longcmd "LIST"
|
||||
list.each_index {|ix|
|
||||
list[ix] = list[ix].split " "
|
||||
}
|
||||
return resp, list
|
||||
end
|
||||
|
||||
def group(name)
|
||||
resp = shortcmd "GROUP #{name}"
|
||||
raise NNTPReplyError, resp, caller unless resp[0...3] == '211'
|
||||
words = resp.split " "
|
||||
count, first, last = 0
|
||||
n = words.length
|
||||
if n>1
|
||||
count = words[1]
|
||||
if n>2
|
||||
first = words[2]
|
||||
if n>3
|
||||
last = words[3]
|
||||
if n>4
|
||||
name = words[4].downcase
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return resp, count, first, last, name
|
||||
end
|
||||
|
||||
def help
|
||||
return longcmd "HELP"
|
||||
end
|
||||
|
||||
def statparse(resp)
|
||||
raise NNTPReplyError, resp, caller unless resp[0...2] == '22'
|
||||
words = resp.split " "
|
||||
nr = 0
|
||||
id = ''
|
||||
n = words.length
|
||||
if n>1
|
||||
nr = words[1]
|
||||
if n>2
|
||||
id = words[2]
|
||||
end
|
||||
end
|
||||
return resp, nr, id
|
||||
end
|
||||
|
||||
def statcmd(line)
|
||||
resp = shortcmd line
|
||||
return statparse(resp)
|
||||
end
|
||||
|
||||
def stat(id)
|
||||
return statcmd "STAT #{id}"
|
||||
end
|
||||
|
||||
def next
|
||||
return statcmd "NEXT"
|
||||
end
|
||||
|
||||
def last
|
||||
return statcmd "LAST"
|
||||
end
|
||||
|
||||
def articlecmd(line)
|
||||
resp, list = longcmd line
|
||||
resp, nr, id = statparse(resp)
|
||||
return resp, nr, id, list
|
||||
end
|
||||
|
||||
def head(id)
|
||||
return articlecmd "HEAD #{id}"
|
||||
end
|
||||
|
||||
def body(id)
|
||||
return articlecmd "BODY #{id}"
|
||||
end
|
||||
|
||||
def article(id)
|
||||
return articlecmd "ARTICLE #{id}"
|
||||
end
|
||||
|
||||
def slave(id)
|
||||
return shortcmd "SLAVE"
|
||||
end
|
||||
|
||||
def xhdr(hdr, str)
|
||||
pat = Regexp.new '^([0-9]+) ?(.*)\n?'
|
||||
resp, lines = longcmd "XHDR #{hdr} #{str}"
|
||||
lines.each_index {|ix|
|
||||
line = lines[ix]
|
||||
m = pat.match line
|
||||
lines[ix] = m[1..2] if m
|
||||
}
|
||||
return resp, lines
|
||||
end
|
||||
|
||||
def xover(start, ed)
|
||||
begin
|
||||
resp, lines = longcmd "XOVER #{start}-#{ed}"
|
||||
xover_lines = []
|
||||
lines.each {|line|
|
||||
elements = line.split "\t"
|
||||
elements[5].split! " "
|
||||
0.upto(7) {|ix| xover_lines << element[ix]}
|
||||
}
|
||||
return resp, xover_lines
|
||||
rescue RuntimeError
|
||||
raise NNTPDataError line, caller
|
||||
end
|
||||
end
|
||||
|
||||
def xgtitle(group)
|
||||
line_pat = Regexp.new "^([^\t]+)[\t]+(.*)$"
|
||||
resp, raw_lines = longcmd "XGTITLE #{group}"
|
||||
lines = []
|
||||
raw_lines.each {|line|
|
||||
match = line_pat.match line.strip
|
||||
lines << match[1..2] if match
|
||||
}
|
||||
return resp, lines
|
||||
end
|
||||
|
||||
def date
|
||||
resp = shortcmd "DATE"
|
||||
raise NNTPReplyError unless resp[0...3] == '111'
|
||||
resp.split! " "
|
||||
raise NNTPDataError unless resp.length == 2
|
||||
date = resp[1][2...8]
|
||||
time = resp[1][-6..-1]
|
||||
raise NNTPDataError resp, caller unless date.length == 6 and time.length == 6
|
||||
return resp, date, time
|
||||
end
|
||||
|
||||
def post(f)
|
||||
resp = shortcmd "POST"
|
||||
raise NNTPReplyError unless resp =~ /^3/ #[0] == 3
|
||||
lines = f.readlines
|
||||
lines.each {|line|
|
||||
line.chop!
|
||||
line = '.' + line if line[0] == '.'
|
||||
putline line
|
||||
}
|
||||
putline '.'
|
||||
return getresp
|
||||
end
|
||||
|
||||
def quit
|
||||
resp = shortcmd "QUIT"
|
||||
@socket.close_read
|
||||
@socket.close_write
|
||||
return resp
|
||||
end
|
||||
|
||||
private :statparse, :getline, :putline, :articlecmd, :statcmd
|
||||
protected :getresp, :getlongresp
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if __FILE__ == $0
|
||||
s = Net::NNTP.new('news')
|
||||
resp, count, first, last, name = s.group('comp.lang.ruby')
|
||||
puts resp
|
||||
puts "group #{name} has #{count} articles, range #{first} to #{last}"
|
||||
resp, subs = s.xhdr('subject', "#{first}-#{last}")
|
||||
puts resp
|
||||
subs.each do |sub| puts sub end
|
||||
resp = s.quit
|
||||
puts resp
|
||||
end
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue