# File lib/rbot/rfc2812.rb, line 1025
    def process(serverstring)
      data = Hash.new
      data[:serverstring] = serverstring

      unless serverstring.chomp =~ /^(:(\S+)\s)?(\S+)(\s(.*))?$/
        raise "Unparseable Server Message!!!: #{serverstring.inspect}"
      end

      prefix, command, params = $2, $3, $5

      if prefix != nil
        # Most servers will send a full nick!user@host prefix for
        # messages from users. Therefore, when the prefix doesn't match this
        # syntax it's usually the server hostname.
        #
        # This is not always true, though, since some servers do not send a
        # full hostmask for user messages.
        #
        if prefix =~ /^#{Regexp::Irc::BANG_AT}$/
          data[:source] = @server.user(prefix)
        else
          if @server.hostname
            if @server.hostname != 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, prefix)
            data[:source] = @server
          end
        end
      end

      # split parameters in an array
      argv = []
      params.scan(/(?!:)(\S+)|:(.*)/) { argv << ($1 || $2) } if params

      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
          data[:message] = argv[1]
          # "Welcome to the Internet Relay Network
          # <nick>!<user>@<host>"
          if not_us
            warning "Server thinks client (#{@user.inspect}) has a different nick"
            @user.nick = data[:target]
          end
          if data[:message] =~ /([^@!\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
          handle(:welcome, data)
        when RPL_YOURHOST
          # "Your host is <servername>, running version <ver>"
          data[:message] = argv[1]
          handle(:yourhost, data)
        when RPL_CREATED
          # "This server was created <date>"
          data[:message] = argv[1]
          handle(:created, data)
        when RPL_MYINFO
          # "<servername> <version> <available user modes>
          # <available channel modes>"
          @server.parse_my_info(params.split(' ', 2).last)
          data[:servername] = @server.hostname
          data[:version] = @server.version
          data[:usermodes] = @server.usermodes
          data[:chanmodes] = @server.chanmodes
          handle(:myinfo, data)
        when RPL_ISUPPORT
          # "PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
          # "MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10 HOSTLEN=63
          # TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23 CHANTYPES=#
          # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available
          # on this server"
          #
          @server.parse_isupport(argv[1..-2].join(' '))
          handle(:isupport, data)
        when ERR_NICKNAMEINUSE
          # "* <nick> :Nickname is already in use"
          data[:nick] = argv[1]
          data[:message] = argv[2]
          handle(:nicktaken, data)
        when ERR_ERRONEUSNICKNAME
          # "* <nick> :Erroneous nickname"
          data[:nick] = argv[1]
          data[:message] = argv[2]
          handle(:badnick, data)
        when RPL_TOPIC
          data[:channel] = @server.channel(argv[1])
          data[:topic] = argv[2]
          data[:channel].topic.text = data[:topic]

          handle(:topic, data)
        when RPL_TOPIC_INFO
          data[:nick] = @server.user(argv[0])
          data[:channel] = @server.channel(argv[1])

          # 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)

          data[:channel].topic.set_by = data[:source]
          data[:channel].topic.set_on = data[:time]

          handle(:topicinfo, data)
        when RPL_NAMREPLY
          # "( "=" / "*" / "@" ) <channel>
          # :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
          # - "@" is used for secret channels, "*" for private
          # channels, and "=" for others (public channels).
          data[:channeltype] = argv[1]
          data[:channel] = chan = @server.channel(argv[2])

          users = []
          argv[3].scan(/\S+/).each { |u|
            # FIXME beware of servers that allow multiple prefixes
            if(u =~ /^([#{@server.supports[:prefix][:prefixes].join}])?(.*)$/)
              umode = $1
              user = $2
              users << [user, umode]
            end
          }

          users.each { |ar|
            u = @server.user(ar[0])
            chan.add_user(u, :silent => true)
            debug "Adding user #{u}"
            if ar[1]
              ms = @server.mode_for_prefix(ar[1].to_sym)
              debug "\twith mode #{ar[1]} (#{ms})"
              chan.mode[ms].set(u)
            end
          }
          @tmpusers += users
        when RPL_ENDOFNAMES
          data[:channel] = @server.channel(argv[1])
          data[:users] = @tmpusers
          handle(:names, data)
          @tmpusers = Array.new
        when RPL_LUSERCLIENT
          # ":There are <integer> users and <integer>
          # services on <integer> servers"
          data[:message] = argv[1]
          handle(:luserclient, data)
        when RPL_LUSEROP
          # "<integer> :operator(s) online"
          data[:ops] = argv[1].to_i
          handle(:luserop, data)
        when RPL_LUSERUNKNOWN
          # "<integer> :unknown connection(s)"
          data[:unknown] = argv[1].to_i
          handle(:luserunknown, data)
        when RPL_LUSERCHANNELS
          # "<integer> :channels formed"
          data[:channels] = argv[1].to_i
          handle(:luserchannels, data)
        when RPL_LUSERME
          # ":I have <integer> clients and <integer> servers"
          data[:message] = argv[1]
          handle(:luserme, data)
        when ERR_NOMOTD
          # ":MOTD File is missing"
          data[:message] = argv[1]
          handle(:motd_missing, data)
        when RPL_LOCALUSERS
          # ":Current local  users: 3  Max: 4"
          data[:message] = argv[1]
          handle(:localusers, data)
        when RPL_GLOBALUSERS
          # ":Current global users: 3  Max: 4"
          data[:message] = argv[1]
          handle(:globalusers, data)
        when RPL_STATSCONN
          # ":Highest connection count: 4 (4 clients) (251 since server was
          # (re)started)"
          data[:message] = argv[1]
          handle(:statsconn, data)
        when RPL_MOTDSTART
          # "<nick> :- <server> Message of the Day -"
          if argv[1] =~ /^-\s+(\S+)\s/
            server = $1
          else
            warning "Server doesn't have an RFC compliant MOTD start."
          end
          @motd = ""
        when RPL_MOTD
          if(argv[1] =~ /^-\s+(.*)$/)
            @motd << $1
            @motd << "\n"
          end
        when RPL_ENDOFMOTD
          data[:motd] = @motd
          handle(:motd, data)
        when RPL_DATASTR
          data[:text] = argv[1]
          handle(:datastr, data)
        when RPL_AWAY
          data[:nick] = user = @server.user(argv[1])
          data[:message] = argv[-1]
          user.away = data[:message]
          handle(:away, data)
        when RPL_WHOREPLY
          data[:channel] = channel = @server.channel(argv[1])
          data[:user] = argv[2]
          data[:host] = argv[3]
          data[:userserver] = argv[4]
          data[:nick] = user = @server.user(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.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.add_user(user, :silent=>true)
          data[:modes].map { |mode|
            channel.mode[mode].set(user)
          }

          handle(:who, data)
        when RPL_ENDOFWHO
          handle(:eowho, data)
        when RPL_WHOISUSER
          @whois ||= Hash.new
          @whois[:nick] = argv[1]
          @whois[:user] = argv[2]
          @whois[:host] = argv[3]
          @whois[:real_name] = argv[-1]

          user = @server.user(@whois[:nick])
          user.user = @whois[:user]
          user.host = @whois[:host]
          user.real_name = @whois[:real_name]
        when RPL_WHOISSERVER
          @whois ||= Hash.new
          @whois[:nick] = argv[1]
          @whois[:server] = argv[2]
          @whois[:server_info] = argv[-1]
          # TODO update user info
        when RPL_WHOISOPERATOR
          @whois ||= Hash.new
          @whois[:nick] = argv[1]
          @whois[:operator] = argv[-1]
          # TODO update user info
        when RPL_WHOISIDLE
          @whois ||= Hash.new
          @whois[:nick] = argv[1]
          user = @server.user(@whois[:nick])
          @whois[:idle] = argv[2].to_i
          user.idle_since = Time.now - @whois[:idle]
          if argv[-1] == 'seconds idle, signon time'
            @whois[:signon] = Time.at(argv[3].to_i)
            user.signon = @whois[:signon]
          end
        when RPL_ENDOFWHOIS
          @whois ||= Hash.new
          @whois[:nick] = argv[1]
          data[:whois] = @whois.dup
          @whois.clear
          handle(:whois, data)
        when RPL_WHOISCHANNELS
          @whois ||= Hash.new
          @whois[:nick] = argv[1]
          @whois[:channels] = []
          user = @server.user(@whois[:nick])
          argv[-1].split.each do |prechan|
            pfx = prechan.scan(/[#{@server.supports[:prefix][:prefixes].join}]/)
            modes = pfx.map { |p| @server.mode_for_prefix p }
            chan = prechan[pfx.length..prechan.length]

            channel = @server.channel(chan)
            channel.add_user(user, :silent => true)
            modes.map { |mode| channel.mode[mode].set(user) }

            @whois[:channels] << [chan, modes]
          end
        when RPL_CHANNELMODEIS
          parse_mode(serverstring, argv[1..-1], data)
          handle(:mode, data)
        when RPL_CREATIONTIME
          data[:channel] = @server.channel(argv[1])
          data[:time] = Time.at(argv[2].to_i)
          data[:channel].creation_time=data[:time]
          handle(:creationtime, data)
        when RPL_CHANNEL_URL
          data[:channel] = @server.channel(argv[1])
          data[:url] = argv[2]
          data[:channel].url=data[:url].dup
          handle(:channel_url, data)
        when ERR_NOSUCHNICK
          data[:nick] = argv[1]
          if user = @server.get_user(data[:nick])
            @server.delete_user(user)
          end
          handle(:nosuchnick, data)
        when ERR_NOSUCHCHANNEL
          data[:channel] = argv[1]
          if channel = @server.get_channel(data[:channel])
            @server.delete_channel(channel)
          end
          handle(:nosuchchannel, data)
        else
          warning "Unknown message #{serverstring.inspect}"
          handle(:unknown, data)
        end
        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.

        begin
          data[:target] = @server.user_or_channel(argv[0])
        rescue
          # The previous may fail e.g. when the target is a server or something
          # like that (e.g. $<mask>). In any of these cases, we just use the
          # String as a target
          # FIXME we probably want to explicitly check for the #<mask> $<mask>
          data[:target] = argv[0]
        end
        data[:message] = argv[1]
        handle(:privmsg, data)

        # Now we split it
        if data[:target].kind_of?(Channel)
          handle(:public, data)
        else
          handle(:msg, data)
        end
      when :NOTICE
        begin
          data[:target] = @server.user_or_channel(argv[0])
        rescue
          # The previous may fail e.g. when the target is a server or something
          # like that (e.g. $<mask>). In any of these cases, we just use the
          # String as a target
          # FIXME we probably want to explicitly check for the #<mask> $<mask>
          data[:target] = argv[0]
        end
        data[:message] = argv[1]
        case data[:source]
        when User
          handle(:notice, data)
        else
          # "server notice" (not from user, noone to reply to)
          handle(:snotice, data)
        end
      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] == @user
          @server.delete_channel(data[:channel])
        end

        handle(:kick, data)
      when :PART
        data[:channel] = @server.channel(argv[0])
        data[:message] = argv[1]

        @server.delete_user_from_channel(data[:source], data[:channel])
        if data[:source] == @user
          @server.delete_channel(data[:channel])
        end

        handle(:part, data)
      when :QUIT
        data[:message] = argv[0]
        data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch|
          list << ch if ch.has_user?(data[:source])
          list
        }

        @server.delete_user(data[:source])

        handle(:quit, data)
      when :JOIN
        data[:channel] = @server.channel(argv[0])
        data[:channel].add_user(data[:source])

        handle(:join, data)
      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
        data[:target] = @server.user(argv[0])
        data[:channel] = @server.channel(argv[1])

        handle(:invite, data)
      when :NICK
        data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch|
          list << ch if ch.has_user?(data[:source])
          list
        }

        data[:newnick] = argv[0]
        data[:oldnick] = data[:source].nick.dup
        data[:source].nick = data[:newnick]

        debug "#{data[:oldnick]} (now #{data[:newnick]}) was on #{data[:is_on].join(', ')}"

        handle(:nick, data)
      when :MODE
        parse_mode(serverstring, argv, data)
        handle(:mode, data)
      else
        warning "Unknown message #{serverstring.inspect}"
        handle(:unknown, data)
      end
    end