Created
December 19, 2012 15:35
-
-
Save eviltrout/4337577 to your computer and use it in GitHub Desktop.
NNTP Connection Class in Ruby
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Example usage: | |
# | |
# require 'rubygems' | |
# require 'date' | |
# require 'socket' | |
# require 'celluloid' | |
# require 'nntp_connection' | |
# | |
# nntp = NNTPConnection.new('news.supernews.com', 119, 'username', 'password') | |
# group = nntp.group('alt.binaries.boneless') | |
# | |
# nntp.xover(group.high - 50, group.high) | |
# ==> articles returned! | |
require 'iconv' | |
class NNTPGroup | |
attr_reader :name, :count, :low, :high | |
def initialize(name, count, low, high) | |
@name = name | |
@count = count | |
@low = low | |
@high = high | |
end | |
end | |
class NNTPOverview | |
attr_accessor :articles, :group, :low, :high | |
def initialize(response) | |
@articles = [] | |
@low = nil | |
@high = nil | |
response.split("\n").each do |row| | |
fields = row.strip.split("\t") | |
if !fields.empty? | |
number, subject, author, created_at, message_id = fields | |
@low = number if @low.nil? or number < low | |
@high = number if @high.nil? or number > high | |
@articles << NNTPArticle.new(number, subject, author, created_at, message_id) | |
end | |
end | |
end | |
end | |
class NNTPArticle | |
attr_accessor :number, :subject, :author, :created_at, :message_id | |
def initialize(number, subject, author, created_at, message_id) | |
@number = number | |
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8') | |
@subject = ic.iconv(subject + ' ')[0..-2] | |
@author = ic.iconv(author + ' ')[0..-2] | |
@created_at = DateTime.parse(created_at) | |
@message_id = message_id | |
end | |
end | |
class NNTPConnection | |
include Celluloid::Logger | |
def initialize(host, port, username, password) | |
@host = host | |
@port = port | |
info "Opening connection to #{host}:#{port}" | |
@socket = TCPSocket.new @host, @port | |
recv_response | |
# Authentifcate | |
command("AUTHINFO USER #{username}", true) | |
command("AUTHINFO PASS #{password}") | |
end | |
def recv_response(allow_continue=false) | |
stat = '' | |
while true | |
line = @socket.readline | |
stat << line << "\n" | |
break unless line[3] == ?- # "210-PIPELINING" | |
end | |
return stat if /\A1/ === stat # 1xx info msg | |
return stat if /\A2/ === stat # 2xx cmd k | |
return stat if allow_continue and /\A[35]/ === stat # 3xx cmd k, snd rst | |
raise "NNTP Error: #{stat}" | |
end | |
def command(cmd, allow_continue=false) | |
@socket.puts(cmd) | |
recv_response(allow_continue) | |
end | |
def group(name) | |
result = command("GROUP #{name}") | |
chunks = result.split | |
NNTPGroup.new(chunks[4], chunks[1].to_i, chunks[2].to_i, chunks[3].to_i) | |
end | |
def multiline_command(cmd, *args) | |
result = "" | |
if stat = command(cmd, *args) | |
while true | |
line = @socket.readline | |
break if line =~ /^\.\s*$/ | |
line = line[1..-1] if line.to_s[0...2] == '..' | |
result << line + $/ | |
end | |
end | |
return result | |
end | |
def xover(low, high) | |
response = multiline_command("XOVER #{low}-#{high}") | |
NNTPOverview.new(response) | |
end | |
def capabilities | |
multiline_command('CAPABILITIES') | |
end | |
def close | |
@socket.close | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment