syndi 0.0.1 → 0.1.0

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +12 -0
  3. data/CHANGELOG.md +0 -0
  4. data/Gemfile +8 -0
  5. data/INSTALL.md +86 -0
  6. data/LICENSE +28 -0
  7. data/README.md +104 -0
  8. data/Rakefile +26 -0
  9. data/WINDOWS.md +64 -0
  10. data/bin/syndi +102 -0
  11. data/bin/syndi-conf +47 -0
  12. data/conf/example.yml +101 -0
  13. data/docs/Events.md +103 -0
  14. data/docs/Upgrade.md +16 -0
  15. data/ext/csyndi/events.c +50 -0
  16. data/ext/csyndi/extconf.rb +20 -0
  17. data/ext/csyndi/integer.c +53 -0
  18. data/ext/csyndi/libauto.c +37 -0
  19. data/ext/csyndi/logger.c +228 -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 +57 -0
  24. data/include/syndi.h +22 -0
  25. data/lib/syndi/actress.rb +12 -0
  26. data/lib/syndi/api/events.rb +170 -0
  27. data/lib/syndi/api/object.rb +29 -0
  28. data/lib/syndi/api/plugin.rb +155 -0
  29. data/lib/syndi/api.rb +7 -0
  30. data/lib/syndi/bot.rb +270 -0
  31. data/lib/syndi/config.rb +113 -0
  32. data/lib/syndi/configure/cli.rb +23 -0
  33. data/lib/syndi/configure/generator.rb +410 -0
  34. data/lib/syndi/configure.rb +19 -0
  35. data/lib/syndi/dsl/base.rb +74 -0
  36. data/lib/syndi/dsl/irc.rb +13 -0
  37. data/lib/syndi/events.rb +114 -0
  38. data/lib/syndi/irc/common.rb +63 -0
  39. data/lib/syndi/irc/library.rb +89 -0
  40. data/lib/syndi/irc/object/channel.rb +21 -0
  41. data/lib/syndi/irc/object/entity.rb +90 -0
  42. data/lib/syndi/irc/object/message.rb +99 -0
  43. data/lib/syndi/irc/object/user.rb +139 -0
  44. data/lib/syndi/irc/protocol/numerics.rb +60 -0
  45. data/lib/syndi/irc/protocol.rb +164 -0
  46. data/lib/syndi/irc/sasl/diffie_hellman.rb +36 -0
  47. data/lib/syndi/irc/sasl/mech/dh_blowfish.rb +83 -0
  48. data/lib/syndi/irc/sasl/mech/plain.rb +39 -0
  49. data/lib/syndi/irc/sasl/mech.rb +15 -0
  50. data/lib/syndi/irc/server.rb +301 -0
  51. data/lib/syndi/irc/state/channel_manager.rb +6 -0
  52. data/lib/syndi/irc/state/support.rb +142 -0
  53. data/lib/syndi/irc/state/user_manager.rb +6 -0
  54. data/lib/syndi/irc/std/commands.rb +99 -0
  55. data/lib/syndi/irc/std/numerics.rb +216 -0
  56. data/lib/syndi/irc.rb +8 -0
  57. data/lib/syndi/jewel/specification.rb +121 -0
  58. data/lib/syndi/jewel/util.rb +27 -0
  59. data/lib/syndi/jewel.rb +5 -0
  60. data/lib/syndi/rubyext/string.rb +10 -0
  61. data/lib/syndi/verbosity.rb +10 -0
  62. data/lib/syndi/version.rb +38 -0
  63. data/lib/syndi.rb +129 -0
  64. data/spec/helper.rb +32 -0
  65. data/spec/syndi/events_spec.rb +43 -0
  66. data/tasks/compile.rake +15 -0
  67. data/tasks/install.rake +10 -0
  68. data/tasks/package.rake +13 -0
  69. data/tasks/spec.rake +12 -0
  70. metadata +101 -13
