root/HTTP/WikiDAV.lua

Revision 1411 (checked in by rsz, 6 months ago)

cleanup

Line 
1 --------------------------------------------------------------------------------
2 -- Title:               WikiDAV.lua
3 -- Description:         Like a square peg in a round hole
4 -- Author:              Raphaël Szwarc http://alt.textdrive.com/lua/
5 -- Creation Date:       January 30, 2007
6 -- Legal:               Copyright (C) 2007 Raphaël Szwarc
7 --                      Under the terms of the MIT License
8 --                      http://www.opensource.org/licenses/mit-license.html
9 --------------------------------------------------------------------------------
10
11 -- import dependencies
12 local HTTP = require( 'HTTP' )
13 local HTTPExtra = require( 'HTTPExtra' )
14 local HTTPResponse = require( 'HTTPResponse' )
15 local URL = require( 'URL' )
16 local URLPath = require( 'URLPath' )
17 local WikiService = require( 'WikiService' )
18
19 local Encode = WikiService.Encode
20
21 local os = require( 'os' )
22
23 local assert = assert
24 local getmetatable = getmetatable
25 local module = module
26 local pairs = pairs
27 local require = require
28 local setmetatable = setmetatable
29 local tonumber = tonumber
30 local tostring = tostring
31
32 --------------------------------------------------------------------------------
33 -- HTTPResponse (Extra)
34 --------------------------------------------------------------------------------
35
36 module( 'HTTPResponse' )
37
38 local self = _M
39
40
41 function WebDAVFilter( aRequest, aResponse )
42     aResponse.header[ 'dav' ] = '1, 2'
43     aResponse.header[ 'ms-author-via' ] = 'DAV'
44 end
45
46 self.filter[ #self.filter + 1 ] = WebDAVFilter
47
48 --------------------------------------------------------------------------------
49 -- WikiDAV
50 --------------------------------------------------------------------------------
51
52 module( 'WikiDAV' )
53 _VERSION = '1.0'
54
55 local self = setmetatable( _M, {} )
56 local meta = getmetatable( self )
57
58 --------------------------------------------------------------------------------
59 -- Utitities
60 --------------------------------------------------------------------------------
61
62 local function Status( aCode, aDescription )
63     HTTP.response.status.code = aCode
64     HTTP.response.status.description = aDescription
65     HTTP.response.header[ 'content-type' ] = 'text/plain'
66    
67     return ( '%d %s' ):format( aCode, aDescription )
68 end
69
70 local function NamedResource( aResource )
71     if not aResource.name then
72         local aPath = HTTP.request.url.path
73        
74         aResource = aPath[ #aPath ]
75     end
76    
77     return aResource
78 end
79
80 local function Href( aResource, isFirst )
81     local anURL = HTTP.request.url
82     local aName = aResource.name or anURL.path[ #anURL.path ] or '/'
83     local aPath = anURL.path( aResource.name )
84    
85     if isFirst then
86         aPath = anURL.path
87     end
88    
89     aPath = URLPath( aPath )
90     aPath.directory = aResource.mode == 'directory'
91     aResource.name = aName
92    
93     return tostring( aPath )
94 end
95
96 local function ContentType( aResource )
97     if aResource.mode == 'file' then
98         local MIME = require( 'MIME' )
99         local MIMEType = require( 'MIMEType' )
100         local aName = aResource.name
101         local anExtension = ( aName:match( '^.+%.(%w+)$' ) or '' ):lower()
102        
103         return MIMEType[ anExtension ] or 'application/octet-stream'
104     end
105    
106     return 'text/html'
107 end
108
109 local function ETag( aResource, aLocation )
110     local crypto = require( 'crypto' )
111     local aTag = ( 'DAV:%s:%s:%d:%d' ):format( aLocation, aResource.name, aResource.modification, aResource.size )
112    
113     return crypto.sha1( aTag )
114 end
115
116 local function ResourceType( aResource )
117     if aResource.mode == 'directory' then
118         return '<collection/>'
119     end
120 end
121
122 local function ResourceIterator( aResource )
123     local anIterator = aResource.iterator
124     local isFirst = true
125    
126     return function()
127         if isFirst then
128             isFirst = false
129        
130             return aResource, true
131         end
132        
133         if anIterator then
134             return anIterator(), false
135         end
136     end
137 end
138
139 --------------------------------------------------------------------------------
140 -- Service methods
141 --------------------------------------------------------------------------------
142
143 function self:propfind()
144     local aDepth = tonumber( HTTP.request.header[ 'depth' ] )
145    
146     if aDepth and aDepth >= 0 and aDepth <= 1 then
147         local crypto = require( 'crypto' )
148         local Template = require( 'Template' )
149         local aTemplate = Template[ 'WikiDAV.txt' ]
150         local anEtag = 'DAV'
151         local aModification = 0
152        
153         for aResource, isFirst in ResourceIterator( self.resource ) do
154             local aResourceTemplate = aTemplate[ 'resources' ]
155             local aResourceHref = Href( aResource, isFirst )
156             local aResourceEtag = ETag( aResource, aResourceHref )
157            
158             aResourceTemplate[ 'href' ] = Encode( aResourceHref )
159             aResourceTemplate[ 'displayName' ] = Encode( aResource.name )
160             aResourceTemplate[ 'contentLength' ] = aResource.size
161             aResourceTemplate[ 'contentType' ] = ContentType( aResource )
162             aResourceTemplate[ 'etag' ] = aResourceEtag
163             aResourceTemplate[ 'creationDate' ] = os.date( '!%a, %d %b %Y %H:%M:%S GMT', aResource.creation or aResource.modification )
164             aResourceTemplate[ 'lastModified' ] = os.date( '!%a, %d %b %Y %H:%M:%S GMT', aResource.modification )
165             aResourceTemplate[ 'lockToken' ] = Encode( crypto.sha1( ( 'DAV:LOCK:%s' ):format( tostring( aResourceHref ) ) ) )
166             aResourceTemplate[ 'resourceType' ] = ResourceType( aResource )
167            
168             aTemplate[ 'resources' ] = aResourceTemplate
169            
170             anEtag = crypto.sha1( ( '%s:%s' ):format( anEtag, aResourceEtag ) )
171            
172             if aResource.modification > aModification then
173                 aModification = aResource.modification
174             end
175            
176             if aDepth == 0 then
177                 break
178             end
179         end
180
181         HTTP.response.status.code = 207
182         HTTP.response.status.description = 'Multi-Status'
183         HTTP.response.header[ 'content-type' ] = 'application/xml; charset=utf-8'
184         HTTP.response.header[ 'etag' ] = anEtag
185         HTTP.response.header[ 'last-modified' ] = os.date( '!%a, %d %b %Y %H:%M:%S GMT', aModification )
186        
187         return tostring( aTemplate )
188     end
189
190     return Status( 403, 'Forbidden' )
191 end
192
193 --------------------------------------------------------------------------------
194 -- Metamethods
195 --------------------------------------------------------------------------------
196
197 function meta:__call( aResource )
198     local aService = { resource = assert( aResource ) }
199    
200     setmetatable( aService, self )
201     self.__index = self   
202    
203     return aService
204 end
205
206 function meta:__concat( aValue )
207     return tostring( self ) .. tostring( aValue )
208 end
209
210 function meta:__tostring()
211     return ( '%s/%s' ):format( self._NAME, self._VERSION )
212 end
213
Note: See TracBrowser for help on using the browser.