118 lines
3.8 KiB
Ruby
118 lines
3.8 KiB
Ruby
require 'ffi'
|
|
|
|
module LibMagic
|
|
extend FFI::Library
|
|
ffi_lib 'magic'
|
|
|
|
## constants
|
|
MAGIC_NONE = 0x0
|
|
MAGIC_DEBUG = 0x1
|
|
MAGIC_SYMLINK = 0x2
|
|
MAGIC_COMPRESS = 0x4
|
|
MAGIC_DEVICES = 0x8
|
|
MAGIC_MIME_TYPE = 0x10
|
|
MAGIC_CONTINUE = 0x20
|
|
MAGIC_CHECK = 0x40
|
|
MAGIC_PRESERVE_ATIME = 0x80
|
|
MAGIC_RAW = 0x100
|
|
MAGIC_ERROR = 0x200
|
|
MAGIC_MIME_ENCODING = 0x400
|
|
MAGIC_MIME = (MAGIC_MIME_TYPE | MAGIC_MIME_ENCODING)
|
|
MAGIC_APPLE = 0x800
|
|
MAGIC_EXTENSION = 0x1000000
|
|
MAGIC_COMPRESS_TRANSP = 0x2000000
|
|
|
|
# error used for handling libmagic errors
|
|
class MagicError < StandardError
|
|
def initialize(msg="Unknown error occurred", errno=0)
|
|
@errno = errno
|
|
super(msg)
|
|
end
|
|
end
|
|
|
|
class Magic
|
|
# initialize libmagic
|
|
def initialize(db_path = nil, flags = LibMagic::MAGIC_MIME_TYPE)
|
|
# nullptr for default db path
|
|
# dbptr = FFI::Pointer.new 0x0
|
|
dbptr = nil
|
|
@magic_inst = LibMagic::ffi_magic_open(flags)
|
|
if @magic_inst.null?
|
|
raise "magic_open() failed"
|
|
end
|
|
|
|
# now that we have our cookie, clear it on exit
|
|
at_exit do
|
|
self.close
|
|
end
|
|
|
|
# load magic database
|
|
if !db_path.nil?
|
|
dbptr = db_path
|
|
end
|
|
|
|
res = LibMagic::ffi_magic_load(@magic_inst, dbptr)
|
|
if res != 0
|
|
self.throw_error
|
|
end
|
|
end
|
|
|
|
# Gets MIME type for a given stream
|
|
def get_mime_type(stream)
|
|
res = nil
|
|
if stream.fileno.nil? # magic_buffer
|
|
buf = stream.string
|
|
buflen = buf.bytesize
|
|
res, ptr = LibMagic::ffi_magic_buffer(@magic_inst, buf, buflen)
|
|
else # magic_descriptor
|
|
res, ptr = LibMagic::ffi_magic_descriptor(@magic_inst, stream.fileno)
|
|
end
|
|
if res.nil? || res.empty?
|
|
self.throw_error
|
|
end
|
|
|
|
return res
|
|
end
|
|
|
|
def close()
|
|
unless @magic_inst.nil?
|
|
LibMagic::ffi_magic_close(@magic_inst)
|
|
@magic_inst = nil
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
# convenience method for throwing MagicErrors
|
|
def throw_error()
|
|
if @magic_inst.null?
|
|
raise LibMagic::MagicError.new
|
|
else
|
|
errptr = LibMagic::ffi_magic_error(@magic_inst)
|
|
errno = LibMagic::ffi_magic_errno(@magic_inst)
|
|
errstr, errfree = FFI::StrPtrConverter.from_native(errptr, nil)
|
|
raise LibMagic::MagicError.new(errstr, errno)
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
## ffi
|
|
|
|
# i/o open/close
|
|
attach_function :ffi_magic_open, :magic_open, [:int], :pointer
|
|
attach_function :ffi_magic_close, :magic_close, [:pointer], :void
|
|
# error info (a la GetLastError/win32)
|
|
attach_function :ffi_magic_error, :magic_error, [:pointer], :strptr
|
|
attach_function :ffi_magic_errno, :magic_errno, [:pointer], :int
|
|
# libmagic flags
|
|
attach_function :ffi_magic_getflags, :magic_getflags, [:pointer], :int
|
|
attach_function :ffi_magic_setflags, :magic_setflags, [:pointer, :int], :int
|
|
# load the libmagic db -- MUST BE DONE BEFORE GETTING MAGIC INFO
|
|
attach_function :ffi_magic_load, :magic_load, [:pointer, :string], :int
|
|
# get magic info
|
|
attach_function :ffi_magic_file, :magic_file, [:pointer, :string], :strptr
|
|
attach_function :ffi_magic_buffer, :magic_buffer, [:pointer, :pointer, :size_t], :strptr
|
|
attach_function :ffi_magic_descriptor, :magic_descriptor, [:pointer, :int], :strptr
|
|
end
|