marcel 1.2.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +26 -6
- data/SECURITY.md +7 -0
- data/lib/marcel/magic/definitions.rb +39 -0
- data/lib/marcel/magic/xml.rb +488 -0
- data/lib/marcel/magic/zip.rb +201 -0
- data/lib/marcel/magic.rb +204 -26
- data/lib/marcel/mime_type/definitions.rb +31 -40
- data/lib/marcel/mime_type.rb +60 -14
- data/lib/marcel/tables.rb +264 -25
- data/lib/marcel/version.rb +1 -1
- metadata +9 -19
data/lib/marcel/mime_type.rb
CHANGED
|
@@ -3,12 +3,46 @@
|
|
|
3
3
|
module Marcel
|
|
4
4
|
class MimeType
|
|
5
5
|
BINARY = "application/octet-stream"
|
|
6
|
+
MAX_DECLARED_TYPE_BYTES = 8 * 1024
|
|
7
|
+
TOKEN = "[!#$%&'*+\\-.^_`|~0-9A-Za-z]+"
|
|
8
|
+
QUOTED_STRING = '"(?:[\t\x20\x21\x23-\x5B\x5D-\x7E\x80-\xFF]|\\\\[\t\x20-\x7E\x80-\xFF])*"'
|
|
9
|
+
MEDIA_TYPE = %r{\A(#{TOKEN}/#{TOKEN})(?:[ \t]*;[ \t]*#{TOKEN}=(?:#{TOKEN}|#{QUOTED_STRING}))*(?:[ \t]*;[ \t]*)?\z}n
|
|
6
10
|
|
|
7
11
|
class << self
|
|
8
|
-
def
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
def canonicalize(type, instead_of:)
|
|
13
|
+
Magic.canonicalize type, instead_of: instead_of
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def extend(type, extensions: nil, aliases: nil, parents: nil, magic: nil)
|
|
17
|
+
type = type.downcase
|
|
18
|
+
|
|
19
|
+
if canonical = Marcel::TYPE_ALIASES[type]
|
|
20
|
+
warn "#{type} is an alias; extending its canonical type #{canonical} instead"
|
|
21
|
+
type = canonical
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
extensions = Array(extensions)
|
|
25
|
+
if extensions.any? && extensions.sort == Array(Marcel::TYPE_EXTS[type]).sort
|
|
26
|
+
warn "#{type} already has extensions #{extensions.inspect}"
|
|
27
|
+
end
|
|
28
|
+
extensions |= Array(Marcel::TYPE_EXTS[type])
|
|
29
|
+
|
|
30
|
+
aliases = Array(aliases)
|
|
31
|
+
existing_aliases = Marcel::TYPE_ALIASES.select { |_, existing| existing == type }.keys
|
|
32
|
+
if aliases.any? && aliases.sort == existing_aliases.sort
|
|
33
|
+
warn "#{type} already has aliases #{aliases.inspect}"
|
|
34
|
+
end
|
|
35
|
+
aliases |= existing_aliases
|
|
36
|
+
|
|
37
|
+
parents = Array(parents)
|
|
38
|
+
if parents.any? && parents.sort == Array(Marcel::TYPE_PARENTS[type]).sort
|
|
39
|
+
warn "#{type} already has parents #{parents.inspect}"
|
|
40
|
+
end
|
|
41
|
+
parents |= Array(Marcel::TYPE_PARENTS[type])
|
|
42
|
+
|
|
43
|
+
# No duplicate-magic warning: matcher order determines precedence, so re-registering
|
|
44
|
+
# an existing matcher legitimately promotes it ahead of the generated tables.
|
|
45
|
+
Magic.add(type, extensions: extensions, magic: magic, aliases: aliases, parents: parents)
|
|
12
46
|
end
|
|
13
47
|
|
|
14
48
|
# Returns the most appropriate content type for the given file.
|
|
@@ -23,7 +57,13 @@ module Marcel
|
|
|
23
57
|
#
|
|
24
58
|
# The most appropriate type is determined by the following:
|
|
25
59
|
# * type declared by binary magic number data
|
|
26
|
-
# *
|
|
60
|
+
# * valid declared MIME type, unless it is application/octet-stream
|
|
61
|
+
# * type inferred from the file name or extension
|
|
62
|
+
#
|
|
63
|
+
# A later candidate is used only if it is more specific than the type already found.
|
|
64
|
+
#
|
|
65
|
+
# The result is a best-effort label, not validation that the file is safe or conforms to
|
|
66
|
+
# the returned type. Treat +name+ and +declared_type+ as hints from their respective sources.
|
|
27
67
|
#
|
|
28
68
|
# If no type can be determined, then +application/octet-stream+ is returned.
|
|
29
69
|
def for(pathname_or_io = nil, name: nil, extension: nil, declared_type: nil)
|
|
@@ -37,7 +77,8 @@ module Marcel
|
|
|
37
77
|
if pathname_or_io
|
|
38
78
|
with_io(pathname_or_io) do |io|
|
|
39
79
|
if magic = Marcel::Magic.by_magic(io)
|
|
40
|
-
magic.type.downcase
|
|
80
|
+
type = magic.canonical.type.downcase
|
|
81
|
+
Marcel::Magic::Xml.refine(io, Marcel::Magic::Zip.refine(io, type))
|
|
41
82
|
end
|
|
42
83
|
end
|
|
43
84
|
end
|
|
@@ -46,7 +87,7 @@ module Marcel
|
|
|
46
87
|
def for_name(name)
|
|
47
88
|
if name
|
|
48
89
|
if magic = Marcel::Magic.by_path(name)
|
|
49
|
-
magic.type.downcase
|
|
90
|
+
magic.canonical.type.downcase
|
|
50
91
|
end
|
|
51
92
|
end
|
|
52
93
|
end
|
|
@@ -54,13 +95,13 @@ module Marcel
|
|
|
54
95
|
def for_extension(extension)
|
|
55
96
|
if extension
|
|
56
97
|
if magic = Marcel::Magic.by_extension(extension)
|
|
57
|
-
magic.type.downcase
|
|
98
|
+
magic.canonical.type.downcase
|
|
58
99
|
end
|
|
59
100
|
end
|
|
60
101
|
end
|
|
61
102
|
|
|
62
103
|
def for_declared_type(declared_type)
|
|
63
|
-
type = parse_media_type(declared_type)
|
|
104
|
+
type = Marcel::Magic.canonical(parse_media_type(declared_type))
|
|
64
105
|
|
|
65
106
|
# application/octet-stream is treated as an undeclared/missing type,
|
|
66
107
|
# allowing the type to be inferred from the filename. If there's no
|
|
@@ -70,17 +111,22 @@ module Marcel
|
|
|
70
111
|
|
|
71
112
|
def with_io(pathname_or_io, &block)
|
|
72
113
|
if defined?(Pathname) && pathname_or_io.is_a?(Pathname)
|
|
73
|
-
pathname_or_io.open(&block)
|
|
114
|
+
pathname_or_io.open("rb", &block)
|
|
74
115
|
else
|
|
75
116
|
yield pathname_or_io
|
|
76
117
|
end
|
|
77
118
|
end
|
|
78
119
|
|
|
79
120
|
def parse_media_type(content_type)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
121
|
+
return unless content_type
|
|
122
|
+
return if content_type.bytesize > MAX_DECLARED_TYPE_BYTES
|
|
123
|
+
|
|
124
|
+
content_type = content_type.dup.force_encoding(Encoding::BINARY)
|
|
125
|
+
content_type.sub!(/\A[ \t\r\n]+/n, "")
|
|
126
|
+
content_type.sub!(/[ \t\r\n]+\z/n, "")
|
|
127
|
+
|
|
128
|
+
media_type = MEDIA_TYPE.match(content_type)
|
|
129
|
+
media_type[1].downcase.force_encoding(Encoding::UTF_8) if media_type
|
|
84
130
|
end
|
|
85
131
|
|
|
86
132
|
# For some document types (notably Microsoft Office) we recognise the main content
|