# -*- coding: binary -*-

#
# This is just a container class basically, that acts like File::Struct
#
# You must supply an initialize method that somehow populates the stathash..
#

module Rex
module Post

###
#
# This class emulates the ruby FileStat class against a remote entity in a
# generic fashion.  Refer to the ruby documentation for expected behavior.
#
###
class FileStat

  #
  # Basic file types.
  #
  @@ftypes = [
    'fifo', 'characterSpecial', 'directory',
    'blockSpecial', 'file', 'link', 'socket'
  ]

  attr_accessor :stathash

  def initialize(buf='')
    self.stathash = {}
    update(buf) if (buf and not buf.empty?)
  end

  def dev
    self.stathash['st_dev']
  end
  def ino
    self.stathash['st_ino']
  end
  def mode
    self.stathash['st_mode']
  end
  def nlink
    self.stathash['st_nlink']
  end
  def uid
    self.stathash['st_uid']
  end
  def gid
    self.stathash['st_gid']
  end
  def rdev
    self.stathash['st_rdev']
  end
  def size
    self.stathash['st_size']
  end
  def blksize
    self.stathash['st_blksize']
  end
  def blocks
    self.stathash['st_blocks']
  end
  def atime
    ::Time.at(self.stathash['st_atime'])
  end
  def mtime
    ::Time.at(self.stathash['st_mtime'])
  end
  def ctime
    ::Time.at(self.stathash['st_ctime'])
  end

  def update(buf)
    skeys = %W{st_dev st_mode st_nlink st_uid st_gid st_rdev st_ino st_size st_atime st_mtime st_ctime}
    svals = buf.unpack("VVVVVVQQQQQ")
    skeys.each_index do |i|
      self.stathash[ skeys[i] ] = svals[i]
    end
  end

  #
  # This handles the old 32bit st_size buf from old stageless meterpreters for backwards compatibility
  # Maybe we can remove this in the future
  #
  def update32(buf)
    skeys = %W{st_dev st_ino st_mode st_pad st_nlink st_uid st_gid st_rdev st_size st_ctime st_atime st_mtime}
    svals = buf.unpack("VvvvvvvVVVVV")
    skeys.each_index do |i|
      self.stathash[ skeys[i] ] = svals[i]
    end
  end

  #
  # S_IFMT     0170000   bitmask for the file type bitfields
  # S_IFSOCK   0140000   socket
  # S_IFLNK    0120000   symbolic link
  # S_IFREG    0100000   regular file
  # S_IFBLK    0060000   block device
  # S_IFDIR    0040000   directory
  # S_IFCHR    0020000   character device
  # S_IFIFO    0010000   fifo
  #

  # this is my own, just a helper...
  def filetype?(mask)
    return true if mode & 0170000 == mask
    return false
  end

  def blockdev?
    filetype?(060000)
  end
  def chardev?
    filetype?(020000)
  end
  def directory?
    filetype?(040000)
  end
  def file?
    filetype?(0100000)
  end
  def pipe?
    filetype?(010000) # ??? fifo?
  end
  def socket?
    filetype?(0140000)
  end
  def symlink?
    filetype?(0120000)
  end

  def ftype
    return @@ftypes[(mode & 0170000) >> 13].dup
  end

  #
  # S_ISUID    0004000   set UID bit
  # S_ISGID    0002000   set GID bit (see below)
  # S_ISVTX    0001000   sticky bit (see below)
  # S_IRWXU    00700     mask for file owner permissions
  # S_IRUSR    00400     owner has read permission
  # S_IWUSR    00200     owner has write permission
  # S_IXUSR    00100     owner has execute permission
  # S_IRWXG    00070     mask for group permissions
  # S_IRGRP    00040     group has read permission
  # S_IWGRP    00020     group has write permission
  # S_IXGRP    00010     group has execute permission
  # S_IRWXO    00007     mask for permissions for others (not in group)
  # S_IROTH    00004     others have read permission
  # S_IWOTH    00002     others have write permisson
  # S_IXOTH    00001     others have execute permission
  #

  def perm?(mask)
    return true if mode & mask == mask
    return false
  end

  def setgid?
    perm?(02000)
  end
  def setuid?
    perm?(04000)
  end
  def sticky?
    perm?(01000)
  end

  def executable?
    raise NotImplementedError
  end
  def executable_real?
    raise NotImplementedError
  end
  def grpowned?
    raise NotImplementedError
  end
  def owned?
    raise NotImplementedError
  end
  def readable?
    raise NotImplementedError
  end
  def readable_real?
    raise NotImplementedError
  end
  def writeable?
    raise NotImplementedError
  end
  def writeable_real?
    raise NotImplementedError
  end

  #
  # Return pretty information about a file's permissions.
  #
  def prettymode
    m  = mode
    om = '%04o' % m
    perms = ''

    3.times {
      perms = ((m & 01) == 01 ? 'x' : '-') + perms
      perms = ((m & 02) == 02 ? 'w' : '-') + perms
      perms = ((m & 04) == 04 ? 'r' : '-') + perms
      m >>= 3
    }

    return "#{om}/#{perms}"
  end

  #
  # Return pretty information about a file.
  #
  def pretty
    "  Size: #{size}   Blocks: #{blocks}   IO Block: #{blksize}   Type: #{rdev}\n"\
    "Device: #{dev}  Inode: #{ino}  Links: #{nlink}\n"\
    "  Mode: #{prettymode}\n"\
    "   Uid: #{uid}  Gid: #{gid}\n"\
    "Access: #{atime}\n"\
    "Modify: #{mtime}\n"\
    "Change: #{ctime}\n"
  end

end
end; end # Post/Rex
