+#-- vim:sw=2:et
+#++
+#
+# :title: IRC Socket
+#
+# This module implements the IRC socket interface, including IRC message
+# penalty computation and the message queue system
+
require 'monitor'
class ::String
# Calculate the penalty which will be assigned to this message
# by the IRCd
def irc_send_penalty
- # According to eggrdop, the initial penalty is
+ # According to eggdrop, the initial penalty is
penalty = 1 + self.size/100
# on everything but UnderNET where it's
# penalty = 2 + self.size/120
dests = pars.split($;,2).first
penalty += dests.split(',').size
when :WHO
- # I'm too lazy to implement this one correctly
- penalty += 5
+ args = pars.split
+ if args.length > 0
+ penalty += args.inject(0){ |sum,x| sum += ((x.length > 4) ? 3 : 5) }
+ else
+ penalty += 10
+ end
+ when :PART
+ penalty += 4
when :AWAY, :JOIN, :VERSION, :TIME, :TRACE, :WHOIS, :DNS
penalty += 2
when :INVITE, :NICK
# normalized uri of the current server
attr_reader :server_uri
+ # penalty multiplier (percent)
+ attr_accessor :penalty_pct
+
# default trivial filter class
class IdentityFilter
def in(x)
@spooler = false
@lines_sent = 0
@lines_received = 0
- if opts.kind_of?(Hash) and opts.key?(:ssl)
- @ssl = opts[:ssl]
- else
- @ssl = false
- end
+ @ssl = opts[:ssl]
+ @ssl_verify = opts[:ssl_verify]
+ @ssl_ca_file = opts[:ssl_ca_file]
+ @ssl_ca_path = opts[:ssl_ca_path]
+ @penalty_pct = opts[:penalty_pct] || 100
end
def connected?
@conn_count += 1
@server_uri = URI.parse(srv_uri)
@server_uri.port = 6667 if !@server_uri.port
+
debug "connection attempt \##{@conn_count} (#{@server_uri.host}:#{@server_uri.port})"
+ # if the host is a bracketed (IPv6) address, strip the brackets
+ # since Ruby doesn't like them in the Socket host parameter
+ # FIXME it would be safer to have it check for a valid
+ # IPv6 bracketed address rather than just stripping the brackets
+ srv_host = @server_uri.host
+ if srv_host.match(/\A\[(.*)\]\z/)
+ srv_host = $1
+ end
+
if(@host)
begin
- sock=TCPSocket.new(@server_uri.host, @server_uri.port, @host)
+ sock=TCPSocket.new(srv_host, @server_uri.port, @host)
rescue ArgumentError => e
error "Your version of ruby does not support binding to a "
error "specific local address, please upgrade if you wish "
error "to use HOST = foo"
error "(this option has been disabled in order to continue)"
- sock=TCPSocket.new(@server_uri.host, @server_uri.port)
+ sock=TCPSocket.new(srv_host, @server_uri.port)
end
else
- sock=TCPSocket.new(@server_uri.host, @server_uri.port)
+ sock=TCPSocket.new(srv_host, @server_uri.port)
end
if(@ssl)
require 'openssl'
ssl_context = OpenSSL::SSL::SSLContext.new()
- ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ if @ssl_verify
+ ssl_context.ca_file = @ssl_ca_file if @ssl_ca_file and not @ssl_ca_file.empty?
+ ssl_context.ca_path = @ssl_ca_path if @ssl_ca_path and not @ssl_ca_path.empty?
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
+ else
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ end
sock = OpenSSL::SSL::SSLSocket.new(sock, ssl_context)
sock.sync_close = true
sock.connect
error "error while shutting down: #{e.pretty_inspect}"
end
@sock = nil
+ @server_uri = nil
@sendq.clear
end
loop do
begin
now = Time.now
- flood_delay = @flood_send - MAX_IRC_SEND_PENALTY - now
- delay = [flood_delay, 0].max
- if delay > 0
+ flood_delay = @flood_send - MAX_IRC_SEND_PENALTY - now
+ delay = [flood_delay, 0].max
+ if delay > 0
debug "sleep(#{delay}) # (f: #{flood_delay})"
sleep(delay)
end
msg = @sendq.shift
- now = Time.now
- @flood_send = now if @flood_send < now
debug "got #{msg.inspect} from queue, sending"
emergency_puts(msg, true)
rescue Exception => e
# the latter is racy and can cause double message output in
# some circumstances
actual = @filter.out(message) + "\n"
+ now = Time.new
@sock.syswrite actual
- @last_send = Time.new
- @flood_send += message.irc_send_penalty if penalty
+ @last_send = now
+ @flood_send = now if @flood_send < now
+ @flood_send += message.irc_send_penalty*@penalty_pct/100.0 if penalty
@lines_sent += 1
end
rescue Exception => e