# -*- coding: binary -*-

require 'rex/post/meterpreter/extensions/python/tlv'
require 'rex/post/meterpreter/extensions/python/command_ids'
require 'set'

module Rex
module Post
module Meterpreter
module Extensions
module Python

###
#
# Python extension - gives remote python scripting capabilities on the target.
#
###

class Python < Extension

  PY_CODE_TYPE_STRING = 0
  PY_CODE_TYPE_PY     = 1
  PY_CODE_TYPE_PYC    = 2

  PY_CODE_FILE_TYPES = [ '.py', '.pyc' ]

  PY_CODE_FILE_TYPE_MAP = {
    '.py'  => PY_CODE_TYPE_PY,
    '.pyc' => PY_CODE_TYPE_PYC
  }

  def self.extension_id
    EXTENSION_ID_PYTHON
  end

  #
  # Typical extension initialization routine.
  #
  # @param client (see Extension#initialize)
  def initialize(client)
    super(client, 'python')

    client.register_extension_aliases(
      [
        {
          'name' => 'python',
          'ext'  => self
        }
      ])
  end

  def reset
    request = Packet.create_request(COMMAND_ID_PYTHON_RESET)
    client.send_request(request)

    return true
  end

  def import(file, mod_name, result_var)
    unless ::File.file?(file)
      raise ArgumentError, "File not found: #{file}"
    end

    ext = ::File.extname(file).downcase
    unless PY_CODE_FILE_TYPES.include?(ext)
      raise ArgumentError, "File not a valid type: #{file}"
    end

    code = ::File.read(file)

    request = Packet.create_request(COMMAND_ID_PYTHON_EXECUTE)
    request.add_tlv(TLV_TYPE_PYTHON_CODE, code)
    request.add_tlv(TLV_TYPE_PYTHON_CODE_LEN, code.length)
    request.add_tlv(TLV_TYPE_PYTHON_CODE_TYPE, PY_CODE_FILE_TYPE_MAP[ext])
    request.add_tlv(TLV_TYPE_PYTHON_NAME, mod_name) if mod_name
    request.add_tlv(TLV_TYPE_PYTHON_RESULT_VAR, result_var) if result_var

    run_exec_request(request)
  end

  #
  # Dump the LSA secrets from the target machine.
  #
  # @return [Hash<Symbol,Object>]
  def execute_string(code, result_var)
    request = Packet.create_request(COMMAND_ID_PYTHON_EXECUTE)
    request.add_tlv(TLV_TYPE_PYTHON_CODE, code)
    request.add_tlv(TLV_TYPE_PYTHON_CODE_TYPE, PY_CODE_TYPE_STRING)
    request.add_tlv(TLV_TYPE_PYTHON_RESULT_VAR, result_var) if result_var

    run_exec_request(request)
  end

private

  def run_exec_request(request)
    response = client.send_request(request)

    result = {
      result: response.get_tlv_value(TLV_TYPE_PYTHON_RESULT),
      stdout: "",
      stderr: ""
    }

    response.each(TLV_TYPE_PYTHON_STDOUT) do |o|
      result[:stdout] << o.value
    end

    response.each(TLV_TYPE_PYTHON_STDERR) do |e|
      result[:stderr] << e.value
    end

    result
  end

end

end; end; end; end; end

