#!/usr/bin/ruby # msuck - suck NNTP groups into Maildirs # # msuck [-s NNTPSERVER[:PORT]] [-d BASEDIR] [-l LIMIT] GROUPS... to fetch GROUPS # msuck [-s NNTPSERVER[:PORT]] -L to list all groups require 'socket' require 'fileutils' require 'optparse' $delivery = 0 HOST = Socket.gethostname def genname(id) $delivery += 1 t = Time.now "%d.M%06dP%05dQ%d.%s,N=%d" % [t.tv_sec, t.tv_usec, $$, $delivery, HOST, id] end params = ARGV.getopts("d:fl:s:L") dir = params["d"] || '.' LIMIT = if params["l"] Integer(params["l"]) else 10 end port = 119 if params["s"] =~ /(.*):(.*)/ params["s"] = $1 port = Integer($2) end SERVER = params["s"] || ENV["NNTPSERVER"] || "news" nntp = TCPSocket.new SERVER, port msg = nntp.gets abort msg unless msg =~ /^20[01] / if params["L"] # list all groups nntp.write("LIST NEWSGROUPS\r\n") msg = nntp.gets if msg !~ /^215 / abort msg end loop { msg = nntp.gets break if msg == ".\r\n" puts msg } exit end STDOUT.sync = true ARGV.each { |group| nntp.write("GROUP #{group}\r\n") msg = nntp.gets unless msg =~ /^211 / STDERR.puts msg $exit = 69 next end _, number, low, high, _ = msg.split(" ", 5) number = number.to_i low = low.to_i high = high.to_i low = high - LIMIT + 1 if number > LIMIT - 1 low = 1 if low <= 0 FileUtils.mkdir_p(File.join(dir, group, "cur")) FileUtils.mkdir_p(File.join(dir, group, "new")) FileUtils.mkdir_p(File.join(dir, group, "tmp")) have = Dir.entries(File.join(dir, group, "cur")). map { |f| $1.to_i if f =~ /N=(\d+)/ }.compact ourhigh = have.max if ourhigh && low < ourhigh && !params["f"] low = ourhigh + 1 end next if low > high printf "%s %d-%d ", group, low, high nntp.write("STAT #{low}\r\n") msg = nntp.gets _, num, mid, _ = msg.split(" ", 4) loop { unless have.include? num.to_i nntp.write("ARTICLE\r\n") msg = nntp.gets if msg =~ /^220 / _, num, mid, _ = msg.split(" ", 4) text = ["X-Msuck: nntp://#{SERVER}/#{group}/#{num}\n"] loop { msg = nntp.gets break if msg == ".\r\n" msg.sub!(/\A\./, "") msg.sub!(/\r\n\z/, "\n") text << msg } text = text.join name = genname(num) File.write(File.join(dir, group, "tmp", name), text) File.rename(File.join(dir, group, "tmp", name), File.join(dir, group, "cur", name + ":2,")) print "." else STDERR.puts msg end else print "=" end nntp.write("NEXT\r\n") msg = nntp.gets if msg !~ /^223 / break end _, num, mid, _ = msg.split(" ", 4) } puts } exit $exit || 0