Class: DAV4Rack::Controller

Inherits:
Object
  • Object
show all
Includes:
HTTPStatus
Defined in:
lib/dav4rack/controller.rb

Constant Summary

Constants included from HTTPStatus

HTTPStatus::StatusMessage

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request, response, options = {}) ⇒ Controller

request

Rack::Request

response

Rack::Response

options

Options hash

Create a new Controller. NOTE: options will be passed to Resource

Raises:

  • (Forbidden)


13
14
15
16
17
18
19
# File 'lib/dav4rack/controller.rb', line 13

def initialize(request, response, options={})
  raise Forbidden if request.path_info.include?('../')
  @request = request
  @response = response
  @options = options
  @resource = resource_class.new(actual_path, implied_path, @request, @response, @options)
end

Instance Attribute Details

#requestObject (readonly)

Returns the value of attribute request.



6
7
8
# File 'lib/dav4rack/controller.rb', line 6

def request
  @request
end

#resourceObject (readonly)

Returns the value of attribute resource.



6
7
8
# File 'lib/dav4rack/controller.rb', line 6

def resource
  @resource
end

#responseObject (readonly)

Returns the value of attribute response.



6
7
8
# File 'lib/dav4rack/controller.rb', line 6

def response
  @response
end

Instance Method Details

#authenticateObject

Perform authentication NOTE: Authentication will only be performed if the Resource has defined an #authenticate method

Raises:

  • (Unauthorized)


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/dav4rack/controller.rb', line 258

def authenticate
  authed = true
  if(resource.respond_to?(:authenticate, true))
    authed = false
    uname = nil
    password = nil
    if(request.env['HTTP_AUTHORIZATION'])
      auth = Rack::Auth::Basic::Request.new(request.env)
      if(auth.basic? && auth.credentials)
        uname = auth.credentials[0]
        password = auth.credentials[1]
      end
    end
    authed = resource.send(:authenticate, uname, password)
  end
  raise Unauthorized unless authed
end

#copyObject

Return response to COPY



116
117
118
# File 'lib/dav4rack/controller.rb', line 116

def copy
  move(:copy)
end

#deleteObject

Return response to DELETE



94
95
96
97
98
99
100
101
# File 'lib/dav4rack/controller.rb', line 94

def delete
  if(resource.exist?)
    resource.lock_check
    resource.delete
  else
    NotFound
  end
end

#getObject

Return response to GET



58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/dav4rack/controller.rb', line 58

def get
  if(resource.exist?)
    res = resource.get(request, response)
    if(res == OK && !resource.collection?)
      response['Etag'] = resource.etag
      response['Content-Type'] = resource.content_type
      response['Content-Length'] = resource.content_length.to_s
      response['Last-Modified'] = resource.last_modified.httpdate
    end
    res
  else
    NotFound
  end
end

#headObject

Return response to HEAD



46
47
48
49
50
51
52
53
54
55
# File 'lib/dav4rack/controller.rb', line 46

def head
  if(resource.exist?)
    response['Etag'] = resource.etag
    response['Content-Type'] = resource.content_type
    response['Last-Modified'] = resource.last_modified.httpdate
    OK
  else
    NotFound
  end
end

#lockObject

Lock current resource NOTE: This will pass an argument hash to Resource#lock and wait for a success/failure response.



199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/dav4rack/controller.rb', line 199

def lock
  lockinfo = request_document.xpath("//#{ns}lockinfo")
  asked = {}
  asked[:timeout] = request.env['Timeout'].split(',').map{|x|x.strip} if request.env['Timeout']
  asked[:depth] = depth
  unless([0, :infinity].include?(asked[:depth]))
    BadRequest
  else
    asked[:scope] = lockinfo.xpath("//#{ns}lockscope").children.find_all{|n|n.element?}.map{|n|n.name}.first
    asked[:type] = lockinfo.xpath("#{ns}locktype").children.find_all{|n|n.element?}.map{|n|n.name}.first
    asked[:owner] = lockinfo.xpath("//#{ns}owner/#{ns}href").children.map{|n|n.text}.first
    begin
      lock_time, locktoken = resource.lock(asked)
      render_xml(:prop) do |xml|
        xml.lockdiscovery do
          xml.activelock do
            if(asked[:scope])
              xml.lockscope do
                xml.send(asked[:scope])
              end
            end
            if(asked[:type])
              xml.locktype do
                xml.send(asked[:type])
              end
            end
            xml.depth asked[:depth].to_s
            xml.timeout lock_time ? "Second-#{lock_time}" : 'infinity'
            xml.locktoken do
              xml.href locktoken
            end
            if(asked[:owner])
              xml.owner asked[:owner]
            end
          end
        end
      end
      response.status = resource.exist? ? OK : Created
    rescue LockFailure => e
      multistatus do |xml|
        e.path_status.each_pair do |path, status|
          xml.response do
            xml.href path
            xml.status "#{http_version} #{status.status_line}"
          end
        end
      end
    end
  end
