syndi 0.1.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +0 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +28 -0
  6. data/README.md +104 -0
  7. data/Rakefile +30 -0
  8. data/WINDOWS.md +64 -0
  9. data/bin/syndi +102 -0
  10. data/bin/syndi-conf +47 -0
  11. data/conf/example.yml +101 -0
  12. data/docs/Events.md +103 -0
  13. data/docs/Upgrade.md +16 -0
  14. data/ext/csyndi/events.c +50 -0
  15. data/ext/csyndi/extconf.rb +20 -0
  16. data/ext/csyndi/integer.c +53 -0
  17. data/ext/csyndi/libauto.c +37 -0
  18. data/ext/csyndi/logger.c +229 -0
  19. data/include/syndi.h +22 -0
  20. data/include/syndi/csyndi.h +38 -0
  21. data/include/syndi/events.h +19 -0
  22. data/include/syndi/integer.h +17 -0
  23. data/include/syndi/logger.h +56 -0
  24. data/lib/csyndi.so +0 -0
  25. data/lib/syndi.rb +137 -0
  26. data/lib/syndi/actress.rb +12 -0
  27. data/lib/syndi/api.rb +7 -0
  28. data/lib/syndi/api/object.rb +29 -0
  29. data/lib/syndi/bot.rb +266 -0
  30. data/lib/syndi/config.rb +113 -0
  31. data/lib/syndi/dsl/base.rb +74 -0
  32. data/lib/syndi/dsl/irc.rb +13 -0
  33. data/lib/syndi/events.rb +130 -0
  34. data/lib/syndi/irc.rb +8 -0
  35. data/lib/syndi/irc/common.rb +63 -0
  36. data/lib/syndi/irc/library.rb +89 -0
  37. data/lib/syndi/irc/object/channel.rb +21 -0
  38. data/lib/syndi/irc/object/entity.rb +90 -0
  39. data/lib/syndi/irc/object/message.rb +99 -0
  40. data/lib/syndi/irc/object/user.rb +139 -0
  41. data/lib/syndi/irc/protocol.rb +161 -0
  42. data/lib/syndi/irc/protocol/numerics.rb +60 -0
  43. data/lib/syndi/irc/sasl/diffie_hellman.rb +36 -0
  44. data/lib/syndi/irc/sasl/mech.rb +7 -0
  45. data/lib/syndi/irc/sasl/mech/dh_blowfish.rb +83 -0
  46. data/lib/syndi/irc/sasl/mech/plain.rb +39 -0
  47. data/lib/syndi/irc/server.rb +301 -0
  48. data/lib/syndi/irc/state/channel_manager.rb +6 -0
  49. data/lib/syndi/irc/state/support.rb +142 -0
  50. data/lib/syndi/irc/state/user_manager.rb +6 -0
  51. data/lib/syndi/irc/std/commands.rb +99 -0
  52. data/lib/syndi/irc/std/numerics.rb +216 -0
  53. data/lib/syndi/jewel.rb +5 -0
  54. data/lib/syndi/jewel/specification.rb +121 -0
  55. data/lib/syndi/jewel/util.rb +27 -0
  56. data/lib/syndi/rubyext/string.rb +10 -0
  57. data/lib/syndi/verbosity.rb +10 -0
  58. data/lib/syndi/version.rb +38 -0
  59. data/spec/helper.rb +37 -0
  60. data/spec/syndi/events_spec.rb +89 -0
  61. data/tasks/compile.rake +15 -0
  62. data/tasks/install.rake +10 -0
  63. data/tasks/package.rake +13 -0
  64. data/tasks/spec.rake +12 -0
  65. metadata +243 -0
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'syndi/irc/std/numerics'
5
+
6
+ module Syndi
7
+
8
+ module IRC
9
+
10
+ class Protocol
11
+
12
+ # An extension of {Syndi::IRC::Protocol}, which parses numerics.
13
+ module Numerics
14
+ include Syndi::IRC::Std::Numerics
15
+
16
+ # RPL_WELCOME
17
+ define_method("on_#{RPL_WELCOME}") do |irc, raw, params|
18
+ $m.info "Successfully connected to #{irc} IRC network!" # log
19
+ end
20
+
21
+ # RPL_MYINFO
22
+ define_method("on_#{RPL_MYINFO}") do |irc, raw, params|
23
+ irc.supp.server_name = params[3]
24
+ end
25
+
26
+ # RPL_ISUPPORT
27
+ define_method("on_#{RPL_ISUPPORT}") do |irc, raw, params|
28
+ irc.supp.isupport(irc, params[3..-5].join(' ')) # process the isupport data
29
+ unless irc.supp.connected
30
+ irc.supp.connected = true
31
+ $m.irc.events.call :connected, irc # emit the :connected event
32
+ end
33
+ end
34
+
35
+ # RPL_SASLSUCCESS
36
+ define_method("on_#{RPL_SASLSUCCESS}") do |irc, raw, params|
37
+ $m.info "SASL authentication on #{irc} succeeded!"
38
+ irc.cap_end
39
+ end
40
+
41
+ # ERR_SASLFAIL
42
+ define_method("on_#{ERR_SASLFAIL}") do |irc, raw, params|
43
+ if irc.supp.sasl_method == :dh_blowfish
44
+ irc.authenticate # try again with the PLAIN mechanism
45
+ else
46
+ # ope, failed
47
+ $m.error "SASL authentication on #{irc} failed: received ERR_SASLFAIL from server."
48
+ irc.cap_end
49
+ end
50
+ end
51
+
52
+ end # class Numerics
53
+
54
+ end # class Protocol
55
+
56
+ end # module IRC
57
+
58
+ end # module Syndi
59
+
60
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,36 @@
1
+ # This code exists in the public domain.
2
+
3
+ class DiffieHellman
4
+
5
+ attr_reader :p, :g, :q, :x, :e
6
+
7
+ # p is the prime, g the generator and q order of the subgroup
8
+ def initialize p, g, q
9
+ @p = p
10
+ @g = g
11
+ @q = q
12
+ end
13
+
14
+ # generate the [secret] random value and the public key
15
+ def generate tries=16
16
+ tries.times do
17
+ @x = rand(@q)
18
+ @e = self.g.mod_exp(@x, self.p)
19
+ return @e if self.valid?
20
+ end
21
+ raise ArgumentError, "can't generate valid e"
22
+ end
23
+
24
+ # validate a public key
25
+ def valid?(_e = self.e)
26
+ _e and _e.between?(2, self.p-2) and _e.bits_set > 1
27
+ end
28
+
29
+ # compute the shared secret, given the public key
30
+ def secret f
31
+ f.mod_exp(self.x, self.p)
32
+ end
33
+
34
+ end
35
+
36
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'syndi/irc/sasl/mech/dh_blowfish'
5
+ require 'syndi/irc/sasl/mech/plain'
6
+
7
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,83 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'base64'
5
+ require 'openssl'
6
+ require 'csyndi'
7
+ require 'syndi/irc/sasl/diffie_hellman'
8
+
9
+ module Syndi
10
+
11
+ module IRC
12
+
13
+ module SASL
14
+
15
+ module Mech
16
+
17
+ # Module which implements the SASL DH-BLOWFISH mechanism.
18
+ # @author noxgirl
19
+ module DHBlowfish
20
+
21
+ # Create an SASL-encrypted hash.
22
+ #
23
+ # @param [String] username The username.
24
+ # @param [String] password The password associated with the username.
25
+ # @param [String] provision The key provided by the server.
26
+ def self.encrypt username, password, provision
27
+ # This is a fairly complex process. Duplicate +username+ and
28
+ # +password+ for safety.
29
+ user = username.dup
30
+ pass = password.dup
31
+
32
+ # Decode the key from base64.
33
+ key = Base64.decode64(provision).force_encoding('ASCII-8BIT')
34
+
35
+ # Unpack it.
36
+ p, g, y = unpack_key key
37
+
38
+ dh = DiffieHellman.new(p, g, 23)
39
+ pkey = dh.generate
40
+ secret = OpenSSL::BN.new(dh.secret(y).to_s).to_s(2)
41
+ pub = OpenSSL::BN.new(pkey.to_s).to_s(2)
42
+
43
+ pass.concat "\0"
44
+ pass.concat('.' * (8 - (password.size % 8)))
45
+
46
+ cipher = OpenSSL::Cipher::Cipher.new 'BF-ECB'
47
+ cipher.key_len = 32
48
+ cipher.encrypt
49
+ cipher.key = secret
50
+
51
+ enc = cipher.update(pass).to_s
52
+ answer = [pub.bytesize, pub, user, enc].pack('na*Z*a*')
53
+
54
+ Base64.strict_encode64(answer) # finally, return the hash
55
+ end
56
+
57
+ # @return [Array(Numeric, Numeric, Numeric)] +p+, +g+, and +y+ for
58
+ # Diffie-Hellman key exchange
59
+ def self.unpack_key key
60
+ key = key.dup
61
+ pgy = []
62
+
63
+ 3.times do
64
+ size = key.unpack('n').first
65
+ key.slice! 0, 2
66
+ pgy << key.unpack("a#{size}").first
67
+ key.slice!(0, size)
68
+ end
69
+
70
+ pgy.map { |int| OpenSSL::BN.new(int, 2).to_i }
71
+ end
72
+
73
+ end # module DHBlowfish
74
+
75
+ end # module Mech
76
+
77
+ end # module SASL
78
+
79
+ end # module IRC
80
+
81
+ end # module Syndi
82
+
83
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'base64'
5
+
6
+ module Syndi
7
+
8
+ module IRC
9
+
10
+ module SASL
11
+
12
+ module Mech
13
+
14
+ # Module which implements the SASL PLAIN mechanism.
15
+ module Plain
16
+
17
+ # Create an SASL-encrypted hash.
18
+ #
19
+ # @author noxgirl
20
+ #
21
+ # @param [String] username The username.
22
+ # @param [String] password The password associated with the username.
23
+ # @param [String] provision The key provided by the server.
24
+ def self.encrypt username, password, provision
25
+ # Easy as this:
26
+ Base64.encode64([username, username, password].join("\0")).gsub(/\n/, '')
27
+ end
28
+
29
+ end # module Plain
30
+
31
+ end # module Mech
32
+
33
+ end # module SASL
34
+
35
+ end # module IRC
36
+
37
+ end # module Syndi
38
+
39
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,301 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (see LICENSE).
3
+
4
+ require 'ostruct'
5
+ require 'socket'
6
+ require 'openssl'
7
+ require 'thread'
8
+ require 'syndi/dsl/base'
9
+ require 'syndi/irc/state/support'
10
+ require 'syndi/irc/std/commands'
11
+
12
+ # namespace Syndi
13
+ module Syndi
14
+
15
+ # Entering namespace: IRC
16
+ module IRC
17
+
18
+ # A class which maintains a connection to an IRC server and provides a highly
19
+ # usable interface for the IRC server.
20
+ #
21
+ # @api IRC
22
+ # @since 4.0.0
23
+ # @author noxgirl
24
+ #
25
+ # @!attribute [r] socket
26
+ # @return [TCPSocket] The TCP socket being used for the connection.
27
+ #
28
+ # @!attribute [r] in
29
+ # @return [Integer] The number of bytes received from the socket.
30
+ #
31
+ # @!attribute [r] out
32
+ # @return [Integer] The number of bytes sent to the socket.
33
+ #
34
+ # @!attribute [r] type
35
+ # @return [Symbol] +:irc+
36
+ #
37
+ # @!attribute [r] supp
38
+ # @return [Syndi::IRC::State::Support] The IRCd capabilities.
39
+ #
40
+ # @!attribute name
41
+ # @return [String] The name of the server as specified by configuration.
42
+ #
43
+ # @!attribute address
44
+ # @return [String] The address used to connect to the server.
45
+ #
46
+ # @!attribute port
47
+ # @return [Integer] The port used to connect to the server
48
+ #
49
+ # @!attribute nick
50
+ # @return [String] The nickname of the bot on the server.
51
+ #
52
+ # @!attribute user
53
+ # @return [String] The username of the bot on the server.
54
+ #
55
+ # @!attribute real
56
+ # @return [String] The real name or GECOS of the bot on the server.
57
+ #
58
+ # @!attribute password
59
+ # @return [String] If needed, the password used to connect to the server
60
+ # @return [nil] If not needed.
61
+ #
62
+ # @!attribute bind
63
+ # @return [String] If desired, the address to which to bind for this socket
64
+ # @return [nil] If not desired.
65
+ # @note This appears to be unused at the moment.
66
+ #
67
+ # @!attribute ssl
68
+ # @return [true, false] If SSL should [not] be used for the connection.
69
+ #
70
+ # @!attribute sasl_id
71
+ # @return [String] If SASL is desired, the username with which to authenticate.
72
+ # @return [nil] If not used.
73
+ # @note This is seemingly deprecated?
74
+ #
75
+ # @!attribute connected
76
+ # @return [true, false] Whether or not we are connected to the server.
77
+ #
78
+ # @!attribute mask
79
+ # @return [String] The bot's own hostname or mask on the IRC server.
80
+ #
81
+ # @!attribute recvq
82
+ # @return [Array<String>] The socket's receive queue, which is comprised of an array
83
+ # of strings which are pending processing.
84
+ #
85
+ #
86
+ # @!attribute prefixes
87
+ # @return [Hash{String => String}] The IRC server's supported prefixes, with the key being
88
+ # the channel mode which represents the prefix, and the value being the prefix.
89
+ #
90
+ # @!attribute channel_modes
91
+ # @return [Hash{Symbol => Array<String>}] The IRC server's supported channel modes, divided as thus:
92
+ #
93
+ # - +:list+ = A list of modes which add/remove a nickname or mask from a channel list, such as ops and bans.
94
+ # - +:always+ = A llst of modes which change a channel setting, and always have a parameter.
95
+ # - +:set+ = A list of modes which change a channel setting, and which have a parameter only when set.
96
+ # - +:never+ = A list of modes which change a channel setting, and which never have a parameter.
97
+ #
98
+ # @!attribute max_modes
99
+ # @return [Integer] The maximum number of mode changes which may be specified in a /MODE query.
100
+ #
101
+ # @!attribute await_self_who
102
+ # @return [true, false] Whether or not we are awaiting for a response to a /WHO on ourselves.
103
+ #
104
+ # @!attribute channels
105
+ # @return [Hash{String => IRC::Object::Channel}] A list of channels in which we reside,
106
+ # with each key being the channel's name in all-lowercase, and the respective values
107
+ # being of {IRC::Object::Channel IRC::Object::Channel}.
108
+ #
109
+ # @!attribute users
110
+ # @return [Hash{String => IRC::Object::User}] A list of users who are known to us,
111
+ # with each key being the user's nickname in all-lowercase, and the respective values
112
+ # being of {IRC::Object::User IRC::Object::User}.
113
+ class Server
114
+ include Syndi::DSL::Base
115
+
116
+ attr_reader :socket, :in, :out, :type, :supp
117
+ attr_accessor :name, :address, :port, :nick, :user, :real, :password,
118
+ :bind, :ssl, :connected, :chans, :users
119
+
120
+ # Produce a new instance of {Syndi::IRC::Server}.
121
+ #
122
+ # @param [String] name The name of the server to which we should connect.
123
+ #
124
+ # @yieldparam [Syndi::IRC::Server] c This instance, intended for configuration of the
125
+ # attributes.
126
+ #
127
+ # Configuration attributes are +address+, +port+, +nick+, +user+, +real+,
128
+ # +password+, +bind+, and +ssl+.
129
+ #
130
+ #
131
+ # @example
132
+ # irc = Syndi::IRC::Server.new('Freenode') do |c|
133
+ # c.address = 'irc.freenode.net'
134
+ # c.port = 7000
135
+ # c.nick = 'cowmoon'
136
+ # c.user = 'foo1'
137
+ # c.real = "The night is lovely."
138
+ # c.bind = 'localhost'
139
+ # c.ssl = true
140
+ # end
141
+ def initialize(name)
142
+
143
+ # Prepare attributes.
144
+ @name = name
145
+ @address = nil
146
+ @port = nil
147
+ @nick = nil
148
+ @user = nil
149
+ @real = nil
150
+ @password = nil
151
+ @bind = nil
152
+ @ssl = false
153
+
154
+ # Yield for configuration.
155
+ yield(self) if block_given? or raise ArgumentError, "Server #{name} unable to initialize because it was not configured."
156
+
157
+ # Additional instance attributes.
158
+ @in = 0
159
+ @out = 0
160
+ @socket = nil
161
+ @connected = false
162
+ @type = :irc
163
+
164
+ # Pull in commands.
165
+ extend Syndi::IRC::Std::Commands
166
+ # State managers.
167
+ @supp = Syndi::IRC::State::Support.new
168
+ @chans = nil
169
+ @users = nil
170
+
171
+ # Our receive queue remainder.
172
+ @recv_rem = nil
173
+
174
+ # Mutual exclusion for thread safety.
175
+ @lock = Mutex.new
176
+
177
+ end
178
+
179
+ # Establish (or attempt to) a connection with the server.
180
+ def connect
181
+
182
+ # Check for missing attributes.
183
+ begin
184
+ attribute_check
185
+ rescue => e
186
+ $m.error("Cannot connect to server #@name: #{e}", false, e.backtrace)
187
+ end
188
+
189
+ $m.info("Connecting to #@name @ #@address:#@port...")
190
+
191
+ # Create a new socket.
192
+ begin
193
+ socket = TCPSocket.new(@address, @port, @bind)
194
+ rescue => e
195
+ $m.error("Failed to connect to server #@name: #{e}", false, e.backtrace)
196
+ raise
197
+ end
198
+
199
+ # Wrap it in SSL if told to.
200
+ if ssl
201
+ begin
202
+ socket = OpenSSL::SSL::SSLSocket.new(socket)
203
+ socket.connect
204
+ rescue => e
205
+ $m.error("Failed to connect to server #@name: #{e}", false, e.backtrace)
206
+ raise
207
+ end
208
+ end
209
+
210
+ @socket = socket
211
+
212
+ # Register.
213
+ emit :irc, :preconnect, self
214
+ pass @password if @password
215
+ snd 'CAP LS'
216
+ self.nickname = @nick
217
+ user @user, Socket.gethostname, @address, @real
218
+
219
+ end
220
+
221
+ # Send data to the socket.
222
+ #
223
+ # @param [String] data The string of data, which should not exceed 512 in length.
224
+ def snd data
225
+ $m.foreground("{irc-send} #@name << #{data}")
226
+ @socket.write("#{data}\r\n")
227
+ @out += "#{data}\r\n".length
228
+ end
229
+
230
+ # Receive data from the socket, and push it into the recvQ.
231
+ def recv
232
+
233
+ # Thread safety.
234
+ @lock.synchronize do
235
+
236
+ if @socket.nil? or @socket.eof?
237
+ return
238
+ end
239
+
240
+ # Read the data.
241
+ data = @socket.sysread(1024)
242
+ # Increase in.
243
+ @in += data.length
244
+
245
+ # Split the data.
246
+ recv = []
247
+ until data !~ /\r\n/
248
+ line, data = data.split(/(?<=\r\n)/, 2)
249
+ recv.push line.chomp "\r\n"
250
+ end
251
+
252
+ # Check if there's a remainder in the recvQ.
253
+ if @recv_rem != ''
254
+ recv[0] = "#@recv_rem#{recv[0]}"
255
+ @recv_rem = ''
256
+ end
257
+ @recv_rem = data if data != ''
258
+
259
+ # Lastly, sent the data out
260
+ recv.each do |dline|
261
+ $m.foreground("{irc-recv} #@name >> #{dline}")
262
+ emit :irc, :receive, self, dline # send it out to :receive
263
+ end
264
+
265
+ end
266
+
267
+ end
268
+
269
+ def to_s; @name; end
270
+ def inspect; "#<Syndi::IRC::Server: name='#@name'>"; end
271
+ alias_method :s, :to_s
272
+
273
+ #######
274
+ private
275
+ #######
276
+
277
+ # Check the presence of all attributes.
278
+ def attribute_check
279
+ raise(ConfigError, "Missing server address") unless @address
280
+ raise(ConfigError, "Missing server port") unless @port
281
+ raise(ConfigError, "Missing nickname to use") unless @nick
282
+ raise(ConfigError, "Missing username to use") unless @user
283
+ raise(ConfigError, "Missing realname to use") unless @real
284
+ end
285
+
286
+ # Check if we are connected.
287
+ #
288
+ # @return [true, false]
289
+ def connected?
290
+ return false unless @socket
291
+ return false unless @connected
292
+ true
293
+ end
294
+
295
+ end # class Server
296
+
297
+ end # module IRC
298
+
299
+ end # module Syndi
300
+
301
+ # vim: set ts=4 sts=2 sw=2 et: