Commit 6f61ff59 by Daniel Mendler

add matching of string and better handling of binary encoding

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