end

#mkcolObject

Return response to MKCOL



104
105
106
107
108
109
110
111
112
113
# File 'lib/dav4rack/controller.rb', line 104

def mkcol
  resource.lock_check
  status = resource.make_collection
  multistatus do |xml|
    xml.response do
      xml.href "#{scheme}://#{host}:#{port}#{url_escape(resource.public_path)}"
      xml.status "#{http_version} #{status.status_line}"
    end
  end
end

#move(*args) ⇒ Object

args

Only argument used: :copy

Move Resource to new location. If :copy is provided, Resource will be copied (implementation ease)



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/dav4rack/controller.rb', line 123

def move(*args)
  unless(resource.exist?)
    NotFound
  else
    resource.lock_check unless args.include?(:copy)
    destination = url_unescape(env['HTTP_DESTINATION'].sub(%r{https?://([^/]+)}, ''))
    dest_host = $1
    if(dest_host && dest_host.gsub(/:\d{2,5}$/, '') != request.host)
      BadGateway
    elsif(destination == resource.public_path)
      Forbidden
    else
      dest = resource_class.new(destination, clean_path(destination), @request, @response, @options.merge(:user => resource.user))
      status = nil
      if(args.include?(:copy))
        status = resource.copy(dest, overwrite)
      else
        return Conflict unless depth.is_a?(Symbol) || depth > 1
        status = resource.move(dest, overwrite)
      end
      response['Location'] = "#{scheme}://#{host}:#{port}#{dest.public_path}" if status == Created
      multistatus do |xml|
        xml.response do
          xml.href "#{scheme}://#{host}:#{port}#{status == Created ? dest.public_path : resource.public_path}"
          xml.status "#{http_version} #{status.status_line}"
        end
      end
    end
  end
end

#optionsObject

Return response to OPTIONS



38
39
40
41
42
43
# File 'lib/dav4rack/controller.rb', line 38

def options
  response["Allow"] = 'OPTIONS,HEAD,GET,PUT,POST,DELETE,PROPFIND,PROPPATCH,MKCOL,COPY,MOVE,LOCK,UNLOCK'
  response["Dav"] = "1, 2"
  response["Ms-Author-Via"] = "DAV"
  OK
end

#postObject

Return response to POST



89
90
91
# File 'lib/dav4rack/controller.rb', line 89

def post
  resource.post(request, response)
end

#propfindObject

Return respoonse to PROPFIND



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/dav4rack/controller.rb', line 155

def propfind
  unless(resource.exist?)
    NotFound
  else
    unless(request_document.xpath("//#{ns}propfind/#{ns}allprop").empty?)
      names = resource.property_names
    else
      names = request_document.xpath("//#{ns}propfind/#{ns}prop").children.find_all{|n|n.element?}.map{|n|n.name}
      names = resource.property_names if names.empty?
    end
    multistatus do |xml|
      find_resources.each do |resource|
        xml.response do
          xml.href "#{scheme}://#{host}:#{port}#{url_escape(resource.public_path)}"
          propstats(xml, get_properties(resource, names))
        end
      end
    end
  end
end

#proppatchObject

Return response to PROPPATCH



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/dav4rack/controller.rb', line 177

def proppatch
  unless(resource.exist?)
    NotFound
  else
    resource.lock_check
    prop_rem = request_match('/propertyupdate/remove/prop').children.map{|n| [n.name] }
    prop_set = request_match('/propertyupdate/set/prop').children.map{|n| [n.name, n.text] }
    multistatus do |xml|
      find_resources.each do |resource|
        xml.response do
          xml.href "#{scheme}://#{host}:#{port}#{url_escape(resource.public_path)}"
          propstats(xml, set_properties(resource, prop_set))
        end
      end
    end
  end
end

#putObject

Return response to PUT



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/dav4rack/controller.rb', line 74

def put
  if(resource.collection?)
    Forbidden
  elsif(!resource.parent_exists? || !resource.parent.collection?)
    Conflict
  else
    resource.lock_check
    status = resource.put(request, response)
    response['Location'] = "#{scheme}://#{host}:#{port}#{resource.public_path}" if status == Created
    response.body = response['Location']
    status
  end
end

#unlockObject

Unlock current resource



251
252
253
# File 'lib/dav4rack/controller.rb', line 251

def unlock
  resource.unlock(lock_token)
end

#url_escape(s) ⇒ Object

s

string

Escape URL string



23
24
25
26
27
# File 'lib/dav4rack/controller.rb', line 23

def url_escape(s)
  s.gsub(/([^\/a-zA-Z0-9_.-]+)/n) do
    '%' + $1.unpack('H2' * $1.size).join('%').upcase
  end.tr(' ', '+')
end

#url_unescape(s) ⇒ Object

s

string

Unescape URL string



31
32
33
34
35
# File 'lib/dav4rack/controller.rb', line 31

def url_unescape(s)
  s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
    [$1.delete('%')].pack('H*')
  end
end