sanguinews 0.60 → 0.61
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/sanguinews/config.rb +152 -0
- data/lib/sanguinews/file_to_upload.rb +3 -6
- data/lib/sanguinews/nntp_msg.rb +5 -9
- data/lib/sanguinews/version.rb +1 -1
- data/lib/sanguinews.rb +58 -185
- data/sample.conf +37 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40aa32aca9a225e90465338f0e81ab91e3a6d252
|
4
|
+
data.tar.gz: d54b92ab842f357493d401bbe2f125896e019c8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6502eba0c482db080ab3a0a3eedd47ae3523a5772e908d68fe6d21041844cae67fcd02cb631cb6501b6b6ee18448c1837ec5a45c01cfca4eb4e04f02828829f
|
7
|
+
data.tar.gz: d501c3176bf7f6e2ca3197e1234c74652e770574e6d668f4881daea9b5e0009b44dc832eca6ca9a91ece5f0af069bf01edb807d8a6f78578908b8f9f069df722
|
@@ -0,0 +1,152 @@
|
|
1
|
+
#######################################################################
|
2
|
+
# Config - class designed specifically for sanguinews
|
3
|
+
# Copyright (c) 2013-2014, Tadeus Dobrovolskij
|
4
|
+
# This library is free software; you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation; either version 2 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License along
|
15
|
+
# with this library; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
#########################################################################
|
18
|
+
module Sanguinews
|
19
|
+
class Config
|
20
|
+
attr_reader :config, :data
|
21
|
+
|
22
|
+
%w(username password server port from connections
|
23
|
+
article_size reconnect_delay groups prefix ssl
|
24
|
+
xna nzb header_check verbose debug filemode files directory).each do |meth|
|
25
|
+
define_method(meth) { @data[meth.to_sym] }
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_config(config)
|
29
|
+
config = ParseConfig.new(config)
|
30
|
+
config.get_params().each do |key|
|
31
|
+
value = config[key]
|
32
|
+
value = true if value == 'yes'
|
33
|
+
value = false if value == 'no'
|
34
|
+
value = value.to_i if %w(connections article_size reconnect_delay).include? key
|
35
|
+
@data[key.to_sym] ||= value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def mode
|
40
|
+
self.ssl ? :tls : :original
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_options!(args)
|
44
|
+
# version and legal info presented to user
|
45
|
+
banner = []
|
46
|
+
banner << ""
|
47
|
+
banner << "sanguinews v#{Sanguinews::VERSION}. Copyright (c) 2013-2014 Tadeus Dobrovolskij."
|
48
|
+
banner << "Comes with ABSOLUTELY NO WARRANTY. Distributed under GPL v2 license(http://www.gnu.org/licenses/gpl-2.0.txt)."
|
49
|
+
banner << "sanguinews is a simple nntp(usenet) binary poster. It supports multithreading and SSL. More info in README."
|
50
|
+
banner << ""
|
51
|
+
# option parser
|
52
|
+
|
53
|
+
opt_parser = OptionParser.new do |opt|
|
54
|
+
opt.banner = "Usage: sanguinews [OPTIONS] [DIRECTORY] | -f FILE1..[FILEX]"
|
55
|
+
opt.separator ""
|
56
|
+
opt.separator "Options"
|
57
|
+
|
58
|
+
opt.on("-c", "--config CONFIG", "use different config file") do |cfg|
|
59
|
+
@config = cfg
|
60
|
+
end
|
61
|
+
opt.on("-C", "--check", "check headers while uploading; slow but reliable") do
|
62
|
+
@data[:header_check] = true
|
63
|
+
end
|
64
|
+
opt.on("-f", "--file FILE", "upload FILE, treat all additional parameters as files") do |file|
|
65
|
+
@data[:filemode] = true
|
66
|
+
@data[:files] << file
|
67
|
+
end
|
68
|
+
opt.on("-g", "--groups GROUP_LIST", "use these groups(comma separated) for upload") do |group_list|
|
69
|
+
@data[:groups] = group_list
|
70
|
+
end
|
71
|
+
opt.on("-h", "--help", "help") do
|
72
|
+
banner.each do |msg|
|
73
|
+
puts msg
|
74
|
+
end
|
75
|
+
puts opt_parser
|
76
|
+
puts
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
opt.on("-p", "--password PASSWORD", "use PASSWORD as your password(overwrites config file)") do |password|
|
80
|
+
@data[:password] = password
|
81
|
+
end
|
82
|
+
opt.on("-u", "--user USERNAME", "use USERNAME as your username(overwrites config file)") do |username|
|
83
|
+
@data[:username] = username
|
84
|
+
end
|
85
|
+
opt.on("-v", "--verbose", "be verbose?") do
|
86
|
+
@data[:verbose] = true
|
87
|
+
end
|
88
|
+
opt.on("-V", "--version", "print version information and then exit") do
|
89
|
+
puts Sanguinews::VERSION
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
begin
|
95
|
+
opt_parser.parse!(args)
|
96
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
97
|
+
puts opt_parser
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
# in file mode treat every additional parameter as a file
|
102
|
+
if !args.empty? && @data[:filemode]
|
103
|
+
args.each do |file|
|
104
|
+
@data[:files] << file.to_s
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# exit when no file list is provided
|
109
|
+
if @data[:files].empty?
|
110
|
+
if @data[:filemode] || args.empty?
|
111
|
+
puts "You need to specify something to upload!"
|
112
|
+
puts opt_parser
|
113
|
+
exit 1
|
114
|
+
else
|
115
|
+
args[0].end_with?('/') ? @data[:directory] = args[0] : @data[:directory] = args[0] + '/'
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
def config_gen
|
122
|
+
puts "It looks like you are launching sanguinews for the first time."
|
123
|
+
`cp #{File.expand_path(File.dirname(__FILE__)) + '/../../sample.conf'} ~/.sanguinews.conf`
|
124
|
+
puts "Sample config was copied to your home directory."
|
125
|
+
puts "Please edit #{File.expand_path('~/.sanguinews.conf')} in your favourite text editor."
|
126
|
+
puts "Relaunch the application when ready."
|
127
|
+
exit
|
128
|
+
end
|
129
|
+
|
130
|
+
def initialize(args)
|
131
|
+
@data = {}
|
132
|
+
@data[:filemode] = false
|
133
|
+
@data[:files] = []
|
134
|
+
|
135
|
+
parse_options!(args)
|
136
|
+
|
137
|
+
# Parse options in config file
|
138
|
+
if @data[:config] && File.exist?(File.expand_path(@data[:config]))
|
139
|
+
config = @data[:config]
|
140
|
+
else
|
141
|
+
config = File.expand_path("~/.sanguinews.conf")
|
142
|
+
end
|
143
|
+
|
144
|
+
if File.exist?(config)
|
145
|
+
parse_config(config)
|
146
|
+
else
|
147
|
+
config_gen
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
end
|
@@ -65,11 +65,8 @@ module Sanguinews
|
|
65
65
|
until self.eof?
|
66
66
|
f = self.read(@@max_mem)
|
67
67
|
crc32 = Zlib.crc32(f, 0)
|
68
|
-
|
69
|
-
|
70
|
-
else
|
71
|
-
fcrc32 = Zlib.crc32_combine(fcrc32, crc32, f.size)
|
72
|
-
end
|
68
|
+
fcrc32 &&= Zlib.crc32_combine(fcrc32, crc32, f.size)
|
69
|
+
fcrc32 ||= crc32
|
73
70
|
end
|
74
71
|
self.rewind
|
75
72
|
fcrc32.to_s(16)
|
@@ -99,7 +96,7 @@ module Sanguinews
|
|
99
96
|
end
|
100
97
|
|
101
98
|
def max_mem
|
102
|
-
|
99
|
+
@@max_mem ||= begin
|
103
100
|
memory = Vmstat.memory
|
104
101
|
@@max_mem = (memory[:free] * memory[:pagesize] * 0.1).floor
|
105
102
|
end
|
data/lib/sanguinews/nntp_msg.rb
CHANGED
@@ -22,17 +22,14 @@ module Sanguinews
|
|
22
22
|
class NntpMsg
|
23
23
|
attr_accessor :message, :from, :groups, :subject, :poster, :date, :xna
|
24
24
|
|
25
|
-
def initialize(from, groups, subject, message='',
|
25
|
+
def initialize(from, groups, subject, message='', **opts)
|
26
26
|
@from = from
|
27
27
|
@groups = groups
|
28
28
|
@subject = subject
|
29
29
|
@message = message
|
30
|
-
if date
|
31
|
-
|
32
|
-
|
33
|
-
@date = date
|
34
|
-
end
|
35
|
-
@poster = poster if !poster.nil?
|
30
|
+
@date = opts[:date] if opts[:date]
|
31
|
+
@date ||= DateTime.now().strftime('%a, %d %b %Y %T %z')
|
32
|
+
@poster = opts[:poster] if opts[:poster]
|
36
33
|
end
|
37
34
|
|
38
35
|
def create_header
|
@@ -40,7 +37,7 @@ module Sanguinews
|
|
40
37
|
sio.puts "From: #{@from}"
|
41
38
|
sio.puts "Newsgroups: #{@groups}"
|
42
39
|
sio.puts "Subject: #{@subject}"
|
43
|
-
sio.puts "X-Newsposter: #{@poster}"
|
40
|
+
sio.puts "X-Newsposter: #{@poster}" if @poster
|
44
41
|
sio.puts "X-No-Archive: yes" if @xna
|
45
42
|
sio.puts "Date: #{@date}"
|
46
43
|
sio.puts
|
@@ -72,7 +69,6 @@ module Sanguinews
|
|
72
69
|
def return_self
|
73
70
|
header = self.create_header
|
74
71
|
header << @message
|
75
|
-
return header
|
76
72
|
end
|
77
73
|
|
78
74
|
def size
|
data/lib/sanguinews/version.rb
CHANGED
data/lib/sanguinews.rb
CHANGED
@@ -31,19 +31,20 @@ require_relative 'sanguinews/nntp'
|
|
31
31
|
require_relative 'sanguinews/nntp_msg'
|
32
32
|
require_relative 'sanguinews/file_to_upload'
|
33
33
|
require_relative 'sanguinews/yencoded'
|
34
|
+
require_relative 'sanguinews/config'
|
34
35
|
require_relative 'sanguinews/version'
|
35
36
|
|
36
37
|
module Sanguinews
|
37
38
|
module_function
|
38
39
|
# Method returns yenc encoded string and crc32 value
|
39
40
|
def yencode(file, length, queue)
|
40
|
-
|
41
|
+
chunk = 1
|
41
42
|
until file.eof?
|
42
43
|
bindata = file.read(length)
|
43
44
|
# We can't take all memory, so we wait
|
44
45
|
queue.synchronize do
|
45
46
|
@cond.wait_while do
|
46
|
-
queue.length > @
|
47
|
+
queue.length > @config.connections * 3
|
47
48
|
end
|
48
49
|
end
|
49
50
|
data = {}
|
@@ -52,12 +53,12 @@ module Sanguinews
|
|
52
53
|
data[:yenc] = Yencoded::Data.yenc(bindata, len)
|
53
54
|
data[:crc32] = Zlib.crc32(bindata, 0).to_s(16)
|
54
55
|
data[:length] = len
|
55
|
-
data[:chunk] =
|
56
|
+
data[:chunk] = chunk
|
56
57
|
data[:file] = file
|
57
58
|
final_data[0] = form_message(data)
|
58
59
|
final_data[1] = file
|
59
60
|
queue.push(final_data)
|
60
|
-
|
61
|
+
chunk += 1
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
@@ -72,168 +73,68 @@ module Sanguinews
|
|
72
73
|
chunks = file.chunks
|
73
74
|
basename = file.name
|
74
75
|
# usenet works with ASCII
|
75
|
-
subject="#{@prefix}#{file.dir_prefix}\"#{basename}\" yEnc (#{chunk}/#{chunks})"
|
76
|
-
msg = NntpMsg.new(@from, @groups, subject)
|
76
|
+
subject="#{@config.prefix}#{file.dir_prefix}\"#{basename}\" yEnc (#{chunk}/#{chunks})"
|
77
|
+
msg = NntpMsg.new(@config.from, @config.groups, subject)
|
77
78
|
msg.poster = "sanguinews v#{Sanguinews::VERSION} (ruby #{RUBY_VERSION}) - https://github.com/tdobrovolskij/sanguinews"
|
78
|
-
msg.xna = @xna
|
79
|
+
msg.xna = @config.xna
|
79
80
|
msg.message = message.force_encoding('ASCII-8BIT')
|
80
81
|
msg.yenc_body(chunk, chunks, crc32, pcrc32, length, fsize, basename)
|
81
82
|
msg = msg.return_self
|
82
83
|
{ message: msg, filename: basename, chunk: chunk, length: length }
|
83
84
|
end
|
84
85
|
|
85
|
-
def connect(
|
86
|
+
def connect(conn_nr)
|
86
87
|
begin
|
87
|
-
nntp = Net::NNTP.start(
|
88
|
+
nntp = Net::NNTP.start(
|
89
|
+
@config.server, @config.port, @config.username, @config.password, @config.mode)
|
88
90
|
rescue
|
89
|
-
@s.log([$!, $@], stderr: true) if @debug
|
90
|
-
if @verbose
|
91
|
+
@s.log([$!, $@], stderr: true) if @config.debug
|
92
|
+
if @config.verbose
|
91
93
|
parse_error($!.to_s)
|
92
|
-
@s.log("Connection nr. #{
|
94
|
+
@s.log("Connection nr. #{conn_nr} has failed. Reconnecting...\n", stderr: true)
|
93
95
|
end
|
94
|
-
sleep @
|
96
|
+
sleep @config.reconnect_delay
|
95
97
|
retry
|
96
98
|
end
|
97
99
|
return nntp
|
98
100
|
end
|
99
101
|
|
100
|
-
def
|
101
|
-
config = ParseConfig.new(config)
|
102
|
-
config.get_params()
|
103
|
-
@username = config['username']
|
104
|
-
@password = config['password']
|
105
|
-
@from = config['from']
|
106
|
-
@server = config['server']
|
107
|
-
@port = config['port']
|
108
|
-
@threads = config['connections'].to_i
|
109
|
-
@length = config['article_size'].to_i
|
110
|
-
@delay = config['reconnect_delay'].to_i
|
111
|
-
@groups = config['groups']
|
112
|
-
@prefix = config['prefix']
|
113
|
-
ssl = config['ssl']
|
114
|
-
if ssl == 'yes'
|
115
|
-
@mode = :tls
|
116
|
-
else
|
117
|
-
@mode = :original
|
118
|
-
end
|
119
|
-
config['xna'] == 'yes' ? @xna = true : @xna = false
|
120
|
-
config['nzb'] == 'yes' ? @nzb = true : @nzb = false
|
121
|
-
config['header_check'] == 'yes' ? @header_check = true : @header_check = false
|
122
|
-
config['debug'] == 'yes' ? @debug = true : @debug = false
|
123
|
-
end
|
124
|
-
|
125
|
-
def get_msgid(response)
|
102
|
+
def get_msgid(responses)
|
126
103
|
msgid = ''
|
127
|
-
|
128
|
-
msgid =
|
104
|
+
responses.each do |response|
|
105
|
+
msgid = response.sub(/>.*/, '').tr("<", '') if response.end_with?('Article posted')
|
129
106
|
end
|
130
107
|
return msgid
|
131
108
|
end
|
132
109
|
|
133
|
-
def parse_options(args)
|
134
|
-
# version and legal info presented to user
|
135
|
-
banner = []
|
136
|
-
banner << ""
|
137
|
-
banner << "sanguinews v#{Sanguinews::VERSION}. Copyright (c) 2013-2014 Tadeus Dobrovolskij."
|
138
|
-
banner << "Comes with ABSOLUTELY NO WARRANTY. Distributed under GPL v2 license(http://www.gnu.org/licenses/gpl-2.0.txt)."
|
139
|
-
banner << "sanguinews is a simple nntp(usenet) binary poster. It supports multithreading and SSL. More info in README."
|
140
|
-
banner << ""
|
141
|
-
# option parser
|
142
|
-
options = {}
|
143
|
-
options[:filemode] = false
|
144
|
-
options[:files] = []
|
145
|
-
|
146
|
-
opt_parser = OptionParser.new do |opt|
|
147
|
-
opt.banner = "Usage: #{$0} [OPTIONS] [DIRECTORY] | -f FILE1..[FILEX]"
|
148
|
-
opt.separator ""
|
149
|
-
opt.separator "Options"
|
150
|
-
|
151
|
-
opt.on("-c", "--config CONFIG", "use different config file") do |cfg|
|
152
|
-
options[:config] = cfg
|
153
|
-
end
|
154
|
-
opt.on("-C", "--check", "check headers while uploading; slow but reliable") do
|
155
|
-
options[:header_check] = true
|
156
|
-
end
|
157
|
-
opt.on("-f", "--file FILE", "upload FILE, treat all additional parameters as files") do |file|
|
158
|
-
options[:filemode] = true
|
159
|
-
options[:files] << file
|
160
|
-
end
|
161
|
-
opt.on("-g", "--groups GROUP_LIST", "use these groups(comma separated) for upload") do |group_list|
|
162
|
-
options[:groups] = group_list
|
163
|
-
end
|
164
|
-
opt.on("-h", "--help", "help") do
|
165
|
-
banner.each do |msg|
|
166
|
-
puts msg
|
167
|
-
end
|
168
|
-
puts opt_parser
|
169
|
-
puts
|
170
|
-
exit
|
171
|
-
end
|
172
|
-
opt.on("-p", "--password PASSWORD", "use PASSWORD as your password(overwrites config file)") do |password|
|
173
|
-
options[:password] = password
|
174
|
-
end
|
175
|
-
opt.on("-u", "--user USERNAME", "use USERNAME as your username(overwrites config file)") do |username|
|
176
|
-
options[:username] = username
|
177
|
-
end
|
178
|
-
opt.on("-v", "--verbose", "be verbose?") do
|
179
|
-
options[:verbose] = true
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
begin
|
184
|
-
opt_parser.parse!(args)
|
185
|
-
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
|
186
|
-
puts opt_parser
|
187
|
-
exit 1
|
188
|
-
end
|
189
|
-
|
190
|
-
options[:directory] = args[0] unless options[:filemode]
|
191
|
-
|
192
|
-
# in file mode treat every additional parameter as a file
|
193
|
-
if !args.empty? && options[:filemode]
|
194
|
-
args.each do |file|
|
195
|
-
options[:files] << file.to_s
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# exit when no file list is provided
|
200
|
-
if options[:directory].nil? && options[:files].empty?
|
201
|
-
puts "You need to specify something to upload!"
|
202
|
-
puts opt_parser
|
203
|
-
exit 1
|
204
|
-
end
|
205
|
-
|
206
|
-
return options
|
207
|
-
end
|
208
|
-
|
209
110
|
def parse_error(msg, **info)
|
210
|
-
if info[:file]
|
211
|
-
fileinfo = ''
|
212
|
-
else
|
111
|
+
if info[:file] && info[:chunk]
|
213
112
|
fileinfo = '(' + info[:file] + ' / Chunk: ' + info[:chunk].to_s + ')'
|
113
|
+
else
|
114
|
+
fileinfo = ''
|
214
115
|
end
|
215
116
|
|
216
117
|
case
|
217
118
|
when /\A411/ === msg
|
218
|
-
@s.log("Invalid newsgroup specified
|
119
|
+
@s.log("Invalid newsgroup specified.\n", stderr: true)
|
219
120
|
when /\A430/ === msg
|
220
|
-
@s.log("No such article. Maybe server is lagging...#{fileinfo}", stderr: true)
|
121
|
+
@s.log("No such article. Maybe server is lagging...#{fileinfo}\n", stderr: true)
|
221
122
|
when /\A(4\d{2}\s)?437/ === msg
|
222
|
-
@s.log("Article rejected by server. Maybe it's too big.#{fileinfo}", stderr: true)
|
123
|
+
@s.log("Article rejected by server. Maybe it's too big.#{fileinfo}\n", stderr: true)
|
223
124
|
when /\A440/ === msg
|
224
|
-
@s.log("Posting not allowed
|
125
|
+
@s.log("Posting not allowed.\n", stderr: true)
|
225
126
|
when /\A441/ === msg
|
226
|
-
@s.log("Posting failed for some reason.#{fileinfo}", stderr: true)
|
127
|
+
@s.log("Posting failed for some reason.#{fileinfo}\n", stderr: true)
|
227
128
|
when /\A450/ === msg
|
228
|
-
@s.log("Not authorized
|
129
|
+
@s.log("Not authorized.\n", stderr: true)
|
229
130
|
when /\A452/ === msg
|
230
|
-
@s.log("Wrong username and/or password
|
131
|
+
@s.log("Wrong username and/or password.\n", stderr: true)
|
231
132
|
when /\A500/ === msg
|
232
|
-
@s.log("Command not recognized
|
133
|
+
@s.log("Command not recognized.\n", stderr: true)
|
233
134
|
when /\A501/ === msg
|
234
|
-
@s.log("Command syntax error
|
135
|
+
@s.log("Command syntax error.\n", stderr: true)
|
235
136
|
when /\A502/ === msg
|
236
|
-
@s.log("Access denied
|
137
|
+
@s.log("Access denied.\n", stderr: true)
|
237
138
|
end
|
238
139
|
end
|
239
140
|
|
@@ -261,80 +162,52 @@ module Sanguinews
|
|
261
162
|
end
|
262
163
|
|
263
164
|
@s.start
|
264
|
-
|
165
|
+
check_delay = 1
|
265
166
|
begin
|
266
167
|
response = nntp.post msg
|
267
168
|
msgid = get_msgid(response)
|
268
|
-
if @header_check
|
269
|
-
sleep
|
169
|
+
if @config.header_check
|
170
|
+
sleep check_delay
|
270
171
|
nntp.stat("<#{msgid}>")
|
271
172
|
end
|
272
173
|
rescue
|
273
|
-
@s.log([$!, $@], stderr: true) if @debug
|
274
|
-
if @verbose
|
174
|
+
@s.log([$!, $@], stderr: true) if @config.debug
|
175
|
+
if @config.verbose
|
275
176
|
parse_error($!.to_s, file: basename, chunk: chunk)
|
276
177
|
@s.log("Upload of chunk #{chunk} from file #{basename} unsuccessful. Retrying...\n", stderr: true)
|
277
178
|
end
|
278
|
-
sleep @
|
279
|
-
|
179
|
+
sleep @config.reconnect_delay
|
180
|
+
check_delay += 4
|
280
181
|
retry
|
281
182
|
end
|
282
183
|
|
283
|
-
if @verbose
|
184
|
+
if @config.verbose
|
284
185
|
@s.log("Uploaded chunk Nr:#{chunk}\n", stderr: true)
|
285
186
|
end
|
286
187
|
|
287
188
|
@s.done(length)
|
288
189
|
@s.uploaded += full_size
|
289
|
-
if @nzb
|
190
|
+
if @config.nzb
|
290
191
|
file.write_segment_info(length, chunk, msgid)
|
291
192
|
end
|
292
193
|
nntp_pool.push(nntp)
|
293
194
|
end
|
294
195
|
|
295
196
|
def run!
|
296
|
-
|
297
|
-
|
298
|
-
config = File.expand_path(config)
|
299
|
-
# variable to store if config was parsed
|
300
|
-
saw_config = false
|
301
|
-
if File.exist?(config)
|
302
|
-
saw_config = true
|
303
|
-
parse_config(config)
|
304
|
-
end
|
305
|
-
|
306
|
-
options = parse_options(ARGV)
|
307
|
-
|
308
|
-
optconfig = options[:config]
|
309
|
-
optconfig = '' if optconfig.nil?
|
310
|
-
if !File.exist?(optconfig) && !saw_config
|
311
|
-
puts "No config information specified. Aborting..."
|
312
|
-
exit
|
313
|
-
end
|
314
|
-
parse_config(optconfig) if File.exist?(optconfig)
|
315
|
-
|
316
|
-
options[:verbose] ? @verbose = true : @verbose = false
|
317
|
-
@header_check = true unless options[:header_check].nil?
|
318
|
-
filemode = options[:filemode]
|
319
|
-
|
320
|
-
@username = options[:username] unless options[:username].nil?
|
321
|
-
@password = options[:password] unless options[:password].nil?
|
322
|
-
@groups = options[:groups] unless options[:groups].nil?
|
323
|
-
directory = options[:directory] unless filemode
|
324
|
-
files = options[:files]
|
197
|
+
@config = Config.new(ARGV)
|
198
|
+
files = @config.files
|
325
199
|
|
326
200
|
# skip hidden files
|
327
|
-
if
|
328
|
-
directory
|
329
|
-
Dir.foreach(directory) do |item|
|
201
|
+
if !@config.filemode
|
202
|
+
Dir.foreach(@config.directory) do |item|
|
330
203
|
next if item.start_with?('.')
|
331
|
-
files << directory+item
|
204
|
+
files << @config.directory+item
|
332
205
|
end
|
333
206
|
end
|
334
207
|
|
335
208
|
# "max" is needed only in dirmode
|
336
209
|
max = files.length
|
337
|
-
|
210
|
+
current_chunk = 1
|
338
211
|
|
339
212
|
unprocessed = 0
|
340
213
|
info_lock=Mutex.new
|
@@ -347,13 +220,13 @@ module Sanguinews
|
|
347
220
|
|
348
221
|
pool = Queue.new
|
349
222
|
Thread.new {
|
350
|
-
@
|
351
|
-
nntp = connect(
|
223
|
+
@config.connections.times do |conn_nr|
|
224
|
+
nntp = connect(conn_nr)
|
352
225
|
pool.push(nntp)
|
353
226
|
end
|
354
227
|
}
|
355
228
|
|
356
|
-
|
229
|
+
thread_pool = Pool.new(@config.connections)
|
357
230
|
informed = {}
|
358
231
|
|
359
232
|
files.each do |file|
|
@@ -361,9 +234,9 @@ module Sanguinews
|
|
361
234
|
|
362
235
|
informed[file.to_sym] = false
|
363
236
|
file = FileToUpload.new(
|
364
|
-
name: file, chunk_length: @
|
365
|
-
|
366
|
-
|
237
|
+
name: file, chunk_length: @config.article_size, prefix: @config.prefix,
|
238
|
+
current: current_chunk, last: max, filemode: @config.filemode,
|
239
|
+
from: @config.from, groups: @config.groups, nzb: @config.nzb
|
367
240
|
)
|
368
241
|
@s.to_upload += file.size
|
369
242
|
|
@@ -372,28 +245,28 @@ module Sanguinews
|
|
372
245
|
end
|
373
246
|
|
374
247
|
files_to_process << file
|
375
|
-
|
248
|
+
current_chunk += 1
|
376
249
|
end
|
377
250
|
|
378
251
|
# let's give a little bit higher priority for file processing thread
|
379
|
-
@
|
252
|
+
@file_proc_thread = Thread.new {
|
380
253
|
files_to_process.each do |file|
|
381
254
|
@s.log("Calculating CRC32 value for #{file.name}\n", stderr: true) if @verbose
|
382
255
|
file.file_crc32
|
383
256
|
@s.log("Encoding #{file.name}\n")
|
384
|
-
yencode(file, @
|
257
|
+
yencode(file, @config.article_size, messages)
|
385
258
|
end
|
386
259
|
}
|
387
|
-
@
|
260
|
+
@file_proc_thread.priority += 2
|
388
261
|
|
389
262
|
until unprocessed == 0
|
390
|
-
|
263
|
+
thread_pool.schedule do
|
391
264
|
process_and_upload(messages, pool, info_lock, informed)
|
392
265
|
end
|
393
266
|
unprocessed -= 1
|
394
267
|
end
|
395
268
|
|
396
|
-
|
269
|
+
thread_pool.shutdown
|
397
270
|
|
398
271
|
until pool.empty?
|
399
272
|
nntp = pool.pop
|
data/sample.conf
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Sample config file
|
2
|
+
# Groups to post to
|
3
|
+
# Separate multiple groups with comma
|
4
|
+
groups = alt.binaries.test
|
5
|
+
# Your identity
|
6
|
+
from = witty_nickname <whatever@example.com>
|
7
|
+
|
8
|
+
# Username to use for authentication
|
9
|
+
username = your_username
|
10
|
+
# Password
|
11
|
+
password = your_password
|
12
|
+
# server to use
|
13
|
+
server = your_server
|
14
|
+
# Use SSL connection?
|
15
|
+
ssl = yes
|
16
|
+
# port
|
17
|
+
port = 563
|
18
|
+
# number of connections
|
19
|
+
connections = 10
|
20
|
+
# article size in bytes
|
21
|
+
article_size = 768000
|
22
|
+
# Wait this many seconds before trying to reconnect after unsuccessful upload
|
23
|
+
reconnect_delay = 5
|
24
|
+
# Subject prefix to use
|
25
|
+
prefix = "[sanguinews] - "
|
26
|
+
# Enable nzb creation
|
27
|
+
nzb = yes
|
28
|
+
# Use header checking? Upload will be slow but reliable.
|
29
|
+
header_check = no
|
30
|
+
|
31
|
+
# Debug information
|
32
|
+
debug = no
|
33
|
+
#######################
|
34
|
+
# headers
|
35
|
+
#######################
|
36
|
+
# X-No-Archive
|
37
|
+
xna = no
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sanguinews
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.61'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tadeus Dobrovolskij
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: speedometer
|
@@ -121,11 +121,13 @@ files:
|
|
121
121
|
- ext/yencoded/yencoded.c
|
122
122
|
- ext/yencoded/yencoded.h
|
123
123
|
- lib/sanguinews.rb
|
124
|
+
- lib/sanguinews/config.rb
|
124
125
|
- lib/sanguinews/file_to_upload.rb
|
125
126
|
- lib/sanguinews/nntp.rb
|
126
127
|
- lib/sanguinews/nntp_msg.rb
|
127
128
|
- lib/sanguinews/thread-pool.rb
|
128
129
|
- lib/sanguinews/version.rb
|
130
|
+
- sample.conf
|
129
131
|
homepage: http://www.tad-do.net
|
130
132
|
licenses:
|
131
133
|
- GPLv2
|