module Net
  module NTLM
    class Message

      # @private false
      class Type2 < Message

        string        :sign,         {:size => 8, :value => SSP_SIGN}
        int32LE       :type,      {:value => 2}
        security_buffer   :target_name,  {:size => 0, :value => ""}
        int32LE       :flag,         {:value => DEFAULT_FLAGS[:TYPE2]}
        int64LE           :challenge,    {:value => 0}
        int64LE           :context,      {:value => 0, :active => false}
        security_buffer   :target_info,  {:value => "", :active => false}
        string        :padding,   {:size => 0, :value => "", :active => false }

        class << Type2
          # Parse a Type 2 packet
          # @param [String] str A string containing Type 2 data
          # @return [Type2]
          def parse(str)
            t = new
            t.parse(str)
            t
          end
        end

        # @!visibility private
        def parse(str)
          super(str)
          if has_flag?(:TARGET_INFO)
            enable(:context)
            enable(:target_info)
            super(str)
          end
          if ( (len = data_edge - head_size) > 0)
            self.padding = "\0" * len
            super(str)
          end
        end

        # Generates a Type 3 response based on the Type 2 Information
        # @return [Type3]
        # @option arg [String] :username The username to authenticate with
        # @option arg [String] :password The user's password
        # @option arg [String] :domain ('') The domain to authenticate to
        # @option opt [String] :workstation (Socket.gethostname) The name of the calling workstation
        # @option opt [Boolean] :use_default_target (False) Use the domain supplied by the server in the Type 2 packet
        # @note An empty :domain option authenticates to the local machine.
        # @note The :use_default_target has presidence over the :domain option
        def response(arg, opt = {})
          usr = arg[:user]
          pwd = arg[:password]
          domain = arg[:domain] ? arg[:domain].upcase : ""
          if usr.nil? or pwd.nil?
            raise ArgumentError, "user and password have to be supplied"
          end

          if opt[:workstation]
            ws = opt[:workstation]
          else
            ws = Socket.gethostname
          end

          if opt[:client_challenge]
            cc  = opt[:client_challenge]
          else
            cc = rand(MAX64)
          end
          cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
          opt[:client_challenge] = cc

          if has_flag?(:OEM) and opt[:unicode]
            usr = NTLM::EncodeUtil.decode_utf16le(usr)
            pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
            ws  = NTLM::EncodeUtil.decode_utf16le(ws)
            domain = NTLM::EncodeUtil.decode_utf16le(domain)
            opt[:unicode] = false
          end

          if has_flag?(:UNICODE) and !opt[:unicode]
            usr = NTLM::EncodeUtil.encode_utf16le(usr)
            pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
            ws  = NTLM::EncodeUtil.encode_utf16le(ws)
            domain = NTLM::EncodeUtil.encode_utf16le(domain)
            opt[:unicode] = true
          end

          if opt[:use_default_target]
            domain = self.target_name
          end

          ti = self.target_info

          chal = self[:challenge].serialize

          if opt[:ntlmv2]
            ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
            lm_res = NTLM::lmv2_response(ar, opt)
            ntlm_res = NTLM::ntlmv2_response(ar, opt)
          elsif has_flag?(:NTLM2_KEY)
            ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
            lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
          else
            lm_res = NTLM::lm_response(pwd, chal)
            ntlm_res = NTLM::ntlm_response(pwd, chal)
          end

          Type3.create({
                           :lm_response => lm_res,
                           :ntlm_response => ntlm_res,
                           :domain => domain,
                           :user => usr,
                           :workstation => ws,
                           :flag => self.flag
                       })
        end
      end



    end
  end
end


