File Coverage

blib/lib/MIME/Types.pm
Criterion Covered Total %
statement 138 151 91.3
branch 62 84 73.8
condition 8 15 53.3
subroutine 22 28 78.5
pod 13 15 86.6
total 243 293 82.9


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution MIME-Types version 2.30.
2             # The POD got stripped from this file by OODoc version 3.05.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 1999-2025 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12             package MIME::Types;{
13             our $VERSION = '2.30';
14             }
15              
16              
17 4     4   502599 use strict;
  4         10  
  4         152  
18 4     4   56 use warnings;
  4         15  
  4         222  
19              
20 4     4   1885 use MIME::Type ();
  4         17  
  4         122  
21 4     4   31 use File::Spec ();
  4         8  
  4         157  
22 4     4   23 use File::Basename qw/dirname/;
  4         7  
  4         438  
23 4     4   26 use List::Util qw/first/;
  4         8  
  4         10388  
24              
25             #--------------------
26              
27             my %typedb;
28 4     4 1 623754 sub new(@) { (bless {}, shift)->init( {@_} ) }
29              
30             sub init($)
31 4     4 0 15 { my ($self, $args) = @_;
32 4 50       36 keys %typedb or $self->_read_db($args);
33 4         226 $self;
34             }
35              
36             sub _read_db($)
37 4     4   10 { my ($self, $args) = @_;
38 4         10 my $skip_extensions = $args->{skip_extensions};
39 4         10 my $only_complete = $args->{only_complete};
40 4         10 my $only_iana = $args->{only_iana};
41              
42             my $db = $ENV{PERL_MIME_TYPE_DB} || $args->{db_file} ||
43 4   33     436 File::Spec->catfile(dirname(__FILE__), 'types.db');
44              
45 4 50   4   3487 open my $dbh, '<:encoding(utf8)', $db
  4         72  
  4         26  
  4         184  
46             or die "cannot open type database in $db: $!\n";
47              
48 4         4633 while(1)
49 196         983 { my $header = $dbh->getline;
50 196 100       41876 defined $header or last;
51 192         307 chomp $header;
52              
53             # This logic is entangled with the bin/collect_types script
54 192         1141 my ($count, $major, $is_iana, $has_ext) = split /\:/, $header;
55 192 100 33     1128 my $skip_section = $major eq 'EXTENSIONS' ? $skip_extensions
56             : (($only_iana && !$is_iana) || ($only_complete && !$has_ext));
57              
58 192         451 (my $section = $major) =~ s/^x-//;
59 192 100       429 if($major eq 'EXTENSIONS')
60 4         42 { while(my $line = $dbh->getline)
61 9368 100       25447 { last if $line =~ m/^$/;
62 9364 50       16910 next if $skip_section;
63 9364         13545 chomp $line;
64 9364 50       74735 $typedb{$section}{$1} = $2 if $line =~ m/(.*);(.*)/;
65             }
66             }
67             else
68 188         652 { while(my $line = $dbh->getline)
69 13204 100       34002 { last if $line =~ m/^$/;
70 13016 50       22898 next if $skip_section;
71 13016         19064 chomp $line;
72 13016 50       96537 $typedb{$section}{$1} = "$major/$line" if $line =~ m/^(?:x-)?([^;]+)/;
73             }
74             }
75             }
76              
77 4         72 $dbh->close;
78             }
79              
80             # Catalyst-Plugin-Static-Simple uses it :(
81       0 0   sub create_type_index {}
82              
83             #--------------------
84              
85             sub type($)
86 4385     4385 1 13913 { my $spec = lc $_[1];
87 4385 50       9529 $spec = 'text/plain' if $spec eq 'text'; # old mailers
88              
89 4385 50       18994 $spec =~ m!^(?:x\-)?([^/]+)/(?:x-)?(.*)!
90             or return;
91              
92 4385 50       12697 my $section = $typedb{$1} or return;
93 4385 50       13582 my $record = $section->{$2} or return;
94 4385 100       12058 return $record if ref $record; # already extended
95              
96 2193         3998 my $simple = $2;
97 2193         9860 my ($type, $ext, $enc, $char) = split m/\;/, $record;
98 2193         3973 my $os = undef; # XXX TODO
99              
100 2193         9207 $section->{$simple} = MIME::Type->new(
101             type => $type,
102             extensions => [split /\,/, $ext],
103             encoding => $enc,
104             system => $os,
105             charset => $char,
106             );
107             }
108              
109              
110             sub mimeTypeOf($)
111 29     29 1 1354 { my $self = shift;
112 29         79 my $ext = lc(shift);
113              
114             # Extensions may contains multiple dots (rare)
115 29         40 while(1)
116 51 100       208 { if(my $type = $typedb{EXTENSIONS}{$ext})
117 27         94 { return $self->type($type);
118             }
119 24 100       216 $ext =~ s/.*?\.// or last;
120             }
121              
122 2         5 undef;
123             }
124              
125              
126             sub addType(@)
127 3     3 1 70 { my $self = shift;
128              
129 3         8 foreach my $type (@_)
130 3         10 { my ($major, $minor) = split m!/!, $type->simplified;
131 3         13 $typedb{$major}{$minor} = $type;
132 3         9 $typedb{EXTENSIONS}{$_} = $type for $type->extensions;
133             }
134 3         8 $self;
135             }
136              
137              
138             sub types()
139 0     0 1 0 { my $self = shift;
140 0         0 my @types;
141 0         0 foreach my $section (keys %typedb)
142 0 0       0 { next if $section eq 'EXTENSIONS';
143 0         0 push @types, map $_->type("$section/$_"), sort keys %{$typedb{$section}};
  0         0  
144             }
145 0         0 @types;
146             }
147              
148              
149             sub listTypes()
150 6     6 1 14 { my $self = shift;
151 6         11 my @types;
152 6         99 foreach my $section (keys %typedb)
153 144 100       465 { next if $section eq 'EXTENSIONS';
154 138         242 foreach my $sub (sort keys %{$typedb{$section}})
  138         20329  
155 19320         39525 { my $record = $typedb{$section}{$sub};
156 19320 50       64762 push @types, ref $record ? $record->type : $record =~ m/^([^;]+)/ ? $1 : die;
    100          
157             }
158             }
159 6         37730 @types;
160             }
161              
162              
163 0     0 1 0 sub extensions { keys %{$typedb{EXTENSIONS}} }
  0         0  
