File Coverage

blib/lib/MIME/Detect/Type.pm
Criterion Covered Total %
statement 80 90 88.8
branch 35 38 92.1
condition 20 33 60.6
subroutine 10 12 83.3
pod 3 6 50.0
total 148 179 82.6


line stmt bran cond sub pod time code
1             package MIME::Detect::Type;
2 4     4   76 use 5.020;
  4         14  
3 4     4   31 use Moo 2;
  4         63  
  4         49  
4 4     4   1731 use experimental 'signatures';
  4         14  
  4         30  
5              
6             our $VERSION = '0.12';
7              
8             =head1 NAME
9              
10             MIME::Detect::Type - the type of a file
11              
12             =head1 SYNOPSIS
13              
14             my $type = $mime->mime_type('/usr/bin/perl');
15             print $type->mime_type;
16             print $type->comment;
17              
18             =head1 METHODS
19              
20             =cut
21              
22             =head2 C<< $type->aliases >>
23              
24             Reference to the aliases of this type
25              
26             =cut
27              
28             has 'aliases' => (
29             is => 'ro',
30             default => sub {[]},
31             );
32              
33             =head2 C<< $type->comment >>
34              
35             Array reference of the type description in various languages
36             (currently unused)
37              
38             =cut
39              
40             has 'comment' => (
41             is => 'ro',
42             );
43              
44             =head2 C<< $type->mime_type >>
45              
46             print "Content-Type: " . $type->mime_type . "\r\n";
47              
48             String of the content type
49              
50             =cut
51              
52             has 'mime_type' => (
53             is => 'ro',
54             );
55              
56             =head2 C<< $type->globs >>
57              
58             print $_ for @{ $type->globs };
59              
60             Arrayref of the wildcard globs of this type
61              
62             =cut
63              
64             has 'globs' => (
65             is => 'ro',
66             default => sub {[]},
67             );
68              
69 2727     2727   3364 sub _get_extension( $e=undef ) {
  2727         2920  
  2727         2584  
70 2727 100       4087 if( defined $e ) { $e =~ s!^\*\.!! };
  6         26  
71 2727         6076 $e
72             }
73              
74 3696     3696   3711 sub _globmatch( $target, $glob ) {
  3696         4063  
  3696         4290  
  3696         3694  
75 3696         12292 $glob =~ s!([.+\\])!\\$1!g;
76 3696         7842 $glob =~ s!\*!.*!g;
77 3696         38278 $target =~ /\A$glob\z/;
78             }
79              
80             =head2 C<< $type->extension >>
81              
82             print $type->extension; # pl
83              
84             Returns the default extension for this mime type, without a separating
85             dot or the glob.
86              
87             =cut
88              
89 0     0 1 0 sub extension($self) {
  0         0  
  0         0  
90 0         0 _get_extension( $self->globs->[0] );
91             }
92              
93             =head2 C<< $type->valid_extension( $fn ) >>
94              
95             print "$fn has the wrong extension"
96             unless $type->valid_extension( $fn );
97              
98             Returns whether C<$fn> matches one of the extensions
99             as specified in C. If there is a match, the extension is returned
100             without dot.
101              
102             =cut
103              
104 2727     2727 1 3588 sub valid_extension( $self, $fn ) {
  2727         2918  
  2727         2900  
  2727         2600  
105             _get_extension((grep {
106 3696         4952 _globmatch( $fn, $_ )
107 2727         2820 } @{ $self->globs })[0])
  2727         6342  
108             }
109              
110             =head2 C<< $type->priority >>
111              
112             print $type->priority;
113              
114             Priority of this type. Types with higher priority
115             get tried first when trying to recognize a file type.
116              
117             The default priority is 50.
118              
119             =cut
120              
121             has 'priority' => (
122             is => 'ro',
123             default => 50,
124             );
125              
126             has 'rules' => (
127             is => 'ro',
128             default => sub { [] },
129             );
130              
131             =head2 C<< $type->superclass >>
132              
133             my $sc = $type->superclass;
134             print $sc->mime_type;
135              
136             The notional superclass of this file type. Note that superclasses
137             don't necessarily match the same magic numbers.
138              
139             =cut
140              
141             has 'superclass' => (
142             is => 'rw',
143             default => undef,
144             );
145              
146 288     288 0 376 sub parse_num( $num ) {
  288         480  
  288         345  
147 288 100       1874 $num =~ /^0x/ and return hex $num;
148 30         172 return 0+$num
149             }
150              
151 2726     2726 0 78551 sub BUILD($self, $args) {
  2726         4009  
  2726         3240  
  2726         3366  
152             # Preparse the rules here:
153 2726         3508 for my $rule (@{ $args->{rules} }) {
  2726         13742  
154 2588         4308 my $value = $rule->{value};
155              
156             # This should go into the part reading the XML, not into the part
157             # evaluating the rules
158 2588 100 66     12702 if( ref $rule eq 'HASH' and $rule->{type} eq 'string' ) {
    100 66        
    100 66        
    100 66        
    100 66        
    100 66        
    100 66        
    50 33        
159 2300         8347 my %replace = (
160             'n' => "\n",
161             'r' => "\r",
162             't' => "\t",
163             "\\" => "\\",
164             );
165 2300         7777 $value =~ s{\\([nrt\\]|([0-7][0-7][0-7])|x([0-9a-fA-F][0-9a-fA-F]))}
166 1814 50       10424 { $replace{$1} ? $replace{$1}
    100          
    100          
167             : $2 ? chr(oct($2))
168             : $3 ? chr(hex($3))
169             : $1
170             }xge;
171              
172             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'little32' ) {
173 72         195 $value = pack 'V', parse_num($rule->{value});
174              
175             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'little16' ) {
176 24         76 $value = pack 'v', parse_num($rule->{value});
177              
178             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'big32' ) {
179 108         302 $value = pack 'N', parse_num($rule->{value});
180              
181             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'big16' ) {
182 45         116 $value = pack 'n', parse_num($rule->{value});
183              
184             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'host16' ) {
185 12         75 $value = pack 'S', parse_num($rule->{value});
186              
187             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'host32' ) {
188 9         25 $value = pack 'L', parse_num($rule->{value});
189              
190             } elsif( ref $rule eq 'HASH' and $rule->{type} eq 'byte' ) {
191 18         50 $value = pack 'c', parse_num($rule->{value});
192              
193             } else {
194 0         0 die "Unknown rule type '$rule->{type}'";
195             };
196              
197 2588         4162 $rule->{type} = 'string';
198 2588         13182 $rule->{value} = $value;
199             }
200             }
201              
202 0     0 0 0 sub compile($self,$fragment) {
  0         0  
  0         0  
  0         0  
203 0         0 die "No direct-to-Perl compilation implemented yet.";
204             }
205              
206             =head2 C<< $type->matches $buffer >>
207              
208             my $buf = "PK\003\004"; # first four bytes of file
209             if( $type->matches( $buf ) {
210             print "Looks like a " . $type->mime_type . " file";
211             };
212              
213             =cut
214              
215 2730     2730 1 9517 sub matches($self, $buffer, $rules = $self->rules) {
  2730         3119  
  2730         3167  
  2730         5164  
  2730         2947  
216 2730         4139 my @rules = @$rules;
217              
218             # Superclasses are for information only
219             #if( $self->superclass and $self->superclass->mime_type !~ m!^text/!) {
220             # return if ! $self->superclass->matches($buffer);
221             #};
222              
223 2730 100       4178 if( !ref $buffer) {
224             # Upgrade to an in-memory filehandle
225 4         7 my $_buffer = $buffer;
226 4 50       65 open my $fh, '<', \$_buffer
227             or die "Couldn't open in-memory handle!";
228 4         12 binmode $fh;
229 4         129 $buffer = MIME::Detect::Buffer->new(fh => $fh);
230             };
231              
232             # Hardcoded rule for plain text detection...
233 2730 100       7509 if( $self->mime_type eq 'text/plain') {
234 3         14 my $buf = $buffer->request(0,256);
235 3         20 return $buf !~ /[\x00-\x08\x0b\x0c\x0e-\x1f]/;
236             };
237              
238 2727         3092 my $matches;
239 2727         3468 for my $rule (@rules) {
240              
241 2587         4316 my $value = $rule->{value};
242              
243 2587         5786 my $buf = $buffer->request($rule->{offset}, length $value);
244             #use Data::Dumper;
245             #$Data::Dumper::Useqq = 1;
246 4     4   7529 no warnings ('uninitialized', 'substr');
  4         8  
  4         1803  
247 2587 100       5163 if( $rule->{offset} =~ m!^(\d+):(\d+)$! ) {
248             #warn sprintf "%s: index match %d:%d for %s", $self->mime_type, $1,$2, Dumper $value;
249             #warn Dumper substr( $buf, 0, ($2-$1)+length($value));
250 361   66     1532 $matches = $matches || 1+index( substr( $buf, 0, ($2-$1)+length($value)), $value );
251             } else {
252             #warn sprintf "%s: substring match %d for %s", $self->mime_type, $rule->{offset}, Dumper $value;
253             #warn Dumper substr( $buf, $rule->{offset}, length($value));
254 2226   66     5426 $matches = $matches || substr( $buf, 0, length($value)) eq $value;
255             };
256 2587 100 33     4662 $matches = $matches && $self->matches( $buffer, $rule->{and} ) if $rule->{and};
257              
258 2587 100       5148 last if $matches;
259             };
260 2727         7053 !!$matches
261             }
262              
263             1;
264              
265             =head1 REPOSITORY
266              
267             The public repository of this module is
268             L.
269              
270             =head1 SUPPORT
271              
272             The public support forum of this module is
273             L.
274              
275             =head1 BUG TRACKER
276              
277             Please report bugs in this module via the RT CPAN bug queue at
278             L
279             or via mail to L.
280              
281             =head1 AUTHOR
282              
283             Max Maischein C
284              
285             =head1 COPYRIGHT (c)
286              
287             Copyright 2015-2024 by Max Maischein C.
288              
289             =head1 LICENSE
290              
291             This module is released under the same terms as Perl itself.
292              
293             =cut