summary refs log tree commit diff
path: root/contrib/msuck
blob: 3fe58b7fa8af58454b06ff65288a2bcda214595a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/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|

  FileUtils.mkdir_p(File.join(dir, group, "cur"))
  FileUtils.mkdir_p(File.join(dir, group, "new"))
  FileUtils.mkdir_p(File.join(dir, group, "tmp"))

  nntp.write("GROUP #{group}\r\n")

  msg = nntp.gets
  unless msg =~ /^211 /
    STDERR.puts msg
    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

  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
}