# File lib/openid/server.rb, line 490
      def self.from_message(message, op_endpoint)
        obj = self.allocate
        obj.message = message
        obj.op_endpoint = op_endpoint
        mode = message.get_arg(OPENID_NS, 'mode')
        if mode == "checkid_immediate"
          obj.immediate = true
          obj.mode = "checkid_immediate"
        else
          obj.immediate = false
          obj.mode = "checkid_setup"
        end

        obj.return_to = message.get_arg(OPENID_NS, 'return_to')
        if message.is_openid1 and !obj.return_to
          msg = sprintf("Missing required field 'return_to' from %s",
                        message)
          raise ProtocolError.new(message, msg)
        end

        obj.identity = message.get_arg(OPENID_NS, 'identity')
        obj.claimed_id = message.get_arg(OPENID_NS, 'claimed_id')
        if message.is_openid1()
          if !obj.identity
            s = "OpenID 1 message did not contain openid.identity"
            raise ProtocolError.new(message, s)
          end
        else
          if obj.identity and not obj.claimed_id
            s = ("OpenID 2.0 message contained openid.identity but not " +
                 "claimed_id")
            raise ProtocolError.new(message, s)
          elsif obj.claimed_id and not obj.identity
            s = ("OpenID 2.0 message contained openid.claimed_id but not " +
                 "identity")
            raise ProtocolError.new(message, s)
          end
        end

        # There's a case for making self.trust_root be a TrustRoot
        # here.  But if TrustRoot isn't currently part of the "public"
        # API, I'm not sure it's worth doing.
        if message.is_openid1
          trust_root_param = 'trust_root'
        else
          trust_root_param = 'realm'
        end
        trust_root = message.get_arg(OPENID_NS, trust_root_param)
        trust_root = obj.return_to if (trust_root.nil? || trust_root.empty?)
        obj.trust_root = trust_root

        if !message.is_openid1 and !obj.return_to and !obj.trust_root
          raise ProtocolError.new(message, "openid.realm required when " +
                                  "openid.return_to absent")
        end

        obj.assoc_handle = message.get_arg(OPENID_NS, 'assoc_handle')

        # Using TrustRoot.parse here is a bit misleading, as we're not
        # parsing return_to as a trust root at all.  However, valid
        # URLs are valid trust roots, so we can use this to get an
        # idea if it is a valid URL.  Not all trust roots are valid
        # return_to URLs, however (particularly ones with wildcards),
        # so this is still a little sketchy.
        if obj.return_to and \
          !TrustRoot::TrustRoot.parse(obj.return_to)
          raise MalformedReturnURL.new(message, obj.return_to)
        end

        # I first thought that checking to see if the return_to is
        # within the trust_root is premature here, a
        # logic-not-decoding thing.  But it was argued that this is
        # really part of data validation.  A request with an invalid
        # trust_root/return_to is broken regardless of application,
        # right?
        if !obj.trust_root_valid()
          raise UntrustedReturnURL.new(message, obj.return_to, obj.trust_root)
        end

        return obj
      end