@@ -0,0 +1,164 @@
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/protocol/numerics'
5
+
6
+ # namespace Syndi
7
+ module Syndi
8
+
9
+ # namespace IRC
10
+ module IRC
11
+ syndiload :SASL, 'syndi/irc/sasl/mech'
12
+
13
+ # A class for parsing of data per the specifications of the IRC protocol,
14
+ # v3.1.
15
+ #
16
+ # @see http://tools.ietf.org/html/rfc1459
17
+ # @see http://ircv3.atheme.org/
18
+ #
19
+ # @since 4.0.0
20
+ class Protocol
21
+
22
+ # Construct a new IRC data parser.
23
+ #
24
+ # @param [Syndi::IRC::Library] lib The IRC library instance.
25
+ def initialize lib
26
+ extend Syndi::IRC::Protocol::Numerics
27
+ lib.events.on :receive, &method(:parse)
28
+ end
29
+
30
+ # Parse IRC data.
31
+ #
32
+ # @param [Syndi::IRC::Server] irc The IRC connection.
33
+ # @param [String] raw The data received.
34
+ def parse irc, raw
35
+
36
+ params = raw.split(/\s+/)
37
+ command = (raw =~ /^:/ ? params[1] : params[0]).dc
38
+
39
+ # Check if we process this command.
40
+ if respond_to? "on_#{command}"
41
+ send("on_#{command}", irc, raw, params)
42
+ end
43
+
44
+ end
45
+
46
+ # AUTHENTICATE
47
+ #
48
+ # @param [Syndi::IRC::Server] irc The IRC connection.
49
+ # @param [String] raw The data received.
50
+ # @param [Array<String>] params The data received divided by +\s+ through
51
+ # regexp.
52
+ def on_authenticate irc, raw, params
53
+ username = $m.conf['irc'][irc.s]['SASL']['username']
54
+ password = $m.conf['irc'][irc.s]['SASL']['password']
55
+
56
+ if irc.supp.sasl_method == :dh_blowfish
57
+ crypt = Syndi::IRC::SASL::Mech::DHBlowfish.encrypt(username, password, params.last)
58
+ elsif irc.supp.sasl_method == :plain
59
+ crypt = Syndi::IRC::SASL::Mech::Plain.encrypt(username, password, params.last)
60
+ end
61
+
62
+ while crypt.length >= 400
63
+ irc.snd "AUTHENTICATE #{crypt.slice!(0, 400)}"
64
+ end
65
+
66
+ if crypt.length > 0
67
+ irc.snd "AUTHENTICATE #{crypt}"
68
+ else
69
+ irc.snd('AUTHENTICATE +')
70
+ end
71
+ # And we're done!
72
+ end
73
+
74
+ # CAP
75
+ #
76
+ # @param [Syndi::IRC::Server] irc The IRC connection.
77
+ # @param [String] raw The data received.
78
+ # @param [Array<String>] params The data received divided by +\s+ through
79
+ # regexp.
80
+ def on_cap irc, raw, params
81
+ case params[3]
82
+
83
+ when 'LS'
84
+ params[4].gsub!(/^:/, '')
85
+ cap_ls(irc, params[4..-1])
86
+ when 'ACK'
87
+ params[4].gsub!(/^:/, '')
88
+ cap_ack(irc, params[4..-1])
89
+ end
90
+ end
91
+
92
+ # PING
93
+ #
94
+ # Return a PONG.
95
+ #
96
+ # @param [Syndi::IRC::Server] irc The IRC connection.
97
+ # @param [String] raw The data received.
98
+ # @param [Array<String>] params The data received divided by +\s+ through
99
+ # regexp.
100
+ def on_ping irc, raw, params
101
+ irc.snd("PONG #{params[1]}")
102
+ end
103
+
104
+ #######
105
+ private
106
+ #######
107
+
108
+ # CAP LS
109
+ #
110
+ # Currently, Syndi's capabilities include the multi-prefix, sasl,
111
+ # account-notify, away-notify, and extended-join extensions.
112
+ #
113
+ # @param [Syndi::IRC::Server] irc The IRC connection.
114
+ # @param [Array<String>] list List of capabilities.
115
+ def cap_ls irc, list
116
+
117
+ req = []
118
+
119
+ # Every extension possible will be used except SASL, which requires
120
+ # special configuration.
121
+ %w[multi-prefix account-notify away-notify extended-join].each do |ext|
122
+ req.push ext if list.include? ext
123
+ end
124
+
125
+ if $m.conf['irc'][irc.s]['SASL'] and list.include? 'sasl'
126
+ req.push 'sasl'
127
+ end
128
+
129
+ # Send CAP REQ.
130
+ irc.snd("CAP REQ :#{req.join(' ')}") unless req.empty?
131
+
132
+ end
133
+
134
+ # CAP ACK
135
+ #
136
+ # We must save all capabilities into +irc.supp.cap+, and initiate SASL
137
+ # if possible.
138
+ #
139
+ # @param [Syndi::IRC::Server] irc The IRC connection.
140
+ # @param [Array<String>] list List of capabilities.
141
+ def cap_ack irc, list
142
+
143
+ irc.supp.cap = list
144
+
145
+ if list.include? 'sasl'
146
+ irc.supp.sasl_id << $m.clock.spawn($m.conf['irc'][irc.s]['SASL']['timeout']||10, :once, irc) do |s|
147
+ $m.error "SASL authentication on #{s} failed: authentication procedure timed out."
148
+ s.snd('AUTHENTICATE *')
149
+ s.cap_end
150
+ end
151
+ irc.authenticate :dh_blowfish
152
+ else
153
+ irc.snd('CAP END') # end capability negotiation and complete registration
154
+ end
155
+
156
+ end
157
+
158
+ end # class Protocol
159
+
160
+ end # module IRC
161
+
162
+ end # module Syndi
163
+
164
+ # 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,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 'libsyndi'
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,15 @@
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
+ module Syndi
5
+ module IRC
6
+ module SASL
7
+ module Mech
8
+ syndiload :DHBlowfish, 'syndi/irc/sasl/mech/dh_blowfish'
9
+ syndiload :Plain, 'syndi/irc/sasl/mech/plain'
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ # 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:
@@ -0,0 +1,6 @@
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
+
5
+
6
+ # vim: set ts=4 sts=2 sw=2 et: