#!/usr/bin/env ruby

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

		# XXX: This needs to understand more than just 'stat' structures
		# Windows can also return _stat32, _stat32i64, _stat64i32, and _stat64 structures
		
		skeys = %W{st_dev st_ino st_mode st_wtf 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
