acceptable 0.2.1
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.
- data/README.rdoc +28 -0
- data/lib/rack/acceptable/const.rb +37 -0
- data/lib/rack/acceptable/data/mime.types +20 -0
- data/lib/rack/acceptable/language_tag.rb +405 -0
- data/lib/rack/acceptable/middleware/fake_accept.rb +28 -0
- data/lib/rack/acceptable/middleware/formats.rb +69 -0
- data/lib/rack/acceptable/middleware/provides.rb +97 -0
- data/lib/rack/acceptable/mimetypes.rb +293 -0
- data/lib/rack/acceptable/mixin/headers.rb +88 -0
- data/lib/rack/acceptable/mixin/media.rb +56 -0
- data/lib/rack/acceptable/request.rb +45 -0
- data/lib/rack/acceptable/utils.rb +231 -0
- data/lib/rack/acceptable/version.rb +7 -0
- data/lib/rack/acceptable.rb +27 -0
- metadata +88 -0
@@ -0,0 +1,293 @@
|
|
1
|
+
# encoding: binary
|
2
|
+
|
3
|
+
require 'rack/acceptable/utils'
|
4
|
+
|
5
|
+
module Rack #:nodoc:
|
6
|
+
module Acceptable #:nodoc:
|
7
|
+
module MIMETypes
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
MEDIA_RANGE_REGEX = /^\s*(#{Utils::TOKEN_PATTERN})\/(#{Utils::TOKEN_PATTERN})\s*$/o.freeze
|
12
|
+
|
13
|
+
#--
|
14
|
+
# RFC 2616, sec. 3.7:
|
15
|
+
# The type, subtype, and parameter attribute names are case-
|
16
|
+
# insensitive. Parameter values might or might not be case-sensitive,
|
17
|
+
# depending on the semantics of the parameter name. Linear white space
|
18
|
+
# (LWS) MUST NOT be used between the type and subtype, nor between an
|
19
|
+
# attribute and its value. The presence or absence of a parameter might
|
20
|
+
# be significant to the processing of a media-type, depending on its
|
21
|
+
# definition within the media type registry.
|
22
|
+
#++
|
23
|
+
|
24
|
+
# ==== Parameters
|
25
|
+
# thing<String>::
|
26
|
+
# The Media-Type snippet or the single item from the HTTP_ACCEPT
|
27
|
+
# request-header, *without* 'q' parameter, accept-extensions and so on.
|
28
|
+
#
|
29
|
+
# ==== Returns
|
30
|
+
# Array[String, String, Hash]::
|
31
|
+
# Media-Range: type, subtype and parameter (as a +Hash+).
|
32
|
+
#
|
33
|
+
# ==== Raises
|
34
|
+
# Same things as Utils#split_mime_type.
|
35
|
+
# In other words, it checks only type/subtype pair.
|
36
|
+
#
|
37
|
+
def parse_media_range(thing)
|
38
|
+
snippets = thing.split(Utils::SEMICOLON_SPLITTER)
|
39
|
+
raise ArgumentError, "Malformed MIME-Type: #{thing}" unless MEDIA_RANGE_REGEX === snippets.shift
|
40
|
+
|
41
|
+
type = $1
|
42
|
+
subtype = $2
|
43
|
+
type.downcase!
|
44
|
+
subtype.downcase!
|
45
|
+
|
46
|
+
raise ArgumentError,
|
47
|
+
"Malformed MIME-Type: #{thing}" if type == Const::WILDCARD && subtype != Const::WILDCARD
|
48
|
+
|
49
|
+
params = {}
|
50
|
+
snippets.each do |pair|
|
51
|
+
pair.strip!
|
52
|
+
k,v = pair.split(Utils::PAIR_SPLITTER,2)
|
53
|
+
k.downcase!
|
54
|
+
params[k] = v
|
55
|
+
end
|
56
|
+
|
57
|
+
[type, subtype, params]
|
58
|
+
end
|
59
|
+
|
60
|
+
# ==== Parameters
|
61
|
+
# thing<String>::
|
62
|
+
# The Media-Type snippet or the single item from the HTTP_ACCEPT request-header.
|
63
|
+
#
|
64
|
+
# ==== Returns
|
65
|
+
# Array[String, String, Hash, Float, Hash]::
|
66
|
+
# Media-Range (type, subtype and parameter, as a +Hash+), quality factor
|
67
|
+
# and accept-extension (as a +Hash+, if any, or +nil+) of the MIME-Type.
|
68
|
+
#
|
69
|
+
# ==== Raises
|
70
|
+
# ArgumentError::
|
71
|
+
# There's a malformed quality factor, or type/subtype pair
|
72
|
+
# is not in a RFC 'Media-Range' pattern.
|
73
|
+
#
|
74
|
+
def parse_mime_type(thing)
|
75
|
+
|
76
|
+
snippets = thing.split(Utils::SEMICOLON_SPLITTER)
|
77
|
+
raise ArgumentError, "Malformed MIME-Type: #{thing}" unless MEDIA_RANGE_REGEX === snippets.shift
|
78
|
+
|
79
|
+
type = $1
|
80
|
+
subtype = $2
|
81
|
+
type.downcase!
|
82
|
+
subtype.downcase!
|
83
|
+
|
84
|
+
raise ArgumentError,
|
85
|
+
"Malformed MIME-Type: #{thing}" if type == Const::WILDCARD && subtype != Const::WILDCARD
|
86
|
+
|
87
|
+
qvalue = Utils::QVALUE_DEFAULT
|
88
|
+
params = {}
|
89
|
+
has_qvalue = false
|
90
|
+
accept_extension = nil
|
91
|
+
|
92
|
+
for pair in snippets
|
93
|
+
pair.strip!
|
94
|
+
k,v = pair.split(Utils::PAIR_SPLITTER,2)
|
95
|
+
|
96
|
+
# RFC 2616, sec. 14.1:
|
97
|
+
# Each media-range MAY be followed by one or more accept-params,
|
98
|
+
# beginning with the "q" parameter for indicating a relative quality
|
99
|
+
# factor. The first "q" parameter (if any) separates the media-range
|
100
|
+
# parameter(s) from the accept-params. Quality factors allow the user
|
101
|
+
# or user agent to indicate the relative degree of preference for that
|
102
|
+
# media-range, using the qvalue scale from 0 to 1 (section 3.9). The
|
103
|
+
# default value is q=1.
|
104
|
+
|
105
|
+
if has_qvalue
|
106
|
+
accept_extension ||= {}
|
107
|
+
accept_extension[k] = v || true # token [ "=" ( token | quoted-string ) ] - i.e, "v" is OPTIONAL.
|
108
|
+
else
|
109
|
+
k.downcase!
|
110
|
+
if k == Utils::QVALUE
|
111
|
+
raise ArgumentError, "Malformed quality factor: #{v.inspect}" unless Utils::QVALUE_REGEX === v
|
112
|
+
qvalue = v.to_f
|
113
|
+
has_qvalue = true
|
114
|
+
else
|
115
|
+
params[k] = v
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
[type, subtype, params, qvalue, accept_extension]
|
122
|
+
end
|
123
|
+
|
124
|
+
# ==== Parameters
|
125
|
+
# thing<String, Array>:: The Media-Type snippet or *parsed* Media-Type.
|
126
|
+
# types<Array>:: Parsed HTTP_ACCEPT request-header to check against.
|
127
|
+
#
|
128
|
+
# ==== Returns
|
129
|
+
# Float:: The quality factor (relative strength of the Media-Type).
|
130
|
+
#
|
131
|
+
def qualify_mime_type(thing, types)
|
132
|
+
weigh_mime_type(thing, types).first
|
133
|
+
end
|
134
|
+
|
135
|
+
# ==== Parameters
|
136
|
+
# thing<String, Array>:: The Media-Type snippet or *parsed* Media-Type.
|
137
|
+
# types<Array>:: Parsed HTTP_ACCEPT request-header to check against.
|
138
|
+
# qvalue_only<Boolean>:: Flag to force weighting to return the qvalue only.
|
139
|
+
# Optional. Default is +false+.
|
140
|
+
#
|
141
|
+
# ==== Returns
|
142
|
+
# Array[Float, Integer, Integer, Integer] or Array[Float]::
|
143
|
+
# Quality factor, rate, specificity and negated index of the most relevant Media-Range;
|
144
|
+
# i.e full relative weight of the Media-Type. If +qvaulue_only+ option is set to true,
|
145
|
+
# returns qvalue only.
|
146
|
+
#
|
147
|
+
def weigh_mime_type(thing, types, qvalue_only = false)
|
148
|
+
|
149
|
+
type, subtype, params = thing.is_a?(String) ? parse_media_range(thing) : thing
|
150
|
+
|
151
|
+
rate = 0
|
152
|
+
specificity = -1
|
153
|
+
quality = 0.00
|
154
|
+
index = 0
|
155
|
+
|
156
|
+
# RFC 2616, sec. 14.1:
|
157
|
+
# Media ranges can be overridden by more specific media ranges or
|
158
|
+
# specific media types. If more than one media range applies to a given
|
159
|
+
# type, the most specific reference has precedence.
|
160
|
+
# ...
|
161
|
+
# The media type quality factor associated with a given type is
|
162
|
+
# determined by finding the media range with the highest precedence
|
163
|
+
# which matches that type.
|
164
|
+
|
165
|
+
type_is_a_wildcard = type == Const::WILDCARD
|
166
|
+
subtype_is_a_wildcard = subtype == Const::WILDCARD
|
167
|
+
|
168
|
+
types.each_with_index do |(t,s,p,q),i|
|
169
|
+
next unless (type_is_a_wildcard || t == type || no_type_match = t == Const::WILDCARD) &&
|
170
|
+
(subtype_is_a_wildcard || s == subtype || no_subtype_match = s == Const::WILDCARD)
|
171
|
+
|
172
|
+
# we should skip when:
|
173
|
+
# - divergence:
|
174
|
+
# * "text;html;a=2" against "text/html;a=1,text/*;a=1" etc
|
175
|
+
# * "text/html;b=1" or "text/html" against "text/html;a=1" etc,
|
176
|
+
# i.e, 'a' parameter is NECESSARY, but our MIME-Type does NOT contain it
|
177
|
+
# - rate is lesser
|
178
|
+
# - rates are equal, but sp(ecificity) is lesser or exactly the same
|
179
|
+
|
180
|
+
r = no_type_match ? 0 : 10
|
181
|
+
r += no_subtype_match ? 0 : 1
|
182
|
+
|
183
|
+
next if r < rate
|
184
|
+
|
185
|
+
sp = 0
|
186
|
+
p.each do |k,v|
|
187
|
+
if params.key?(k) && params[k] == v
|
188
|
+
sp += 1
|
189
|
+
else
|
190
|
+
sp = -1
|
191
|
+
break
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
#next if sp == -1 || (r == rate && (sp < specificity || sp == specificity && quality > q))
|
196
|
+
if sp > -1 && (r > rate || (sp > specificity || sp == specificity && quality < q))
|
197
|
+
specificity = sp
|
198
|
+
rate = r
|
199
|
+
quality = q
|
200
|
+
index = i
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
qvalue_only ? [quality] : [quality, rate, specificity, -index]
|
205
|
+
end
|
206
|
+
|
207
|
+
# ==== Parameters
|
208
|
+
# provides<Array>:: The Array of available Media-Types (snippets or parsed). Could be empty.
|
209
|
+
# accepts<String>:: The Array of acceptable Media-Ranges. Could be empty.
|
210
|
+
# by_qvalue_only<String>:: Optional flag, see MIMETypes#weigh_mime_type. Default is +false+.
|
211
|
+
#
|
212
|
+
# ==== Returns
|
213
|
+
# The best one of available Media-Types or +nil+.
|
214
|
+
#
|
215
|
+
# ==== Raises
|
216
|
+
# Same things as Utils#parse_media_range.
|
217
|
+
#
|
218
|
+
# ==== Notes
|
219
|
+
# Acceptable Media-Types are supposed to have *downcased* and *well-formed*
|
220
|
+
# type, subtype, parameter's keys (according to RFC 2616, enumerated things
|
221
|
+
# are case-insensitive too), and *sensible* qvalues ("real numbers in the
|
222
|
+
# range 0 through 1, where 0 is the minimum and 1 the maximum value").
|
223
|
+
#
|
224
|
+
def detect_best_mime_type(provides, accepts, by_qvalue_only = false)
|
225
|
+
return nil if provides.empty?
|
226
|
+
return provides.first if accepts.empty?
|
227
|
+
i = 1
|
228
|
+
candidate = provides.map { |t| weigh_mime_type(t,accepts,by_qvalue_only) << i-=1 }.max
|
229
|
+
candidate.at(0) == 0 ? nil : provides.at(-candidate.last)
|
230
|
+
end
|
231
|
+
|
232
|
+
REGISTRY_PATH = ::File.expand_path(::File.join(::File.dirname(__FILE__), 'data', 'mime.types')).freeze
|
233
|
+
REGISTRY = {}
|
234
|
+
EXTENSIONS = {}
|
235
|
+
|
236
|
+
# Registers the new MIME-Type and associated extensions.
|
237
|
+
# The first one of extensions will be treated as the 'preferred'
|
238
|
+
# for the MIME-Type.
|
239
|
+
#
|
240
|
+
def register(thing, *extensions)
|
241
|
+
return if extensions.empty?
|
242
|
+
extensions.map! { |ext| ext[0] == ?. ? ext.downcase : ".#{ext.downcase}" }
|
243
|
+
extensions.each { |ext| REGISTRY[ext] = thing }
|
244
|
+
EXTENSIONS[thing] = extensions.first
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
|
248
|
+
# Deletes the MIME-Type (and associated extensions) from registry.
|
249
|
+
def delete(thing)
|
250
|
+
REGISTRY.delete_if { |_,v| v == thing }
|
251
|
+
EXTENSIONS.delete thing
|
252
|
+
end
|
253
|
+
|
254
|
+
def lookup(ext, fallback = 'application/octet-stream')
|
255
|
+
REGISTRY.fetch(ext[0] == ?. ? ext.downcase : ".#{ext.downcase}", fallback)
|
256
|
+
end
|
257
|
+
|
258
|
+
def extension_for(thing, fallback = nil)
|
259
|
+
EXTENSIONS.fetch thing, fallback
|
260
|
+
end
|
261
|
+
|
262
|
+
# Empties the registry.
|
263
|
+
def clear
|
264
|
+
EXTENSIONS.clear
|
265
|
+
REGISTRY.clear
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
# Resets the registry, i.e removes all and loads
|
270
|
+
# the default set of the MIME-Types.
|
271
|
+
def reset
|
272
|
+
clear
|
273
|
+
load_from(REGISTRY_PATH)
|
274
|
+
end
|
275
|
+
|
276
|
+
# Loads the set of MIME-Types from the Apache compatible mime.types file.
|
277
|
+
# original source: webrick.
|
278
|
+
def load_from(file)
|
279
|
+
open(file) do |io|
|
280
|
+
io.each do |line|
|
281
|
+
line.strip!
|
282
|
+
next if line.empty? || /^#/ === line
|
283
|
+
register *line.split(/\s+/)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
true
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# EOF
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'rack/acceptable/utils'
|
2
|
+
|
3
|
+
module Rack #:nodoc:
|
4
|
+
module Acceptable #:nodoc:
|
5
|
+
module Headers
|
6
|
+
|
7
|
+
# ==== Returns
|
8
|
+
# An Array with wildcards / *downcased* Content-Codings
|
9
|
+
# and associated quality factors (qvalues). Default qvalue is 1.0.
|
10
|
+
#
|
11
|
+
# ==== Raises
|
12
|
+
# ArgumentError::
|
13
|
+
# Syntax of the Accept-Encoding request-header is bad.
|
14
|
+
# For example, one of Content-Codings is not a 'token',
|
15
|
+
# one of quality factors is malformed etc.
|
16
|
+
#
|
17
|
+
def acceptable_encodings
|
18
|
+
Utils.parse_header(
|
19
|
+
env[Const::ENV_HTTP_ACCEPT_ENCODING].to_s.downcase,
|
20
|
+
Utils::HTTP_ACCEPT_TOKEN_REGEX)
|
21
|
+
rescue
|
22
|
+
raise ArgumentError,
|
23
|
+
"Malformed Accept-Encoding header: #{env[Const::ENV_HTTP_ACCEPT_ENCODING].inspect}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# ==== Returns
|
27
|
+
# An Array with wildcards / *downcased* Charsets and
|
28
|
+
# associated quality factors (qvalues). Default qvalue is 1.0.
|
29
|
+
#
|
30
|
+
# ==== Raises
|
31
|
+
# ArgumentError::
|
32
|
+
# Syntax of the Accept-Charset request-header is bad.
|
33
|
+
# For example, one of Charsets is not a 'token',
|
34
|
+
# one of quality factors is malformed etc.
|
35
|
+
#
|
36
|
+
def acceptable_charsets
|
37
|
+
Utils.parse_header(
|
38
|
+
env[Const::ENV_HTTP_ACCEPT_CHARSET].to_s.downcase,
|
39
|
+
Utils::HTTP_ACCEPT_TOKEN_REGEX)
|
40
|
+
rescue
|
41
|
+
raise ArgumentError,
|
42
|
+
"Malformed Accept-Charset header: #{env[Const::ENV_HTTP_ACCEPT_CHARSET].inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# ==== Returns
|
46
|
+
# An Array with wildcards / Language-Tags (as +Strings+)
|
47
|
+
# and associated quality factors (qvalues). Default qvalue is 1.0.
|
48
|
+
#
|
49
|
+
# ==== Raises
|
50
|
+
# ArgumentError::
|
51
|
+
# Syntax of the Accept-Language request-header is bad.
|
52
|
+
# For example, one of Language-Ranges is not in a RFC 'Language-Range'
|
53
|
+
# pattern, one of quality factors is malformed etc.
|
54
|
+
#
|
55
|
+
# ==== Notes
|
56
|
+
# * It uses {Extended Language-Range pattern}[http://tools.ietf.org/html/rfc4647#section-2.2].
|
57
|
+
# * It does *not* perform 'convenient transformations' (downcasing of primary tags etc).
|
58
|
+
# In other words, it parses Accept-Language header in unpretentious manner.
|
59
|
+
#
|
60
|
+
def acceptable_language_ranges
|
61
|
+
Utils.parse_header(
|
62
|
+
env[Const::ENV_HTTP_ACCEPT_LANGUAGE].to_s,
|
63
|
+
Utils::HTTP_ACCEPT_LANGUAGE_REGEX)
|
64
|
+
rescue
|
65
|
+
raise ArgumentError,
|
66
|
+
"Malformed Accept-Language header: #{env[Const::ENV_HTTP_ACCEPT_LANGUAGE].inspect}"
|
67
|
+
end
|
68
|
+
|
69
|
+
# ==== Returns
|
70
|
+
# An Array with Media-Ranges (as +Strings+) / wildcards and
|
71
|
+
# associated qvalues. Default qvalue is 1.0.
|
72
|
+
#
|
73
|
+
# ==== Raises
|
74
|
+
# ArgumentError::
|
75
|
+
# There's a malformed qvalue in header.
|
76
|
+
#
|
77
|
+
def acceptable_media_ranges
|
78
|
+
Utils.extract_qvalues(env[Const::ENV_HTTP_ACCEPT].to_s)
|
79
|
+
rescue
|
80
|
+
raise ArgumentError,
|
81
|
+
"Malformed Accept header: #{env[Const::ENV_HTTP_ACCEPT].inspect}"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# EOF
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rack/acceptable/mimetypes'
|
2
|
+
|
3
|
+
module Rack #:nodoc:
|
4
|
+
module Acceptable #:nodoc:
|
5
|
+
module Media
|
6
|
+
|
7
|
+
# ==== Returns
|
8
|
+
# An Array with *completely* parsed MIME-Types (incl. qvalues
|
9
|
+
# and accept-extensions; see Rack::Acceptable::MIMETypes).
|
10
|
+
# Default qvalue is 1.0.
|
11
|
+
#
|
12
|
+
# ==== Raises
|
13
|
+
# ArgumentError::
|
14
|
+
# Syntax of the The Accept request-header is bad.
|
15
|
+
# For example, one of Media-Ranges is not in a RFC 'Media-Range'
|
16
|
+
# pattern (type or subtype is invalid, or there's something like "*/foo")
|
17
|
+
# or, at last, one of MIME-Types has malformed qvalue.
|
18
|
+
#
|
19
|
+
def acceptable_media
|
20
|
+
@_acceptable_media ||= begin
|
21
|
+
header = env[Const::ENV_HTTP_ACCEPT].to_s
|
22
|
+
header.split(Utils::COMMA_SPLITTER).map! { |entry| MIMETypes.parse_mime_type(entry) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Checks if the MIME-Type passed acceptable.
|
27
|
+
def accept_media?(thing)
|
28
|
+
qvalue = MIMETypes.weigh_mime_type(thing, acceptable_media).first
|
29
|
+
qvalue > 0
|
30
|
+
rescue
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns the best match for the MIME-Type or
|
35
|
+
# pattern (like "text/*" etc) passed or +nil+.
|
36
|
+
def best_media_for(thing)
|
37
|
+
weight = MIMETypes.weigh_mime_type(thing, acceptable_media)
|
38
|
+
if weight.first > 0
|
39
|
+
acceptable_media.at(-weight.last)
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def negotiate_media(*things)
|
46
|
+
flag = (things.last == true || things.last == false) ? things.pop : false
|
47
|
+
MIMETypes.detect_best_mime_type(things, acceptable_media, flag)
|
48
|
+
end
|
49
|
+
|
50
|
+
alias :preferred_media_from :negotiate_media
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# EOF
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
require 'rack/acceptable/mixin/headers'
|
3
|
+
require 'rack/acceptable/mixin/media'
|
4
|
+
|
5
|
+
module Rack #:nodoc:
|
6
|
+
module Acceptable #:nodoc:
|
7
|
+
class Request < Rack::Request
|
8
|
+
include Rack::Acceptable::Headers
|
9
|
+
include Rack::Acceptable::Media
|
10
|
+
|
11
|
+
def acceptable_charsets
|
12
|
+
@_acceptable_charsets ||= super
|
13
|
+
end
|
14
|
+
|
15
|
+
def accept_charset?(chs)
|
16
|
+
chs = chs.downcase
|
17
|
+
accepts = acceptable_charsets
|
18
|
+
return true if accepts.empty?
|
19
|
+
if ch = accepts.assoc(chs) || accepts.assoc(Const::WILDCARD)
|
20
|
+
ch.last > 0
|
21
|
+
else
|
22
|
+
chs == Const::ISO_8859_1
|
23
|
+
end
|
24
|
+
rescue
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def accept_content?(content_type)
|
29
|
+
media = MIMETypes.parse_media_range(content_type)
|
30
|
+
chs = media.last.delete(Const::CHARSET)
|
31
|
+
chs ||= Const::ISO_8859_1 if media.first == Const::TEXT
|
32
|
+
if chs
|
33
|
+
accept_media?(media) && accept_charset?(chs)
|
34
|
+
else
|
35
|
+
accept_media?(media)
|
36
|
+
end
|
37
|
+
rescue
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# EOF
|