In Files

Parent

Included Modules

Class/Module Index [+]

Quicksearch

Cinch::IRC

This class manages the connection to the IRC server. That includes processing incoming and outgoing messages, creating Ruby objects and invoking plugins.

Attributes

bot[R]

@return [Bot]

isupport[R]

@return [ISupport]

network[R]

@return [Network] The detected network

Public Class Methods

new(bot) click to toggle source
# File lib/cinch/irc.rb, line 21
def initialize(bot)
  @bot      = bot
  @isupport = ISupport.new
end

Public Instance Methods

connect() click to toggle source

@api private @return [Boolean] True if the connection could be established

# File lib/cinch/irc.rb, line 45
def connect
  tcp_socket = nil

  begin
    Timeout::timeout(@bot.config.timeouts.connect) do
      tcp_socket = TCPSocket.new(@bot.config.server, @bot.config.port, @bot.config.local_host)
    end
  rescue Timeout::Error
    @bot.loggers.warn("Timed out while connecting")
    return false
  rescue SocketError => e
    @bot.loggers.warn("Could not connect to the IRC server. Please check your network: #{e.message}")
    return false
  rescue => e
    @bot.loggers.exception(e)
    return false
  end

  if @bot.config.ssl.use
    setup_ssl(tcp_socket)
  else
    @socket = tcp_socket
  end

  @socket              = Net::BufferedIO.new(@socket)
  @socket.read_timeout = @bot.config.timeouts.read
  @queue               = MessageQueue.new(@socket, @bot)

  return true
end
parse(input) click to toggle source

@api private @return [void]

# File lib/cinch/irc.rb, line 221
def parse(input)
  return if input.chomp.empty?
  @bot.loggers.incoming(input)

  msg          = Message.new(input, @bot)
  events       = [[:catchall]]

  if ("001".."004").include? msg.command
    @registration << msg.command
    if registered?
      events << [:connect]
      @bot.last_connection_was_successful = true
      on_connect(msg, events)
    end
  end

  if ["PRIVMSG", "NOTICE"].include?(msg.command)
    events << [:ctcp] if msg.ctcp?
    if msg.channel?
      events << [:channel]
    else
      events << [:private]
    end

    if msg.command == "PRIVMSG"
      events << [:message]
    end

    if msg.action?
      events << [:action]
    end
  end

  meth = "on_#{msg.command.downcase}"
  __send__(meth, msg, events) if respond_to?(meth, true)

  if msg.error?
    events << [:error]
  end

  events << [msg.command.downcase.to_sym]

  msg.events = events.map(&:first)
  events.each do |event, *args|
    @bot.handlers.dispatch(event, msg, *args)
  end
end
registered?() click to toggle source

@return [Boolean] true if we successfully registered yet

# File lib/cinch/irc.rb, line 270
def registered?
  (("001".."004").to_a - @registration).empty?
end
send(msg) click to toggle source

Send a message to the server. @param [String] msg @return [void]

# File lib/cinch/irc.rb, line 277
def send(msg)
  @queue.queue(msg)
end
send_cap_end() click to toggle source

@since 2.0.0 @api private @return [void]

# File lib/cinch/irc.rb, line 121
def send_cap_end
  send "CAP END"
end
send_cap_ls() click to toggle source

@api private @return [void] @since 2.0.0

# File lib/cinch/irc.rb, line 107
def send_cap_ls
  send "CAP LS"
end
send_cap_req() click to toggle source

@api private @return [void] @since 2.0.0

# File lib/cinch/irc.rb, line 114
def send_cap_req
  send "CAP REQ :" + ([:"away-notify", :"multi-prefix", :sasl] & @network.capabilities).join(" ")
end
send_login() click to toggle source

@api private @return [void] @since 2.0.0

# File lib/cinch/irc.rb, line 128
def send_login
  send "PASS #{@bot.config.password}" if @bot.config.password
  send "NICK #{@bot.generate_next_nick!}"
  send "USER #{@bot.config.user} 0 * :#{@bot.config.realname}"
