net-irc 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,44 @@
1
+ class Net::IRC::Message::ModeParser
2
+
3
+ def initialize(require_arg, definition)
4
+ @require_arg = require_arg.map {|i| i.to_sym }
5
+ @definition = definition
6
+ end
7
+
8
+ def parse(arg)
9
+ params = arg.kind_of?(Net::IRC::Message) ? arg.to_a : arg.split(/\s+/)
10
+
11
+ ret = {
12
+ :positive => [],
13
+ :negative => [],
14
+ }
15
+
16
+ current = nil, arg_pos = 0
17
+ params[1].each_byte do |c|
18
+ sym = c.chr.to_sym
19
+ case sym
20
+ when :+
21
+ current = ret[:positive]
22
+ when :-
23
+ current = ret[:negative]
24
+ else
25
+ case
26
+ when @require_arg.include?(sym)
27
+ current << [sym, params[arg_pos + 2]]
28
+ arg_pos += 1
29
+ when @definition.key?(sym)
30
+ current << [sym, nil]
31
+ else
32
+ # fallback, should raise exception
33
+ # but not for convenience
34
+ current << [sym, nil]
35
+ end
36
+ end
37
+ end
38
+
39
+ ret
40
+ end
41
+
42
+ autoload :RFC1459, "net/irc/message/modeparser/rfc1459"
43
+ autoload :Hyperion, "net/irc/message/modeparser/hyperion"
44
+ end
@@ -0,0 +1,88 @@
1
+ module Net::IRC::Message::ModeParser::Hyperion
2
+ Channel = Net::IRC::Message::ModeParser.new(%w|o l b v k q e|, {
3
+ :o => "channel ban",
4
+ :c => "color filter",
5
+ :d => "realname ban",
6
+ :e => "ban exemption",
7
+ :f => "forward on uninvited",
8
+ :g => "allow anybody to invite",
9
+ :i => "invite-only",
10
+ :I => "invite-only exemption",
11
+ :j => "jupe channel",
12
+ :J => "join throttling",
13
+ :k => "channel password",
14
+ :l => "join limit",
15
+ :L => "large ban/exempt /invex lists",
16
+ :m => "moderated",
17
+ :n => "prevent external send",
18
+ :p => "deprecated",
19
+ :P => "permanent channel",
20
+ :q => "quiet user",
21
+ :Q => "block forwarded users",
22
+ :r => "block unidentified",
23
+ :R => "quiet unidentified",
24
+ :s => "secret channel",
25
+ :t => "only ops can change topic",
26
+ :z => "reduced moderation",
27
+ })
28
+ User = Net::IRC::Message::ModeParser.new(%w||, {
29
+ :a => "user auspex",
30
+ :A => "server auspex",
31
+ :b => "see bots",
32
+ :B => "edit other people",
33
+ :c => "see client connections",
34
+ :C => "block ctcp",
35
+ :d => "debug",
36
+ :D => "die command",
37
+ :e => "identified",
38
+ :E => "reject messages from unidentified users",
39
+ :f => "full warning",
40
+ :F => "immune to flood protection",
41
+ :G => "global kill",
42
+ :h => "high priority",
43
+ :H => "rehash",
44
+ :i => "invisible",
45
+ :I => "refuse invite",
46
+ :j => "autodline",
47
+ :k => "server kill notices",
48
+ :K => "local kill",
49
+ :l => "channel creation notices",
50
+ :m => "immune",
51
+ :M => "mass notices",
52
+ :n => "nick change notices",
53
+ :N => "use any nick",
54
+ :o => "operator",
55
+ :p => "god mode",
56
+ :P => "setname and setident",
57
+ :Q => "disable forwarding",
58
+ :r => "notices on name rejections",
59
+ :R => "server routing",
60
+ :s => "server notices",
61
+ :S => "run commands remotely",
62
+ :T => "appear in STATS p",
63
+ :u => "join more channels",
64
+ :U => "unkline",
65
+ :v => "view privileges",
66
+ :V => "view routing",
67
+ :w => "receive wallops",
68
+ :W => "send wallops",
69
+ :x => "see servers joining",
70
+ :X => "experimental features",
71
+ :y => "spy",
72
+ :Y => "misc server connect notices",
73
+ :z => "receive operwall",
74
+ :Z => "send operwall",
75
+ :0 => "see opers",
76
+ :1 => "see connection settings",
77
+ :2 => "see network bans",
78
+ :3 => "see nick bans",
79
+ :4 => "see STATS T",
80
+ :5 => "see STATS ?",
81
+ :6 => "send/receive regardless of +E/noidprivmsg",
82
+ :9 => "use testline",
83
+ :* => "grant umodes",
84
+ })
85
+ end
86
+
87
+
88
+
@@ -0,0 +1,21 @@
1
+ module Net::IRC::Message::ModeParser::RFC1459
2
+ Channel = Net::IRC::Message::ModeParser.new(%w|o l b v k|, {
3
+ :o => "give/take channel operator privileges",
4
+ :p => "private channel flag",
5
+ :s => "select channel flag",
6
+ :i => "invite-only channel flag",
7
+ :t => "topic settable by channel operator only flag",
8
+ :n => "no messages to channel from clients on the outside",
9
+ :m => "moderated channel",
10
+ :l => "set the user limit to channel",
11
+ :b => "set a ban mask to keep users out",
12
+ :v => "give/take the ability to speak on a moderated channel",
13
+ :k => "set a channel key (password)",
14
+ })
15
+ User = Net::IRC::Message::ModeParser.new(%w||, {
16
+ :i => "marks a users as invisible",
17
+ :s => "marks a user for receipt of server notices",
18
+ :w => "user receives wallops",
19
+ :o => "operator flag",
20
+ })
21
+ end
@@ -0,0 +1,68 @@
1
+
2
+ module Net::IRC::PATTERN # :nodoc:
3
+ # letter = %x41-5A / %x61-7A ; A-Z / a-z
4
+ # digit = %x30-39 ; 0-9
5
+ # hexdigit = digit / "A" / "B" / "C" / "D" / "E" / "F"
6
+ # special = %x5B-60 / %x7B-7D
7
+ # ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
8
+ LETTER = 'A-Za-z'
9
+ DIGIT = '\d'
10
+ HEXDIGIT = "#{DIGIT}A-Fa-f"
11
+ SPECIAL = '\x5B-\x60\x7B-\x7D'
12
+
13
+ # shortname = ( letter / digit ) *( letter / digit / "-" )
14
+ # *( letter / digit )
15
+ # ; as specified in RFC 1123 [HNAME]
16
+ # hostname = shortname *( "." shortname )
17
+ SHORTNAME = "[#{LETTER}#{DIGIT}](?:[-#{LETTER}#{DIGIT}]*[#{LETTER}#{DIGIT}])?"
18
+ HOSTNAME = "#{SHORTNAME}(?:\\.#{SHORTNAME})*"
19
+
20
+ # servername = hostname
21
+ SERVERNAME = HOSTNAME
22
+
23
+ # nickname = ( letter / special ) *8( letter / digit / special / "-" )
24
+ #NICKNAME = "[#{LETTER}#{SPECIAL}\\w][-#{LETTER}#{DIGIT}#{SPECIAL}]*"
25
+ NICKNAME = "\\S+" # for multibytes
26
+
27
+ # user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
28
+ # ; any octet except NUL, CR, LF, " " and "@"
29
+ USER = '[\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x3F\x41-\xFF]+'
30
+
31
+ # ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
32
+ IP4ADDR = "[#{DIGIT}]{1,3}(?:\\.[#{DIGIT}]{1,3}){3}"
33
+ # ip6addr = 1*hexdigit 7( ":" 1*hexdigit )
34
+ # ip6addr =/ "0:0:0:0:0:" ( "0" / "FFFF" ) ":" ip4addr
35
+ IP6ADDR = "(?:[#{HEXDIGIT}]+(?::[#{HEXDIGIT}]+){7}|0:0:0:0:0:(?:0|FFFF):#{IP4ADDR})"
36
+ # hostaddr = ip4addr / ip6addr
37
+ HOSTADDR = "(?:#{IP4ADDR}|#{IP6ADDR})"
38
+
39
+ # host = hostname / hostaddr
40
+ HOST = "(?:#{HOSTNAME}|#{HOSTADDR})"
41
+
42
+ # prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
43
+ PREFIX = "(?:#{NICKNAME}(?:(?:!#{USER})?@#{HOST})?|#{SERVERNAME})"
44
+
45
+ # nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF
46
+ # ; any octet except NUL, CR, LF, " " and ":"
47
+ NOSPCRLFCL = '\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x39\x3B-\xFF'
48
+
49
+ # command = 1*letter / 3digit
50
+ COMMAND = "(?:[#{LETTER}]+|[#{DIGIT}]{3})"
51
+
52
+ # SPACE = %x20 ; space character
53
+ # middle = nospcrlfcl *( ":" / nospcrlfcl )
54
+ # trailing = *( ":" / " " / nospcrlfcl )
55
+ # params = *14( SPACE middle ) [ SPACE ":" trailing ]
56
+ # =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
57
+ MIDDLE = "[#{NOSPCRLFCL}][:#{NOSPCRLFCL}]*"
58
+ TRAILING = "[: #{NOSPCRLFCL}]*"
59
+ PARAMS = "(?:((?: #{MIDDLE}){0,14})(?: :(#{TRAILING}))?|((?: #{MIDDLE}){14})(?::?)?(#{TRAILING}))"
60
+
61
+ # crlf = %x0D %x0A ; "carriage return" "linefeed"
62
+ # message = [ ":" prefix SPACE ] command [ params ] crlf
63
+ CRLF = '\x0D\x0A'
64
+ MESSAGE = "(?::(#{PREFIX}) )?(#{COMMAND})#{PARAMS}\s*#{CRLF}"
65
+
66
+ CLIENT_PATTERN = /\A#{NICKNAME}(?:(?:!#{USER})?@#{HOST})\z/on
67
+ MESSAGE_PATTERN = /\A#{MESSAGE}\z/on
68
+ end # PATTERN
@@ -0,0 +1,188 @@
1
+ class Net::IRC::Server
2
+ # Server global state for accessing Server::Session
3
+ attr_accessor :state
4
+
5
+ def initialize(host, port, session_class, opts={})
6
+ @host = host
7
+ @port = port
8
+ @session_class = session_class
9
+ @opts = OpenStruct.new(opts)
10
+ @sessions = []
11
+ @state = {}
12
+ end
13
+
14
+ # Start server loop.
15
+ def start
16
+ @serv = TCPServer.new(@host, @port)
17
+ @log = @opts.logger || Logger.new($stdout)
18
+ @log.info "Host: #{@host} Port:#{@port}"
19
+ @accept = Thread.start do
20
+ loop do
21
+ Thread.start(@serv.accept) do |s|
22
+ begin
23
+ @log.info "Client connected, new session starting..."
24
+ s = @session_class.new(self, s, @log, @opts)
25
+ @sessions << s
26
+ s.start
27
+ rescue Exception => e
28
+ puts e
29
+ puts e.backtrace
30
+ ensure
31
+ @sessions.delete(s)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ @accept.join
37
+ end
38
+
39
+ # Close all sessions.
40
+ def finish
41
+ Thread.exclusive do
42
+ @accept.kill
43
+ begin
44
+ @serv.close
45
+ rescue
46
+ end
47
+ @sessions.each do |s|
48
+ s.finish
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ class Session
55
+ include Net::IRC
56
+ include Constants
57
+
58
+ attr_reader :prefix, :nick, :real, :host
59
+
60
+ # Override subclass.
61
+ def server_name
62
+ "net-irc"
63
+ end
64
+
65
+ # Override subclass.
66
+ def server_version
67
+ "0.0.0"
68
+ end
69
+
70
+ # Override subclass.
71
+ def avaiable_user_modes
72
+ "eixwy"
73
+ end
74
+
75
+ # Override subclass.
76
+ def avaiable_channel_modes
77
+ "spknm"
78
+ end
79
+
80
+ def initialize(server, socket, logger, opts={})
81
+ @server, @socket, @log, @opts = server, socket, logger, opts
82
+ end
83
+
84
+ def self.start(*args)
85
+ new(*args).start
86
+ end
87
+
88
+ # Start session loop.
89
+ def start
90
+ on_connected
91
+ while l = @socket.gets
92
+ begin
93
+ @log.debug "RECEIVE: #{l.chomp}"
94
+ m = Message.parse(l)
95
+ next if on_message(m) === true
96
+
97
+ name = "on_#{(COMMANDS[m.command.upcase] || m.command).downcase}"
98
+ send(name, m) if respond_to?(name)
99
+
100
+ break if m.command == QUIT
101
+ rescue Message::InvalidMessage
102
+ @log.error "MessageParse: " + l.inspect
103
+ end
104
+ end
105
+ rescue IOError
106
+ ensure
107
+ finish
108
+ end
109
+
110
+ # Close this session.
111
+ def finish
112
+ begin
113
+ @socket.close
114
+ rescue
115
+ end
116
+ on_disconnected
117
+ end
118
+
119
+ # Default PASS callback.
120
+ # Set @pass.
121
+ def on_pass(m)
122
+ @pass = m.params[0]
123
+ end
124
+
125
+ # Default NICK callback.
126
+ # Set @nick.
127
+ def on_nick(m)
128
+ @nick = m.params[0]
129
+ end
130
+
131
+ # Default USER callback.
132
+ # Set @user, @real, @host and call initial_message.
133
+ def on_user(m)
134
+ @user, @real = m.params[0], m.params[3]
135
+ @host = @socket.peeraddr[2]
136
+ @prefix = Prefix.new("#{@nick}!#{@user}@#{@host}")
137
+ initial_message
138
+ end
139
+
140
+ # Call when socket connected.
141
+ def on_connected
142
+ end
143
+
144
+ # Call when socket closed.
145
+ def on_disconnected
146
+ end
147
+
148
+ # Catch all messages.
149
+ # If this method return true, aother callback will not be called.
150
+ def on_message(m)
151
+ end
152
+
153
+ # Default PING callback. Response PONG.
154
+ def on_ping(m)
155
+ post server_name, PONG, m.params[0]
156
+ end
157
+
158
+ # Do nothing.
159
+ # This is for avoiding error on calling super.
160
+ # So you can always call super at subclass.
161
+ def method_missing(name, *args)
162
+ end
163
+
164
+ private
165
+ # Post message to server.
166
+ #
167
+ # include Net::IRC::Constans
168
+ # post prefix, PRIVMSG, "#channel", "foobar"
169
+ def post(prefix, command, *params)
170
+ m = Message.new(prefix, command, params.map {|s|
171
+ s.gsub(/[\r\n]/, " ")
172
+ })
173
+ @log.debug "SEND: #{m.to_s.chomp}"
174
+ @socket << m
175
+ rescue IOError
176
+ finish
177
+ end
178
+
179
+ # Call when client connected.
180
+ # Send RPL_WELCOME sequence. If you want to customize, override this method at subclass.
181
+ def initial_message
182
+ post server_name, RPL_WELCOME, @nick, "Welcome to the Internet Relay Network #{@prefix}"
183
+ post server_name, RPL_YOURHOST, @nick, "Your host is #{server_name}, running version #{server_version}"
184
+ post server_name, RPL_CREATED, @nick, "This server was created #{Time.now}"
185
+ post server_name, RPL_MYINFO, @nick, "#{server_name} #{server_version} #{avaiable_user_modes} #{avaiable_channel_modes}"
186
+ end
187
+ end
188
+ end # Server
@@ -0,0 +1,43 @@
1
+
2
+ $LOAD_PATH << "lib"
3
+ $LOAD_PATH << "../lib"
4
+
5
+ require "net/irc"
6
+ include Net::IRC
7
+ include Constants
8
+
9
+
10
+ describe Message::ModeParser do
11
+ it "should parse RFC1459 correctly" do
12
+ Message::ModeParser::RFC1459::Channel.parse("#Finish +im")[:positive].should == [[:i, nil], [:m, nil]]
13
+ Message::ModeParser::RFC1459::Channel.parse("#Finish +o Kilroy")[:positive].should == [[:o, "Kilroy"]]
14
+ Message::ModeParser::RFC1459::Channel.parse("#Finish +v Kilroy")[:positive].should == [[:v, "Kilroy"]]
15
+ Message::ModeParser::RFC1459::Channel.parse("#Fins -s")[:negative].should == [[:s, nil]]
16
+ Message::ModeParser::RFC1459::Channel.parse("#42 +k oulu")[:positive].should == [[:k, "oulu"]]
17
+ Message::ModeParser::RFC1459::Channel.parse("#eu-opers +l 10")[:positive].should == [[:l, "10"]]
18
+ Message::ModeParser::RFC1459::Channel.parse("&oulu +b")[:positive].should == [[:b, nil]]
19
+ Message::ModeParser::RFC1459::Channel.parse("&oulu +b *!*@*")[:positive].should == [[:b, "*!*@*"]]
20
+ Message::ModeParser::RFC1459::Channel.parse("&oulu +b *!*@*.edu")[:positive].should == [[:b, "*!*@*.edu"]]
21
+
22
+ Message::ModeParser::RFC1459::Channel.parse("#foo +ooo foo bar baz").should == {
23
+ :positive => [[:o, "foo"], [:o, "bar"], [:o, "baz"]],
24
+ :negative => [],
25
+ }
26
+ Message::ModeParser::RFC1459::Channel.parse("#foo +oo-o foo bar baz").should == {
27
+ :positive => [[:o, "foo"], [:o, "bar"]],
28
+ :negative => [[:o, "baz"]],
29
+ }
30
+ Message::ModeParser::RFC1459::Channel.parse("#foo -oo+o foo bar baz").should == {
31
+ :positive => [[:o, "baz"]],
32
+ :negative => [[:o, "foo"], [:o, "bar"]],
33
+ }
34
+ Message::ModeParser::RFC1459::Channel.parse("#foo +imv foo").should == {
35
+ :positive => [[:i, nil], [:m, nil], [:v, "foo"]],
36
+ :negative => [],
37
+ }
38
+
39
+ Message::ModeParser::RFC1459::User.parse("WIZ -w")[:negative].should == [[:w, nil]]
40
+ Message::ModeParser::RFC1459::User.parse("ANGEL +i")[:positive].should == [[:i, nil]]
41
+ Message::ModeParser::RFC1459::User.parse("WIZ -o")[:negative].should == [[:o, nil]]
42
+ end
43
+ end