Auto 4.0.0.alpha.1-x86-mingw32
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.
- data/.yardopts +7 -0
- data/Gemfile +19 -0
- data/LICENSE.md +31 -0
- data/README.md +109 -0
- data/Rakefile +41 -0
- data/bin/auto +110 -0
- data/bin/auto-conf +45 -0
- data/conf/example.json +100 -0
- data/conf/example.yml +125 -0
- data/docs/Contributing.md +77 -0
- data/docs/Events.md +103 -0
- data/docs/Todo.md +21 -0
- data/docs/Upgrade.md +16 -0
- data/ext/dsl_base.c +49 -0
- data/ext/libauto/auto.h +20 -0
- data/ext/libauto/extconf.rb +16 -0
- data/ext/libauto/libauto.c +29 -0
- data/ext/libauto/libauto.h +28 -0
- data/ext/libauto/logger.c +177 -0
- data/ext/libauto/logger.h +44 -0
- data/lib/auto.rb +43 -0
- data/lib/auto/api.rb +7 -0
- data/lib/auto/api/events.rb +166 -0
- data/lib/auto/api/object.rb +29 -0
- data/lib/auto/api/plugin.rb +155 -0
- data/lib/auto/api/timers.rb +93 -0
- data/lib/auto/bot.rb +338 -0
- data/lib/auto/config.rb +181 -0
- data/lib/auto/configure.rb +410 -0
- data/lib/auto/configure/shell.rb +154 -0
- data/lib/auto/dsl/base.rb +74 -0
- data/lib/auto/dsl/irc.rb +13 -0
- data/lib/auto/irc.rb +8 -0
- data/lib/auto/irc/common.rb +63 -0
- data/lib/auto/irc/library.rb +89 -0
- data/lib/auto/irc/object/channel.rb +21 -0
- data/lib/auto/irc/object/entity.rb +90 -0
- data/lib/auto/irc/object/message.rb +99 -0
- data/lib/auto/irc/object/user.rb +139 -0
- data/lib/auto/irc/protocol.rb +164 -0
- data/lib/auto/irc/protocol/numerics.rb +60 -0
- data/lib/auto/irc/sasl/diffie_hellman.rb +36 -0
- data/lib/auto/irc/sasl/mech.rb +15 -0
- data/lib/auto/irc/sasl/mech/dh_blowfish.rb +83 -0
- data/lib/auto/irc/sasl/mech/plain.rb +39 -0
- data/lib/auto/irc/server.rb +301 -0
- data/lib/auto/irc/state/channel_manager.rb +6 -0
- data/lib/auto/irc/state/support.rb +142 -0
- data/lib/auto/irc/state/user_manager.rb +6 -0
- data/lib/auto/irc/std/commands.rb +99 -0
- data/lib/auto/irc/std/numerics.rb +216 -0
- data/lib/auto/rubyext/integer.rb +25 -0
- data/lib/auto/rubyext/string.rb +10 -0
- data/lib/auto/version.rb +18 -0
- data/lib/libauto.so +0 -0
- data/spec/api_events_spec.rb +68 -0
- data/spec/config_json_spec.rb +116 -0
- data/spec/config_other_spec.rb +29 -0
- data/spec/config_yaml_spec.rb +136 -0
- data/spec/helper.rb +19 -0
- data/spec/irc_object_entity_spec.rb +51 -0
- data/spec/logger_spec.rb +30 -0
- data/spec/plugin_base_spec.rb +35 -0
- data/spec/timers_spec.rb +42 -0
- metadata +238 -0
@@ -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,15 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
module Auto
|
5
|
+
module IRC
|
6
|
+
module SASL
|
7
|
+
module Mech
|
8
|
+
autoload :DHBlowfish, 'auto/irc/sasl/mech/dh_blowfish'
|
9
|
+
autoload :Plain, 'auto/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,83 @@
|
|
1
|
+
# Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
|
2
|
+
# This free software is distributed under the FreeBSD license (LICENSE.md).
|
3
|
+
|
4
|
+
require 'base64'
|
5
|
+
require 'openssl'
|
6
|
+
require 'auto/rubyext/integer'
|
7
|
+
require 'auto/irc/sasl/diffie_hellman'
|
8
|
+
|
9
|
+
module Auto
|
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 Auto
|
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 (LICENSE.md).
|
3
|
+
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
module Auto
|
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 Auto
|
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 (LICENSE.md).
|
3
|
+
|
4
|
+
require 'ostruct'
|
5
|
+
require 'socket'
|
6
|
+
require 'openssl'
|
7
|
+
require 'thread'
|
8
|
+
require 'auto/dsl/base'
|
9
|
+
require 'auto/irc/state/support'
|
10
|
+
require 'auto/irc/std/commands'
|
11
|
+
|
12
|
+
# namespace Auto
|
13
|
+
module Auto
|
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 [Auto::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 Auto::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 {Auto::IRC::Server}.
|
121
|
+
#
|
122
|
+
# @param [String] name The name of the server to which we should connect.
|
123
|
+
#
|
124
|
+
# @yieldparam [Auto::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 = Auto::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 Auto::IRC::Std::Commands
|
166
|
+
# State managers.
|
167
|
+
@supp = Auto::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; "#<Auto::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 Auto
|
300
|
+
|
301
|
+
# vim: set ts=4 sts=2 sw=2 et:
|