X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=lib%2Frbot%2Frfc2812.rb;h=2d4d323ba439db270d6fb188c3f62f6996ccfe7a;hb=c4502412f30f69c1ffa053b160e01d3974b338aa;hp=9ca6571a85b374e1f9723b0f3cd590344ab7ff71;hpb=aeff57ecf846b714e08d0095f1aea28a74e47d0b;p=user%2Fhenk%2Fcode%2Fruby%2Frbot.git diff --git a/lib/rbot/rfc2812.rb b/lib/rbot/rfc2812.rb index 9ca6571a..2d4d323b 100644 --- a/lib/rbot/rfc2812.rb +++ b/lib/rbot/rfc2812.rb @@ -812,16 +812,16 @@ module Irc RPL_DATASTR=290 # implements RFC 2812 and prior IRC RFCs. - # clients register handler proc{}s for different server events and IrcClient + # clients register handler proc{}s for different server events and Client # handles dispatch - class IrcClient + class Client - attr_reader :server, :client + attr_reader :server, :user - # create a new IrcClient instance + # create a new Client instance def initialize @server = Server.new # The Server - @client = @server.user("") # The User representing the client on this Server + @user = @server.user("") # The User representing the client on this Server @handlers = Hash.new @@ -830,12 +830,21 @@ module Irc @tmpusers = [] end + # clear the server and reset the User + def reset + @server.clear + @user = @server.user("") + end + # key:: server event to handle # value:: proc object called when event occurs # set a handler for a server event # # ==server events currently supported: # + # TODO handle errors ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL + # TODO handle errors ERR_CHANOPRIVSNEEDED, ERR_CANNOTSENDTOCHAN + # # welcome:: server welcome message on connect # yourhost:: your host details (on connection) # created:: when the server was started @@ -879,8 +888,8 @@ module Irc data = Hash.new data[:serverstring] = serverstring - unless serverstring =~ /^(:(\S+)\s)?(\S+)(\s(.*))?/ - raise "Unparseable Server Message!!!: #{serverstring}" + unless serverstring.chomp =~ /^(:(\S+)\s)?(\S+)(\s(.*))?$/ + raise "Unparseable Server Message!!!: #{serverstring.inspect}" end prefix, command, params = $2, $3, $5 @@ -893,18 +902,19 @@ module Irc # This is not always true, though, since some servers do not send a # full hostmask for user messages. # - if prefix =~ /^(?:\S+)(?:!\S+)?@(?:\S+)$/ + if prefix =~ /^#{Regexp::Irc::BANG_AT}$/ data[:source] = @server.user(prefix) else if @server.hostname if @server.hostname != prefix - debug "Origin #{prefix} for message\n\t#{serverstring.inspect}\nis neither a user hostmask nor the server hostname, assuming it's a nick" - data[:source] = @server.user(prefix) + # TODO do we want to be able to differentiate messages that are passed on to us from /other/ servers? + debug "Origin #{prefix} for message\n\t#{serverstring.inspect}\nis neither a user hostmask nor the server hostname\nI'll pretend that it's from the server anyway" + data[:source] = @server else data[:source] = @server end else - @server.instance_variable_set(:@hostname, data[:source]) + @server.instance_variable_set(:@hostname, prefix) data[:source] = @server end end @@ -914,37 +924,36 @@ module Irc argv = [] params.scan(/(?!:)(\S+)|:(.*)/) { argv << ($1 || $2) } if params - case command - when 'PING' - data[:pingid] = argv[0] - handle(:ping, data) - when 'PONG' - data[:pingid] = argv[0] - handle(:pong, data) - when /^(\d+)$/ # numerical server message + if command =~ /^(\d+)$/ # Numeric replies + data[:target] = argv[0] + # A numeric reply /should/ be directed at the client, except when we're connecting with a used nick, in which case + # it's directed at '*' + not_us = !([@user.nick, '*'].include?(data[:target])) + if not_us + warning "Server reply #{serverstring.inspect} directed at #{data[:target]} instead of client (#{@user.nick})" + end + num=command.to_i case num when RPL_WELCOME # "Welcome to the Internet Relay Network # !@" - case argv[1] - when /((\S+)!(\S+))/ - data[:netmask] = $1 - data[:nick] = $2 - data[:address] = $3 - @client = @server.user(data[:netmask]) - set = true - when /Welcome to the Internet Relay Network\s(\S+)/ - data[:nick] = $1 - when /Welcome.*\s+(\S+)$/ - data[:nick] = $1 - when /^(\S+)$/ - data[:nick] = $1 + if not_us + warning "Server thinks client (#{@user.inspect}) has a different nick" + @user.nick = data[:target] + end + if argv[1] =~ /([^@!\s]+)(?:!([^@!\s]+?))?@(\S+)/ + nick = $1 + user = $2 + host = $3 + warning "Welcome message nick mismatch (#{nick} vs #{data[:target]})" if nick != data[:target] + @user.user = user if user + @user.host = host if host end - @client = @server.user(data[:nick]) unless set handle(:welcome, data) when RPL_YOURHOST # "Your host is , running version " + data[:message] = argv[1] handle(:yourhost, data) when RPL_CREATED # "This server was created " @@ -992,11 +1001,15 @@ module Irc when RPL_TOPIC_INFO data[:nick] = @server.user(argv[0]) data[:channel] = @server.get_channel(argv[1]) - data[:source] = @server.user(argv[2]) + + # This must not be an IRC::User because it might not be an actual User, + # and we risk overwriting valid User data + data[:source] = argv[2].to_irc_netmask(:server => @server) + data[:time] = Time.at(argv[3].to_i) if data[:channel] - data[:channel].topic.set_by = data[:nick] + data[:channel].topic.set_by = data[:source] data[:channel].topic.set_on = data[:time] else warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on" @@ -1086,8 +1099,10 @@ module Irc # " :- Message of the Day -" if argv[1] =~ /^-\s+(\S+)\s/ server = $1 - @motd = "" + else + warning "Server doesn't have an RFC compliant MOTD start." end + @motd = "" when RPL_MOTD if(argv[1] =~ /^-\s+(.*)$/) @motd << $1 @@ -1099,11 +1114,59 @@ module Irc when RPL_DATASTR data[:text] = argv[1] handle(:datastr, data) + when RPL_WHOREPLY + data[:channel] = argv[1] + data[:user] = argv[2] + data[:host] = argv[3] + data[:userserver] = argv[4] + data[:nick] = argv[5] + if argv[6] =~ /^(H|G)(\*)?(.*)?$/ + data[:away] = ($1 == 'G') + data[:ircop] = $2 + data[:modes] = $3.scan(/./).map { |mode| + m = @server.supports[:prefix][:prefixes].index(mode.to_sym) + @server.supports[:prefix][:modes][m] + } rescue [] + else + warning "Strange WHO reply: #{serverstring.inspect}" + end + data[:hopcount], data[:real_name] = argv[7].split(" ", 2) + + user = @server.get_user(data[:nick]) + + user.user = data[:user] + user.host = data[:host] + user.away = data[:away] # FIXME doesn't provide the actual message + # TODO ircop status + # TODO userserver + # TODO hopcount + user.real_name = data[:real_name] + + channel = @server.get_channel(data[:channel]) + + channel.add_user(user, :silent=>true) + data[:modes].map { |mode| + channel.mode[mode].set(user) + } + + handle(:who, data) + when RPL_ENDOFWHO + handle(:eowho, data) else handle(:unknown, data) end - # end of numeric replies - when 'PRIVMSG' + return # We've processed the numeric reply + end + + # Otherwise, the command should be a single word + case command.to_sym + when :PING + data[:pingid] = argv[0] + handle(:ping, data) + when :PONG + data[:pingid] = argv[0] + handle(:pong, data) + when :PRIVMSG # you can either bind to 'PRIVMSG', to get every one and # parse it yourself, or you can bind to 'MSG', 'PUBLIC', # etc and get it all nicely split up for you. @@ -1126,7 +1189,7 @@ module Irc else handle(:msg, data) end - when 'NOTICE' + when :NOTICE begin data[:target] = @server.user_or_channel(argv[0]) rescue @@ -1144,28 +1207,28 @@ module Irc # "server notice" (not from user, noone to reply to) handle(:snotice, data) end - when 'KICK' + when :KICK data[:channel] = @server.channel(argv[0]) data[:target] = @server.user(argv[1]) data[:message] = argv[2] @server.delete_user_from_channel(data[:target], data[:channel]) - if data[:target] == @client + if data[:target] == @user @server.delete_channel(data[:channel]) end handle(:kick, data) - when 'PART' + when :PART data[:channel] = @server.channel(argv[0]) data[:message] = argv[1] @server.delete_user_from_channel(data[:source], data[:channel]) - if data[:source] == @client + if data[:source] == @user @server.delete_channel(data[:channel]) end handle(:part, data) - when 'QUIT' + when :QUIT data[:message] = argv[0] data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch| list << ch if ch.has_user?(data[:source]) @@ -1175,23 +1238,23 @@ module Irc @server.delete_user(data[:source]) handle(:quit, data) - when 'JOIN' + when :JOIN data[:channel] = @server.channel(argv[0]) data[:channel].add_user(data[:source]) handle(:join, data) - when 'TOPIC' + when :TOPIC data[:channel] = @server.channel(argv[0]) data[:topic] = Channel::Topic.new(argv[1], data[:source], Time.new) data[:channel].topic.replace(data[:topic]) handle(:changetopic, data) - when 'INVITE' + when :INVITE data[:target] = @server.user(argv[0]) data[:channel] = @server.channel(argv[1]) handle(:invite, data) - when 'NICK' + when :NICK data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch| list << ch if ch.has_user?(data[:source]) list @@ -1204,7 +1267,7 @@ module Irc debug "#{data[:oldnick]} (now #{data[:newnick]}) was on #{data[:is_on].join(', ')}" handle(:nick, data) - when 'MODE' + when :MODE # MODE ([+-] ()*)* # When a MODE message is received by a server, # Type C will have parameters too, so we must @@ -1216,7 +1279,7 @@ module Irc case data[:channel] when User # TODO - warn "Unhandled user mode message '#{serverstring}'" + warning "Unhandled user mode message '#{serverstring}'" else # data[:modes] is an array where each element # is either a flag which doesn't need parameters @@ -1252,13 +1315,13 @@ module Irc data[:modes] << [setting + m] who_wants_params << data[:modes].length - 1 else - warn "Unknown mode #{m} in #{serverstring.inspect}" + warning "Unknown mode #{m} in #{serverstring.inspect}" end } else idx = who_wants_params.shift if idx.nil? - warn "Oops, problems parsing #{serverstring.inspect}" + warning "Oops, problems parsing #{serverstring.inspect}" break end data[:modes][idx] << arg @@ -1282,7 +1345,7 @@ module Irc handle(:mode, data) else - warn "Unknown message #{serverstring.inspect}" + warning "Unknown message #{serverstring.inspect}" handle(:unknown, data) end end