164 0     0   0 sub _MojoExtTable() {$typedb{EXTENSIONS}}
165              
166             #--------------------
167              
168             sub httpAccept($)
169 7     7 1 7800 { my $self = shift;
170 7         52 my @listed;
171              
172 7         49 foreach (split /\,\s*/, shift)
173             {
174 17 50       104 m!^ ([a-zA-Z0-9-]+ | \*) / ( [a-zA-Z0-9+-]+ | \* )
175             \s* (?: \;\s*q\=\s* ([0-9]+(?:\.[0-9]*)?) \s* )?
176             (\;.* | )
177             $ !x or next;
178              
179 17         68 my $mime = "$1/$2$4";
180 17 100       44 my $q = defined $3 ? $3 : 1; # q, default=1
181              
182             # most complex first
183 17 100       72 $q += $4 ? +0.01 : $1 eq '*' ? -0.02 : $2 eq '*' ? -0.01 : 0;
    100          
    50          
184              
185             # keep order
186 17         43 $q -= @listed*0.0001;
187              
188 17         53 push @listed, [ $mime => $q ];
189             }
190 7         53 map $_->[0], sort {$b->[1] <=> $a->[1]} @listed;
  15         66  
191             }
192              
193              
194             sub httpAcceptBest($@)
195 8     8 1 1499 { my $self = shift;
196 8 100       27 my @accept = ref $_[0] eq 'ARRAY' ? @{(shift)} : $self->httpAccept(shift);
  4         10  
197 8         12 my $match;
198              
199 8         11 foreach my $acc (@accept)
200 10         20 { $acc =~ s/\s*\;.*//; # remove attributes
201 17     17   38 my $m = $acc !~ s#/\*$## ? first { $_->equals($acc) } @_
202             : $acc eq '*' ? $_[0] # $acc eq */*
203 10 0   0   65 : first { $_->mediaType eq $acc } @_;
  0 50       0  
204 10 100       48 return $m if defined $m;
205             }
206              
207 1         2 ();
208             }
209              
210              
211             sub httpAcceptSelect($@)
212 4     4 1 13 { my ($self, $accept) = (shift, shift);
213 4 50       20 my $fns = !@_ ? return () : ref $_[0] eq 'ARRAY' ? shift : [@_];
    50          
214              
215 4 100       12 unless(defined $accept)
216 1         3 { my $fn = $fns->[0];
217 1         3 return ($fn, $self->mimeTypeOf($fn));
218             }
219              
220             # create mapping type -> filename
221 3         7 my (%have, @have);
222 3         7 foreach my $fn (@$fns)
223 6 50       15 { my $type = $self->mimeTypeOf($fn) or next;
224 6         22 $have{$type->simplified} = $fn;
225 6         14 push @have, $type;
226             }
227              
228 3         10 my $type = $self->httpAcceptBest($accept, @have);
229 3 100       13 defined $type ? ($have{$type}, $type) : ();
230             }
231              
232             # OLD INTERFACE (version 0.06 and lower)
233              
234             #--------------------
235              
236 4     4   36 use base 'Exporter';
  4         9  
  4         2353  
237             our @EXPORT_OK = qw(by_suffix by_mediatype import_mime_types);
238              
239              
240             my $mime_types;
241              
242             sub by_suffix($)
243 7     7 1 233476 { my $filename = shift;
244 7   66     50 $mime_types ||= MIME::Types->new;
245 7         27 my $mime = $mime_types->mimeTypeOf($filename);
246              
247 7 100       29 my @data = defined $mime ? ($mime->type, $mime->encoding) : ('','');
248 7 100       36 wantarray ? @data : \@data;
249             }
250              
251              
252             sub by_mediatype($)
253 8     8 1 24752 { my $type = shift;
254 8   33     30 $mime_types //= MIME::Types->new;
255              
256 8         17 my @found;
257 8 100 100     57 if(!ref $type && index($type, '/') >= 0)
258 2         12 { my $mime = $mime_types->type($type);
259 2 50       7 @found = $mime if $mime;
260             }
261             else
262 6 100       108 { my $search = ref $type eq 'Regexp' ? $type : qr/$type/i;
263 6         31 @found = map $mime_types->type($_), grep $_ =~ $search, $mime_types->listTypes;
264             }
265              
266 8         1802 my @data;
267 8         30 foreach my $mime (@found)
268 4350         9434 { push @data, map +[$_, $mime->type, $mime->encoding], $mime->extensions;
269             }
270              
271 8 100       2652 wantarray ? @data : \@data;
272             }
273              
274              
275             sub import_mime_types($)
276 0     0 1   { my $filename = shift;
277 4     4   73 use Carp;
  4         8  
  4         497  
278 0           croak <<'CROAK';
279             import_mime_types is not supported anymore: if you have types to add
280             please send them to the author.
281             CROAK
282             }
283              
284             1;