nntp-client 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -17
- data/.rspec +2 -2
- data/Gemfile +8 -8
- data/LICENSE.txt +21 -21
- data/Rakefile +1 -1
- data/lib/nntp/article.rb +3 -0
- data/lib/nntp/connection.rb +1 -1
- data/lib/nntp/session.rb +42 -8
- data/lib/nntp/status.rb +6 -6
- data/lib/nntp/version.rb +1 -1
- data/spec/session_spec.rb +17 -0
- metadata +3 -6
- data/lib/NNTPClient.rb +0 -120
- data/lib/article.rb +0 -7
- data/lib/nntp/message.rb +0 -3
data/.gitignore
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
|
-
.bundle
|
4
|
-
.config
|
5
|
-
.yardoc
|
6
|
-
Gemfile.lock
|
7
|
-
InstalledFiles
|
8
|
-
_yardoc
|
9
|
-
coverage
|
10
|
-
doc/
|
11
|
-
lib/bundler/man
|
12
|
-
pkg
|
13
|
-
rdoc
|
14
|
-
spec/reports
|
15
|
-
test/tmp
|
16
|
-
test/version_tmp
|
17
|
-
tmp
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
data/.rspec
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
--format specdoc
|
2
|
-
--color
|
1
|
+
--format specdoc
|
2
|
+
--color
|
data/Gemfile
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
# Specify your gem's dependencies in nntp.gemspec
|
4
|
-
gem 'rake'
|
5
|
-
gem "rspec"
|
6
|
-
gem "rspec-core"
|
7
|
-
gem "rspec-expectations"
|
8
|
-
gem "rspec-mocks"
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in nntp.gemspec
|
4
|
+
gem 'rake'
|
5
|
+
gem "rspec"
|
6
|
+
gem "rspec-core"
|
7
|
+
gem "rspec-expectations"
|
8
|
+
gem "rspec-mocks"
|
data/LICENSE.txt
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
Copyright (c) 2012 Michael Westbom
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
1
|
+
Copyright (c) 2012 Michael Westbom
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
22
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/nntp/article.rb
ADDED
data/lib/nntp/connection.rb
CHANGED
@@ -7,7 +7,7 @@ module NNTP
|
|
7
7
|
# Most communication with an NNTP server happens in a back-and-forth
|
8
8
|
# style.
|
9
9
|
#
|
10
|
-
# The client sends a
|
10
|
+
# The client sends a article to the server. The server will respond with a status response, and sometimes with extra data. See {https://tools.ietf.org/html/rfc3977 RFC 3977} for more details.
|
11
11
|
#
|
12
12
|
# This class handles this communication by providing two methods,
|
13
13
|
# one for use when additional data is expected, and one for when it is not.
|
data/lib/nntp/session.rb
CHANGED
@@ -1,17 +1,22 @@
|
|
1
1
|
require "nntp/group"
|
2
|
-
require 'nntp/
|
2
|
+
require 'nntp/article'
|
3
3
|
|
4
4
|
module NNTP
|
5
5
|
# Most of the action happens here. This class describes the
|
6
6
|
# object the user interacts with the most.
|
7
7
|
# It is constructed by NNTP::open, but you can build your own
|
8
8
|
# if you like.
|
9
|
+
|
10
|
+
# @!attribute [r] article
|
11
|
+
# The current article pointed to by the server or nil if unset.
|
12
|
+
# @return [NNTP::Article, nil]
|
9
13
|
class Session
|
10
|
-
attr_reader :connection, :group
|
14
|
+
attr_reader :connection, :group, :article
|
11
15
|
# @option options [NNTP::Connection, NNTP::SSLConnection] :connection
|
12
16
|
# The connection object.
|
13
17
|
def initialize(options)
|
14
18
|
@group = nil
|
19
|
+
@article = nil
|
15
20
|
@connection = options.fetch(:connection) do
|
16
21
|
raise ArgumentError ":connection missing"
|
17
22
|
end
|
@@ -51,18 +56,18 @@ module NNTP
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
|
-
# Fetch list of
|
59
|
+
# Fetch list of article numbers from a given group.
|
55
60
|
# @param group The name of the group to list defaults to {#group #group.name}
|
56
61
|
# @param range (nil) If given, specifies the range of messages to retrieve
|
57
|
-
# @return [Array<NNTP::
|
58
|
-
# (only the
|
62
|
+
# @return [Array<NNTP::Article>] The list of messages
|
63
|
+
# (only the article numbers will be populated).
|
59
64
|
# @see https://tools.ietf.org/html/rfc3977#section-6.1.2
|
60
65
|
def listgroup(*args)
|
61
66
|
messages = []
|
62
67
|
connection.query(:listgroup, *args) do |status, data|
|
63
68
|
if status[:code] == 211
|
64
69
|
data.each do |line|
|
65
|
-
message =
|
70
|
+
message = Article.new
|
66
71
|
message.num = line.to_i
|
67
72
|
messages << message
|
68
73
|
end
|
@@ -71,8 +76,29 @@ module NNTP
|
|
71
76
|
messages
|
72
77
|
end
|
73
78
|
|
79
|
+
# Fetch ID and/or Number of an article.
|
80
|
+
#
|
81
|
+
# If an article number is passed, the current article is changed
|
82
|
+
# to the selected article. If an ID is passed, the current article
|
83
|
+
# is NOT changed.
|
84
|
+
# @param id|num The article-id or article number to be selected.
|
85
|
+
# @return [NNTP::Article, nil] a Article struct or nil upon failure.
|
86
|
+
def stat(identifier = nil)
|
87
|
+
params = [:stat]
|
88
|
+
params += [identifier.to_s] unless identifier.nil?
|
89
|
+
connection.command(*params)
|
90
|
+
if status.code == 223
|
91
|
+
message = article_from_status(status.msg)
|
92
|
+
if identifier.to_i != 0
|
93
|
+
@article = message
|
94
|
+
end
|
95
|
+
message
|
96
|
+
else
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
end
|
74
100
|
# Fetch list of messages from current group.
|
75
|
-
# @return [Array<NNTP::
|
101
|
+
# @return [Array<NNTP::Article>] The list of messages
|
76
102
|
# (The numbers AND the subjects will be populated).
|
77
103
|
def subjects
|
78
104
|
subjects = []
|
@@ -80,7 +106,7 @@ module NNTP
|
|
80
106
|
connection.query(:xhdr, "Subject", range) do |status, data|
|
81
107
|
if status[:code] == 221
|
82
108
|
data.each do |line|
|
83
|
-
message =
|
109
|
+
message = Article.new
|
84
110
|
message.num, message.subject = line.split(' ', 2)
|
85
111
|
subjects << message
|
86
112
|
end
|
@@ -108,6 +134,14 @@ module NNTP
|
|
108
134
|
end
|
109
135
|
end
|
110
136
|
|
137
|
+
def article_from_status(status_msg)
|
138
|
+
message = Article.new
|
139
|
+
num, id = status_msg.split(' ', 2)
|
140
|
+
message.num = num.to_i unless num.to_i.zero?
|
141
|
+
message.id = id
|
142
|
+
message
|
143
|
+
end
|
144
|
+
|
111
145
|
def check_initial_status
|
112
146
|
raise "#{status}" if [400, 502].include? connection.get_status.code
|
113
147
|
end
|
data/lib/nntp/status.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
module NNTP
|
2
|
-
Status = Struct.new(:code, :msg) do
|
3
|
-
def to_s
|
4
|
-
"#{self[:code]} #{self[:msg]}"
|
5
|
-
end
|
6
|
-
end
|
1
|
+
module NNTP
|
2
|
+
Status = Struct.new(:code, :msg) do
|
3
|
+
def to_s
|
4
|
+
"#{self[:code]} #{self[:msg]}"
|
5
|
+
end
|
6
|
+
end
|
7
7
|
end
|
data/lib/nntp/version.rb
CHANGED
data/spec/session_spec.rb
CHANGED
@@ -100,4 +100,21 @@ describe NNTP::Session do
|
|
100
100
|
message_subjects(nntp.subjects).should eq ['Foo bar', 'Baz bang']
|
101
101
|
end
|
102
102
|
end
|
103
|
+
describe "#stat" do
|
104
|
+
let(:article) do
|
105
|
+
msg = NNTP::Article.new
|
106
|
+
msg.num = 1
|
107
|
+
msg.id = "<foo@bar.baz>"
|
108
|
+
msg
|
109
|
+
end
|
110
|
+
|
111
|
+
it "can set the current article using an article number" do
|
112
|
+
sock.should_receive(:print).with("STAT 1\r\n")
|
113
|
+
sock.should_receive(:gets).and_return("223 1 <foo@bar.baz>\r\n")
|
114
|
+
nntp.stub(:group) {NNTP::Group.new("alt.bin.foo", 1, 2, 2)}
|
115
|
+
nntp.stat(1)
|
116
|
+
nntp.status.should eq NNTP::Status.new(223, "1 <foo@bar.baz>")
|
117
|
+
nntp.article.should eq article
|
118
|
+
end
|
119
|
+
end
|
103
120
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nntp-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -40,12 +40,10 @@ files:
|
|
40
40
|
- LICENSE.txt
|
41
41
|
- README.md
|
42
42
|
- Rakefile
|
43
|
-
- lib/NNTPClient.rb
|
44
|
-
- lib/article.rb
|
45
43
|
- lib/nntp.rb
|
44
|
+
- lib/nntp/article.rb
|
46
45
|
- lib/nntp/connection.rb
|
47
46
|
- lib/nntp/group.rb
|
48
|
-
- lib/nntp/message.rb
|
49
47
|
- lib/nntp/session.rb
|
50
48
|
- lib/nntp/ssl_connection.rb
|
51
49
|
- lib/nntp/status.rb
|
@@ -87,4 +85,3 @@ test_files:
|
|
87
85
|
- spec/nntp_spec.rb
|
88
86
|
- spec/session_spec.rb
|
89
87
|
- spec/spec_helper.rb
|
90
|
-
has_rdoc:
|
data/lib/NNTPClient.rb
DELETED
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require_relative 'group'
|
3
|
-
require_relative 'article'
|
4
|
-
require "NNTPClient/version"
|
5
|
-
|
6
|
-
class NNTPClient
|
7
|
-
attr_reader :socket, :status, :current_group
|
8
|
-
|
9
|
-
def initialize(options = {})
|
10
|
-
@socket = open_socket(options)
|
11
|
-
init_attributes
|
12
|
-
end
|
13
|
-
|
14
|
-
def groups
|
15
|
-
@groups ||= list_groups
|
16
|
-
end
|
17
|
-
|
18
|
-
def group(group)
|
19
|
-
send_message "GROUP #{group}"
|
20
|
-
self.status = get_status
|
21
|
-
if status[:code] == 211
|
22
|
-
self.current_group = create_group(status)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def articles
|
27
|
-
@articles ||= fetch_articles
|
28
|
-
end
|
29
|
-
|
30
|
-
def auth(options = {})
|
31
|
-
send_message "AUTHINFO USER #{options[:user]}"
|
32
|
-
self.status = get_status
|
33
|
-
send_message "AUTHINFO PASS #{options[:pass]}"
|
34
|
-
self.status = get_status
|
35
|
-
if status[:code] == 281
|
36
|
-
true
|
37
|
-
else
|
38
|
-
false
|
39
|
-
end
|
40
|
-
end
|
41
|
-
private
|
42
|
-
def fetch_articles
|
43
|
-
send_message "XHDR Subject #{current_group.first}-"
|
44
|
-
self.status = get_status
|
45
|
-
return nil unless status[:code] == 221
|
46
|
-
get_data_block.map do |line|
|
47
|
-
article_id, article_subject = line.split(' ', 2)
|
48
|
-
NNTP::Article.new(article_id, article_subject)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def init_attributes
|
53
|
-
@current_group = nil
|
54
|
-
@status = nil
|
55
|
-
@groups = nil
|
56
|
-
@articles = nil
|
57
|
-
end
|
58
|
-
|
59
|
-
def create_group(status)
|
60
|
-
params = status[:params]
|
61
|
-
# TODO: This is ugly
|
62
|
-
NNTP::Group.new(*params[1..-1])
|
63
|
-
end
|
64
|
-
|
65
|
-
def open_socket(options)
|
66
|
-
options.fetch(:socket) {
|
67
|
-
url = options.fetch(:url) { raise ArgumentError, ':url is required' }
|
68
|
-
port = options.fetch(:port, 119)
|
69
|
-
socket_factory = options.fetch(:socket_factory) { TCPSocket }
|
70
|
-
socket_factory.new(url, port)
|
71
|
-
}
|
72
|
-
end
|
73
|
-
|
74
|
-
def list_groups
|
75
|
-
send_message "LIST"
|
76
|
-
status = get_status
|
77
|
-
return nil unless status[:code] == 215
|
78
|
-
|
79
|
-
get_data_block
|
80
|
-
end
|
81
|
-
|
82
|
-
def groups=(list=[])
|
83
|
-
@groups = list
|
84
|
-
end
|
85
|
-
|
86
|
-
def status=(status)
|
87
|
-
@status = status
|
88
|
-
end
|
89
|
-
|
90
|
-
def current_group=(group)
|
91
|
-
@current_group = group
|
92
|
-
end
|
93
|
-
|
94
|
-
def get_status
|
95
|
-
line = get_line
|
96
|
-
{
|
97
|
-
:code => line[0..2].to_i,
|
98
|
-
:message => line[3..-1].lstrip,
|
99
|
-
:params => line.split
|
100
|
-
}
|
101
|
-
end
|
102
|
-
|
103
|
-
def get_data_block
|
104
|
-
lines = []
|
105
|
-
current_line = get_line
|
106
|
-
until current_line == '.'
|
107
|
-
lines << current_line
|
108
|
-
current_line = get_line
|
109
|
-
end
|
110
|
-
lines
|
111
|
-
end
|
112
|
-
|
113
|
-
def get_line
|
114
|
-
socket.gets().chomp
|
115
|
-
end
|
116
|
-
|
117
|
-
def send_message(msg)
|
118
|
-
socket.print "#{msg}\r\n"
|
119
|
-
end
|
120
|
-
end
|
data/lib/article.rb
DELETED
data/lib/nntp/message.rb
DELETED