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) # nullptr for default db path dbptr = FFI::Pointer.new 0x0 @magic_inst = 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 is not nil dbptr = db_path end res = ffi_magic_load(@magic_inst, dbptr) if res != 0 self.throw_error end end # Gets MIME type for a given stream def self.get_mime_type(stream) res = nil if stream.fileno.nil? # magic_buffer buf = stream.string buflen = buf.bytesize res = ffi_magic_buffer(@magic_inst, buf, buflen) else # magic_descriptor res = ffi_magic_descriptor(@magic_inst, stream.fileno) end if res.nil? || res.null? self.throw_error else retstr, retfree = FFI::StrPtrConverter.from_native(res, nil) return retstr end # unreachable return nil end def self.close() unless @magic_inst.nil? ffi_magic_close(@magic_inst) @magic_inst = nil end end private # convenience method for throwing MagicErrors def self.throw_error() if @magic_inst.null? raise LibMagic::MagicError.new else errptr = ffi_magic_error(@magic_inst) errno = ffi_magic_errno(@magic_inst) errstr, errfree = FFI::StrPtrConverter.from_native(errptr, nil) raise LibMagic::MagicError.new(errstr, errno) 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 end