Commit 6f61ff59 by Daniel Mendler

add matching of string and better handling of binary encoding

parent c84d4e23
require 'mimemagic/tables'
require 'mimemagic/version'
require 'stringio'
# Mime type detection
class MimeMagic
......@@ -77,11 +76,15 @@ class MimeMagic
# Lookup mime type by magic content analysis.
# This is a slow operation.
def self.by_magic(io)
if !(io.respond_to?(:seek) && io.respond_to?(:read))
str = io.respond_to?(:read) ? io.read : io.to_s
io = StringIO.new(str, 'rb:binary')
end
mime = MAGIC.find {|type, matches| magic_match(io, matches) }
mime =
unless io.respond_to?(:seek) && io.respond_to?(:read)
str = io.respond_to?(:read) ? io.read : io.to_s
str = str.b if str.respond_to? :b
MAGIC.find {|type, matches| magic_match_str(str, matches) }
else
io.binmode
MAGIC.find {|type, matches| magic_match_io(io, matches) }
end
mime && new(mime[0])
end
......@@ -101,31 +104,39 @@ class MimeMagic
alias == eql?
private
def self.child?(child, parent)
child == parent || TYPES.key?(child) && TYPES[child][1].any? {|p| child?(p, parent) }
end
def self.magic_match(io, matches)
def self.magic_match_io(io, matches)
matches.any? do |offset, value, children|
match =
if Range === offset
io.seek(offset.begin)
io.read(offset.end - offset.begin + value.length).include?(value)
else
io.seek(offset)
io.read(value.length) == value
end
match && (!children || magic_match_io(io, children))
end
rescue
false
end
def self.magic_match_str(str, matches)
matches.any? do |offset, value, children|
value = value.b if value.respond_to?(:b)
match = if Range === offset
io.seek(offset.begin)
io_val = io.read(offset.end - offset.begin + value.length)
io_val = io_val.b if io_val.respond_to?(:b)
io_val.include?(value)
else
io.seek(offset)
io_val = io.read(value.length)
io_val = io_val.b if io_val.respond_to?(:b)
value == io_val
end
match && (!children || magic_match(io, children))
match =
if Range === offset
str[offset.begin, offset.end - offset.begin + value.length].include?(value)
else
str[offset, value.length] == value
end
match && (!children || magic_match_str(str, children))
end
rescue
false
end
private_class_method :magic_match
private_class_method :magic_match_io, :magic_match_str
end
# -*- coding: binary -*-
# Generated from freedesktop.org.xml
class MimeMagic
private
# @private
# :nodoc:
EXTENSIONS = {
'123' => 'application/vnd.lotus-1-2-3',
'3ds' => 'image/x-3ds',
......@@ -808,6 +810,8 @@ class MimeMagic
'zip' => 'application/zip',
'zoo' => 'application/x-zoo',
}
# @private
# :nodoc:
TYPES = {
'application/andrew-inset' => [%w(ez), %w(), 'ATK inset'],
'application/annodex' => [%w(anx), %w(), 'Annodex exchange format'],
......@@ -1386,6 +1390,8 @@ class MimeMagic
'video/x-theora+ogg' => [%w(ogg ogv), %w(video/ogg), 'Ogg Theora video'],
'x-epoc/x-sisx-app' => [%w(sisx), %w(), 'SISX package'],
}
# @private
# :nodoc:
MAGIC = [
['application/annodex', [[0, 'OggS', [[28, "fishead\000", [[56..512, "CMML\000\000\000\000"]]]]]]],
['application/atom+xml', [[0..256, '<feed ']]],
......
......@@ -90,14 +90,18 @@ end
magics = magics.sort {|a,b| b[0] <=> a[0] }.map {|x| [x[1], x[2]] }
puts "# -*- coding: binary -*-"
puts "# Generated from #{FILE}"
puts "class MimeMagic"
puts " private"
puts " # @private"
puts " # :nodoc:"
puts " EXTENSIONS = {"
extensions.keys.sort.each do |key|
puts " '#{key}' => '#{extensions[key]}',"
end
puts " }"
puts " # @private"
puts " # :nodoc:"
puts " TYPES = {"
types.keys.sort.each do |key|
exts = types[key][0].sort.join(' ')
......@@ -106,6 +110,8 @@ types.keys.sort.each do |key|
puts " '#{key}' => [%w(#{exts}), %w(#{parents}), #{comment}],"
end
puts " }"
puts " # @private"
puts " # :nodoc:"
puts " MAGIC = ["
magics.sort.each do |type, matches|
puts " ['#{type}', #{matches.inspect}],"
......
require 'bacon'
require 'mimemagic'
require 'stringio'
describe 'MimeMagic' do
it 'should have type, mediatype and subtype' do
......@@ -73,14 +74,30 @@ describe 'MimeMagic' do
MimeMagic.add('application/mimemagic-test',
:magic => [[0, 'MAGICTEST'], # MAGICTEST at position 0
[1, 'MAGICTEST'], # MAGICTEST at position 1
[9..12, 'MAGICTEST'], # MAGICTEST starting at position 9 to 12
[2, 'MAGICTEST', [[0, 'X'], [0, 'Y']]]]) # MAGICTEST at position 2 and (X at 0 or Y at 0)
MimeMagic.by_magic('MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('XMAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(' MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('123456789MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('123456789ABMAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('123456789ABCMAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('123456789ABCDMAGICTEST').should.equal nil
MimeMagic.by_magic('X MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('Y MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic('Z MAGICTEST').should.equal nil
MimeMagic.by_magic(StringIO.new 'MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new 'XMAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new ' MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new '123456789MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new '123456789ABMAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new '123456789ABCMAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new '123456789ABCDMAGICTEST').should.equal nil
MimeMagic.by_magic(StringIO.new 'X MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new 'Y MAGICTEST').should.equal 'application/mimemagic-test'
MimeMagic.by_magic(StringIO.new 'Z MAGICTEST').should.equal nil
end
it 'should handle different file objects' do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment