Froze rails gems
[depot.git] / vendor / rails / actionpack / lib / action_controller / mime_type.rb
1 require 'set'
2
3 module Mime
4 SET = []
5 EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
6 LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
7
8 # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
9 #
10 # class PostsController < ActionController::Base
11 # def show
12 # @post = Post.find(params[:id])
13 #
14 # respond_to do |format|
15 # format.html
16 # format.ics { render :text => post.to_ics, :mime_type => Mime::Type["text/calendar"] }
17 # format.xml { render :xml => @people.to_xml }
18 # end
19 # end
20 # end
21 class Type
22 @@html_types = Set.new [:html, :all]
23 cattr_reader :html_types
24
25 # These are the content types which browsers can generate without using ajax, flash, etc
26 # i.e. following a link, getting an image or posting a form. CSRF protection
27 # only needs to protect against these types.
28 @@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
29 cattr_reader :browser_generated_types
30
31
32 @@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
33 def self.unverifiable_types
34 ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller)
35 @@unverifiable_types
36 end
37
38 # A simple helper class used in parsing the accept header
39 class AcceptItem #:nodoc:
40 attr_accessor :order, :name, :q
41
42 def initialize(order, name, q=nil)
43 @order = order
44 @name = name.strip
45 q ||= 0.0 if @name == Mime::ALL # default wilcard match to end of list
46 @q = ((q || 1.0).to_f * 100).to_i
47 end
48
49 def to_s
50 @name
51 end
52
53 def <=>(item)
54 result = item.q <=> q
55 result = order <=> item.order if result == 0
56 result
57 end
58
59 def ==(item)
60 name == (item.respond_to?(:name) ? item.name : item)
61 end
62 end
63
64 class << self
65 def lookup(string)
66 LOOKUP[string]
67 end
68
69 def lookup_by_extension(extension)
70 EXTENSION_LOOKUP[extension]
71 end
72
73 # Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
74 # rendering different HTML versions depending on the user agent, like an iPhone.
75 def register_alias(string, symbol, extension_synonyms = [])
76 register(string, symbol, [], extension_synonyms, true)
77 end
78
79 def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
80 Mime.instance_eval { const_set symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms) }
81
82 SET << Mime.const_get(symbol.to_s.upcase)
83
84 ([string] + mime_type_synonyms).each { |string| LOOKUP[string] = SET.last } unless skip_lookup
85 ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last }
86 end
87
88 def parse(accept_header)
89 if accept_header !~ /,/
90 [Mime::Type.lookup(accept_header)]
91 else
92 # keep track of creation order to keep the subsequent sort stable
93 list = []
94 accept_header.split(/,/).each_with_index do |header, index|
95 params, q = header.split(/;\s*q=/)
96 if params
97 params.strip!
98 list << AcceptItem.new(index, params, q) unless params.empty?
99 end
100 end
101 list.sort!
102
103 # Take care of the broken text/xml entry by renaming or deleting it
104 text_xml = list.index("text/xml")
105 app_xml = list.index(Mime::XML.to_s)
106
107 if text_xml && app_xml
108 # set the q value to the max of the two
109 list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
110
111 # make sure app_xml is ahead of text_xml in the list
112 if app_xml > text_xml
113 list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
114 app_xml, text_xml = text_xml, app_xml
115 end
116
117 # delete text_xml from the list
118 list.delete_at(text_xml)
119
120 elsif text_xml
121 list[text_xml].name = Mime::XML.to_s
122 end
123
124 # Look for more specific XML-based types and sort them ahead of app/xml
125
126 if app_xml
127 idx = app_xml
128 app_xml_type = list[app_xml]
129
130 while(idx < list.length)
131 type = list[idx]
132 break if type.q < app_xml_type.q
133 if type.name =~ /\+xml$/
134 list[app_xml], list[idx] = list[idx], list[app_xml]
135 app_xml = idx
136 end
137 idx += 1
138 end
139 end
140
141 list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
142 list
143 end
144 end
145 end
146
147 def initialize(string, symbol = nil, synonyms = [])
148 @symbol, @synonyms = symbol, synonyms
149 @string = string
150 end
151
152 def to_s
153 @string
154 end
155
156 def to_str
157 to_s
158 end
159
160 def to_sym
161 @symbol || @string.to_sym
162 end
163
164 def ===(list)
165 if list.is_a?(Array)
166 (@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
167 else
168 super
169 end
170 end
171
172 def ==(mime_type)
173 return false if mime_type.blank?
174 (@synonyms + [ self ]).any? do |synonym|
175 synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
176 end
177 end
178
179 # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
180 # ActionController::RequestForgeryProtection.
181 def verify_request?
182 browser_generated?
183 end
184
185 def html?
186 @@html_types.include?(to_sym) || @string =~ /html/
187 end
188
189 def browser_generated?
190 @@browser_generated_types.include?(to_sym)
191 end
192
193 private
194 def method_missing(method, *args)
195 if method.to_s =~ /(\w+)\?$/
196 $1.downcase.to_sym == to_sym
197 else
198 super
199 end
200 end
201 end
202 end
203
204 require 'action_controller/mime_types'