#! /usr/local/bin/ruby ################################################################## #### Net::NNTP module, feeding oriented #### #### Written by F. Senault (fred@lacave.net), free to use, #### redistribute, modify. #### No warranty at all, implied or whatever. Use at your own #### risk. ################################################################## require 'socket' module Net class NNTP public attr_accessor :reader attr_reader :address, :ip, :port, :lastrep @@debug = false def NNTP.default_port 119 end def NNTP.debug=(debug) NNTP::Response.debug = debug @@debug = true end def NNTP.debug() @@debug end def initialize(address, port = nil) @address = address @port = (port || NNTP.default_port) @reader = false @started = false @socket = nil @ip = nil @banner = nil @lastrep = nil end def started? @started end def NNTP.start(address, port = nil, reader = nil, user = nil, pass = nil, &block) n = self.new(address, port) n.start(user, pass, reader, &block) end def start(reader = nil, user = nil, pass = nil, &block) if(!block.nil?) then begin @lastrep = do_start(reader, user, pass) block.call(self) ensure do_finish end else @lastrep = do_start(reader, user, pass) return self end end def list(command = '') raise "Command before start." if(!@started) @lastrep = do_send("list #{command}", true) end def check(mid) raise "Command before start." if(!@started) mid = "<#{mid}>" unless(mid[0,1] == '<') @lastrep = do_send("check #{mid}") end def takethis(art) raise "Command before start." if(!@started) do_send("takethis #{art['Message-ID']}", false, true) @lastrep = do_send_raw(art.to_wireformat) end def article(mid) raise "Command before start." if(!@started) @lastrep = do_send("article #{mid}", true) end def stop() @lastrep = do_finish() end private def do_start(reader, user, pass) @socket = TCPSocket.new(@address, @port) r = NNTP::Response.new(@socket) if(!r.ok) then do_finish return r end if(r.ok && reader == true) then r = do_send("mode reader") @reader = r.ok end if(r.ok && !user.nil?) then r = do_send("authinfo user #{user}") end if(r.ok && !pass.nil?) then r = do_send("authinfo pass #{pass}") end @started = true r end def do_finish() @started = false do_send("quit") end def do_send(t, long = false, nowait = false) puts ">>> " + t if(@@debug) @socket.print t + "\r\n" NNTP::Response.new(@socket, long) unless(nowait) end def do_send_raw(t, long = false) nl = 0 t.each do |l| l.chomp! nl+=1 @socket.print(l + "\r\n") end puts ">>> [#{nl} lines]" if(@@debug) @socket.print(".\r\n") NNTP::Response.new(@socket, long) end end class NNTP::Response attr_reader :code, :desc, :resp @@debug = false def self.debug=(debug) @@debug = true end def self.debug() @@debug end def initialize(datain, long = false) @resp = '' r = nil if(datain.kind_of? IO) then r = [ datain.gets.chomp ] if(long) then while((o = datain.gets.chomp) != '.') r.push(o) end end else if(long) then r = datain.split(/(?:\r|\r\n|\n)/) else r = [ datain.to_s.chomp ] end end l = r.shift re = Regexp.new(/^(\d\d\d)(?: (.*))?$/).match(l) if(re.nil?) then (@code, @desc) = [ '499', l ] else (@code, @desc) = re[1,2] end if(long) then r.inject do |@resp, l| #@resp = (@resp == '' ? '' : @resp + "\n") + l @resp += l + "\n" unless(l == '.') end end puts self.short("<<< ") if(@@debug) self end def ok case @code[0,1] when '1', '2' then true else false end end def to_s(prefix = '') r = "#{prefix}#{@code} #{@desc}\n" if(@resp != '') then @resp.each do |l| r += "#{prefix}#{l}\n" end r += "#{prefix}.\n" end r end def short(prefix = '') r = "#{prefix}#{code} #{desc}\n" if(@resp != '') then r += "#{prefix}[#{@resp.scan(/\n/).length} lines]\n" r += "#{prefix}.\n" end r end end class NNTP::Article attr_accessor :body def initialize() @hdr = {} @hdro = {} @body = '' @ord = 0 end def []=(key, value) if(!key.nil? && key.to_s != '' && !value.nil? && value.to_s != '') then @hdr[key] = value @hdro[key] = @ord @ord += 1 end end def [](key) @hdr[key] end def to_wireformat r = '' @hdr.keys.sort { |a, b| @hdro[a] <=> @hdro[b] }.each do |k| r += "#{k}: #{@hdr[k]}\n" end r += "\n" @body.each do |l| l.chomp! l = '.' + l if(l[0,1] == '.') r += "#{l}\n" end r end def to_s r = '' @hdr.keys.sort { |a, b| @hdro[a] <=> @hdro[b] }.each do |k| r += "#{k}: #{@hdr[k]}\n" end r += "\n" @body.each do |l| r += "#{l}" end r end def to_h r = '' @hdr.keys.sort { |a, b| @hdro[a] <=> @hdro[b] }.each do |k| r += "#{k}: #{@hdr[k]}\n" end r += "\n" end def to_h_light r = '' [ 'From', 'Newsgroups', 'Subject', 'Message-ID', 'Date', 'Path' ].each do |k| r += "#{k}: #{@hdr[k]}\n" unless(@hdr[k].nil?) end r += "\n" end def self.make_date(d) "%s %+05d (%s)" % [ d.strftime("%a, %d %b %Y %H:%M:%S"), d.gmt_offset/36, d.zone ] end end end