Revert "Add Content-Length header."
[ecs.git] / app / controllers / messages_controller.rb
blob7eecc7bbe7dc110792795dcce637acace7d6578e
1 # Copyright (C) 2007, 2008, 2009, 2010 Heiko Bernloehr (FreeIT.de).
2
3 # This file is part of ECS.
4
5 # ECS is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of
8 # the License, or (at your option) any later version.
9
10 # ECS is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 # Affero General Public License for more details.
14
15 # You should have received a copy of the GNU Affero General Public
16 # License along with ECS. If not, see <http://www.gnu.org/licenses/>.
19 class MessagesController < ApplicationController
21   before_filter :late_initialize
22   before_filter :authentication
23   before_filter :add_cookie_header
24   before_filter :get_record, :only => [:show, :update, :destroy]
25   after_filter  :touch_participant_ttl
27   def initialize
28     super
29     @render_cmd=nil
30   end
32   def index
33     index_querystring_list
34     @list.each do |li| 
35       @body << @ressource_name << "/" << li.id.to_s << "\n"
36     end unless @list.empty?
37     index_render
38   end
40   def show
41     # TODO Webcache via stale? and fresh_when
42     @memberships = Membership.receiver(@participant.id, @record.id)
43     case
44     when @record.outtimed_auths_resource_by_non_owner?(@app_namespace, @resource_name, @participant)
45       raise Ecs::OuttimedAuthsException, 'Authorization token outtimed'
46     when (!@memberships.empty? or @participant.sender?(@record))
47       @record.filter(__method__, @app_namespace, @ressource_name, params)
48       @body = @record.body 
49       show_render
50       eval(@render_cmd) unless @render_cmd.blank?
51     else
52       raise Ecs::AuthorizationException, 
53             "You are not allowed to access this resource, " +
54             "because you are not the original sender or a receiver."
55     end
56   end
59   # Create and save a new message. Then render "Created 201" response.
60   # TODO exceptions for: create, constantize
61   def create
62     @record= Message.create__(request, @app_namespace, @ressource_name, @participant)
63     @body = @record.body
64     create_render
65   end
67   def update
68     @record.update__(request, @app_namespace, @ressource_name, @participant)
69     update_render
70   end
72   def destroy
73     @body = @record.body
74     @memberships = Membership.receiver(@participant.id, @record.id)
75     show_render
76     case
77     when @record.outtimed_auths_resource_by_non_owner?(@app_namespace, @resource_name, @participant)
78       @record.destroy_as_receiver(@participant)
79       raise Ecs::OuttimedAuthsException, 'Authorization token outtimed'
80     when (@participant.sender?(@record) and not @participant.receiver?(@record))
81       @record.destroy_as_sender
82     else
83       @record.destroy_as_receiver(@participant)
84     end
85     eval(@render_cmd) unless @render_cmd.blank?
86   end
88   def fifo
89     queue(:queue_type => :fifo)
90   end
92   def lifo
93     queue(:queue_type => :lifo)
94   end
96   def details
97     details = nil
98     no_data_to_render = false
99     if params["id"]
100       # member subresource
101       details = member_subresource_details(params["id"])
102       if !details[:receivers] then  no_data_to_render = true end
103     else
104       index_querystring_list
105       # collection subresource
106       details ||= []
107       @list.each do |li| 
108         details << member_subresource_details(li.id)
109       end unless @list.empty?
110       if details.empty? then  no_data_to_render = true end
111     end
112     if no_data_to_render
113       render :text => "", :content_type => "application/json", :layout => false
114     else
115       respond_to do |format|
116         format.json  { render :json  => JSON.pretty_generate(details) }
117         format.xml   { render :xml   => details }
118       end
119     end
120   end
122 protected
124   def member_subresource_details(record_id)
125     get_record(record_id)
126     if @participant.sender?(@record) or @participant.receiver?(@record)
127       receivers=[]
128       senders=[]
129       Membership.receivers(@record.id).each do |recv|
130         receivers << { :pid => recv.participant.id, :mid => recv.id, :cid => recv.community_id,
131                        :itsyou => recv.participant_id == @participant.id }
132         senders << { :mid => Membership.find_by_participant_id_and_community_id(@record.sender, recv.community_id).id }
133       end
134       content_type = @record.content_type
135       url = @ressource_name + "/" + record_id.to_s
136       { :receivers => receivers,
137         :senders => senders,
138         :content_type => content_type,
139         :url => url,
140         :owner => { :itsyou => @participant.id == @record.sender,
141                     :pid => @record.sender }
142       }
143     else
144       raise Ecs::AuthorizationException, 
145             "You are not allowed to access this resource, " +
146             "because you are not the original sender or a receiver."
147     end
148   end
150   def index_querystring_list
151     header_querystrings = request.headers["X-EcsQueryStrings"]
152     if header_querystrings
153       hqs = header_querystrings.split(",").map{|s| s.strip}.map{|s| s.split("=").map{|s| s.strip}}
154       sender = (m=hqs.assoc("sender")) ? m[1] : nil
155       receiver = (m=hqs.assoc("receiver")) ? m[1] : nil
156       all = (m=hqs.assoc("all")) ? m[1] : nil
157     end
158     sender ||= params["sender"] ? params["sender"] : nil
159     receiver ||= params["receiver"] ? params["receiver"] : nil
160     all ||= params["all"] ? params["all"] : nil
161     case
162     when sender == "true"
163       @list = Message.for_participant_sender(@participant).for_resource(@app_namespace,@ressource_name).for_not_removed.uniq
164     when receiver == "true"
165       @list = Message.for_participant_receiver(@participant).for_resource(@app_namespace,@ressource_name).for_not_removed.uniq
166     when all == "true"
167       list1 = Message.for_participant_sender(@participant).for_resource(@app_namespace,@ressource_name).for_not_removed
168       list2 = Message.for_participant_receiver(@participant).for_resource(@app_namespace,@ressource_name).for_not_removed
169       @list = list1.concat(list2).uniq
170     else
171       @list = Message.for_participant_receiver(@participant).for_resource(@app_namespace,@ressource_name).for_not_removed.uniq
172     end
173   end
175   def queue(queue_options = {:queue_type => :fifo})
176     begin
177       Message.transaction do
178         # returned record holds a lock (pessimistic locking)
179         @record = Message.fifo_lifo_rest(@app_namespace, @ressource_name,@participant.id, queue_options)
180         if @record
181           @memberships = Membership.receiver(@participant.id, @record.id)
182           @body = @record.body 
183           if request.post?
184             if @record
185               show_render
186               @record.destroy_as_receiver(@participant)
187               eval(@render_cmd) unless @render_cmd.blank?
188             else
189               raise ActiveRecord::RecordNotFound
190             end
191           else
192             show_render
193             eval(@render_cmd) unless @render_cmd.blank?
194           end
195         else
196           empty_render
197         end
198       end
199     rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotFound => error
200       logger.info "Concurrent access at queue resource"
201       raise
202     end
203   end
205   # inititialize instance variables dependent from request object
206   def late_initialize
207     @app_namespace= request.path.sub(/^\//,'').sub(/\/.*/,'')
208     @ressource_name= $&.sub(/\//,'').sub(/\/.*/,'')
209     #@ar_model_name= "#{@app_namespace}_#{@ressource_name}".pluralize.classify
210     #@ar_model= @ar_model_name.constantize
211   end
213   # get a record  out of the message table
214   def get_record(record_id = params["id"], app_namespace=@app_namespace, ressource_name=@ressource_name)
215     @record, @outdated_auth_token = Message.get_record(record_id, app_namespace, ressource_name)
216   end
217     
218   def empty_render
219     render :text => "", :content_type => "application/json"
220   end
222   def index_render
223     render :text => @body, :content_type => "text/uri-list"
224   end
226   def show_render
227     #expires_in 3.hours, 'max-stale' => 5.hours, :public => true
228     headers["Cache-Control"] = "private, max-age=5"
229     x_ecs_receiver_communities= ""
230     x_ecs_sender= ""
231     @memberships.each do |memb| 
232       x_ecs_receiver_communities << memb.community.id.to_s 
233       x_ecs_sender << Membership.find_by_participant_id_and_community_id(Participant.find(@record.sender).id, memb.community.id).id.to_s 
234       unless @memberships.last == memb
235         x_ecs_receiver_communities << ","
236         x_ecs_sender << "," 
237       end
238     end unless @memberships.blank?
239     headers["X-EcsReceiverCommunities"]= x_ecs_receiver_communities unless x_ecs_receiver_communities.blank?
240     headers["X-EcsSender"]= x_ecs_sender unless x_ecs_sender.blank?
241     @render_cmd='render :text => @body, :layout => false, :status => 200, :content_type => Mime::Type.lookup(@record.content_type)'
242   end
244   def create_render
245     location = request.protocol + request.host
246     # FIXME request.headers["SERVER_PORT"] is a string compared to integers.
247     location += ":" + request.headers["SERVER_PORT"] unless [80,443].include? request.headers["SERVER_PORT"]
248     location += request.headers["SCRIPT_NAME"] if request.headers["SCRIPT_NAME"]
249     location += request.path.gsub(/\/*$/,'') + "/" + @record.id.to_s
250     if @app_namespace == 'sys' and @ressource_name == 'auths'
251       render :text => @body, :layout => false, :status => 201, :location => location, :content_type => Mime::Type.lookup_by_extension("json")
252     else
253       render :text => "", :layout => false, :status => 201, :location => location, :content_type => Mime::Type.lookup(@record.content_type)
254     end
255   end
257   def update_render
258     location = request.protocol + request.host
259     location += request.headers["SCRIPT_NAME"] if request.headers["SCRIPT_NAME"]
260     location += request.path.gsub(/\/*$/,'')
261     render :text => "", :layout => false, :status => 200,
262            :location => location
263   end
265   def destroy_render
266     render :nothing => true, :layout => false, :status => 200,
267            :content_type => "application/json"
268   end