end
send_sasl() click to toggle source

@since 2.0.0

# File lib/cinch/irc.rb, line 189
def send_sasl
  if @bot.config.sasl.username && @sasl_current_method = @sasl_remaining_methods.pop
    @bot.loggers.info "[SASL] Trying to authenticate with #{@sasl_current_method.mechanism_name}"
    send "AUTHENTICATE #{@sasl_current_method.mechanism_name}"
  else
    send_cap_end
  end
end
setup() click to toggle source

@api private @return [void] @since 2.0.0

# File lib/cinch/irc.rb, line 36
def setup
  @registration  = []
  @network       = Network.new(:unknown, :unknown)
  @whois_updates = Hash.new {|h, k| h[k] = {}}
  @in_lists      = Set.new
end
setup_ssl(socket) click to toggle source

@api private @return [void] @since 2.0.0

# File lib/cinch/irc.rb, line 79
def setup_ssl(socket)
  # require openssl in this method so the bot doesn't break for
  # people who don't have SSL but don't want to use SSL anyway.
  require 'openssl'

  ssl_context = OpenSSL::SSL::SSLContext.new

  if @bot.config.ssl.is_a?(Configuration::SSL)
    if @bot.config.ssl.client_cert
      ssl_context.cert = OpenSSL::X509::Certificate.new(File.read(@bot.config.ssl.client_cert))
      ssl_context.key  = OpenSSL::PKey::RSA.new(File.read(@bot.config.ssl.client_cert))
    end

    ssl_context.ca_path     = @bot.config.ssl.ca_path
    ssl_context.verify_mode = @bot.config.ssl.verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
  else
    ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
  end
  @bot.loggers.info "Using SSL with #{@bot.config.server}:#{@bot.config.port}"

  @socket      = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
  @socket.sync = true
  @socket.connect
end
socket() click to toggle source

@return [TCPSocket] @api private @since 2.0.0

# File lib/cinch/irc.rb, line 29
def socket
  @socket.io
end
start() click to toggle source

Establish a connection.

@return [void] @since 2.0.0

# File lib/cinch/irc.rb, line 202
def start
  setup
  if connect
    @sasl_remaining_methods = [SASL::Plain, SASL::DH_Blowfish]
    send_cap_ls
    send_login

    reading_thread = start_reading_thread
    sending_thread = start_sending_thread
    ping_thread    = start_ping_thread

    reading_thread.join
    sending_thread.kill
    ping_thread.kill
  end
end
start_ping_thread() click to toggle source

@api private @return [Thread] The ping thread. @since 2.0.0

# File lib/cinch/irc.rb, line 177
def start_ping_thread
  Thread.new do
    while true
      sleep @bot.config.ping_interval
      # PING requires a single argument. In our case the value
      # doesn't matter though.
      send("PING 0")
    end
  end
end
start_reading_thread() click to toggle source

@api private @return [Thread] the reading thread @since 2.0.0

# File lib/cinch/irc.rb, line 137
def start_reading_thread
  Thread.new do
    begin
      while line = @socket.readline
        rescue_exception do
          line = Cinch::Utilities::Encoding.encode_incoming(line, @bot.config.encoding)
          parse line
        end
      end
    rescue Timeout::Error
      @bot.loggers.warn "Connection timed out."
    rescue EOFError
      @bot.loggers.warn "Lost connection."
    rescue => e
      @bot.loggers.exception(e)
    end

    @socket.close
    @bot.handlers.dispatch(:disconnect)
    # FIXME won't we kill all :disconnect handlers here? prolly
    # not, as they have 10 seconds to finish. that should be
    # plenty of time
    @bot.handlers.stop_all
  end
end
start_sending_thread() click to toggle source

@api private @return [Thread] the sending thread @since 2.0.0

# File lib/cinch/irc.rb, line 166
def start_sending_thread
  Thread.new do
    rescue_exception do
      @queue.process!
    end
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.