-------------------------------------------------------------------------------- -- Title: LWDAVService.lua -- Description: Like a square peg in a round hole -- Author: Raphaël Szwarc http://alt.textdrive.com/lua/ -- Creation Date: February 1, 2005 -- Legal: Copyright (C) 2005 Raphaël Szwarc -------------------------------------------------------------------------------- -- Based on Pier Fumagalli's "A simple approach to WebDAV" -- http://www.betaversion.org/~pier/wiki/display/pier/A+simple+approach+to+WebDAV -- import dependencies local LWObjectService = require( "LWObjectService" ) local LWDAVResponse = require( "LWDAVResponse" ) local LWDirectoryService = require( "LWDirectoryService" ) local LWFileService = require( "LWFileService" ) local LUList = require( "LUList" ) local LUMap = require( "LUMap" ) local LUString = require( "LUString" ) local LUURI = require( "LUURI" ) local LFSFile = require( "LFSFile" ) local LULog = require( "LULog" ) -- define the class local super = LWObjectService local self = super() -- initialization method function self:init( aPrefix, aPath, anAuthenticator ) self = super.init( self, aPrefix, nil, anAuthenticator ) self._path = aPath return self end -- method to access this service directory function self:directory() if self._directory == nil then self._directory = LFSFile( self:path() ) end return self._directory end -- method to access this service path function self:path() return self._path end -- method to define an object for a given path function self:objectWithPath( aPath, aContext ) if aPath ~= nil then local someComponents = LUList():addAll( LFSFile:pathComponents( self:path() ) ) someComponents:addAll( LUString:components( aPath, "/" ) ) local aName = someComponents:last() local aPath = LFSFile:separator() .. someComponents:removeLast():join( LFSFile:separator() ) local aFile = LFSFile( aPath, aName ) return aFile end return self:directory() end -- method to define a path for a given a object function self:pathWithObject( anObject, aContext ) if anObject ~= nil then local aServicePath = self:path() local aPath = anObject:path() aPath = aPath:sub( aServicePath:len() + 1 ) aPath = self:prefix() .. aPath:sub( 2 ) if anObject:isDirectory() == true and aPath:find( "/$" ) == nil then aPath = aPath .. "/" end aPath = aPath:gsub( "%" .. LFSFile:separator(), "%/" ) return aPath end return self:prefix() end -- method to define a component for given a object function self:componentWithObject( anObject, aContext ) local someBindings = LUMap() someBindings:put( "object", anObject ) local aComponent = LWDAVResponse( aContext, nil, someBindings ) return aComponent end -- method to handle a COPY method function self:handleCopy( aContext, shouldDelete ) local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) if anObject:exists() == true then local aDestination = aContext:request():headers():get( "destination" ) local anIndex = aDestination:find( "://" ) + 3 anIndex = aDestination:find( "/", anIndex ) aDestination = aDestination:sub( anIndex ) aDestination = aDestination:sub( self:prefix():len() + 1 ) aDestination = LUURI:decode( aDestination ) local aDestinationObject = self:objectWithPath( aDestination, aContext ) if aDestinationObject:exists() == true then local shouldOverwrite = aContext:request():headers( "overwrite" ) if shouldOverwrite == "T" then anObject:copy( aDestinationObject, shouldDelete ) aContext:response():writeStatus( 200 ) else aContext:response():writeStatus( 412 ) end else anObject:copy( aDestinationObject, shouldDelete ) aContext:response():writeStatus( 200 ) end else aContext:response():writeStatus( 404 ) end return self end -- method to handle a DELETE method function self:handleDelete( aContext ) local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) if anObject:exists() == true then anObject:delete() aContext:response():writeStatus( 200 ) else aContext:response():writeStatus( 404 ) end return self end -- method to handle a GET method function self:handleGet( aContext, hasContent ) local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) LULog:debug( anObject ) if anObject:exists() == true then local aPrefix = aContext:request():uri():path() local aPath = anObject:path() local anAuthenticator = self:authenticator() local aService = nil; if anObject:isFile() == true then aPrefix = LUString:components( aPrefix, "/" ) aPrefix:removeLast() aPrefix = "/" .. aPrefix:join( "/" ) .. "/" aPath = anObject:directory() aService = LWFileService( aPrefix, aPath, anAuthenticator ) else aService = LWDirectoryService( aPrefix, aPath, anAuthenticator ) end aService:handleGet( aContext, hasContent ) else aContext:response():writeStatus( 404, hasContent ) end return self end -- method to handle a MKCOL method function self:handleMkCol( aContext ) if aContext:request():content() == nil then local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) anObject:mkdir() aContext:response():writeStatus( 201 ) else aContext:response():writeStatus( 415 ) end return self end -- method to handle a MOVE method function self:handleMove( aContext ) self:handleCopy( aContext, true ) end -- method to handle a PROPFIND method function self:handlePropFind( aContext ) local aDepth = aContext:request():headers():get( "depth" ) aContext:request():content() if aDepth ~= nil then aDepth = tonumber( aDepth ) if aDepth ~= nil and aDepth >= 0 and aDepth <= 1 then local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) if anObject:exists() == true then local aComponent = self:componentWithObject( anObject, aContext ) aComponent:bindings():put( "depth", aDepth ) local aContent = aComponent:toString() aContext:response():writeContent( aContent ) else aContext:response():writeStatus( 404 ) end else aContext:response():writeStatus( 403 ) end else aContext:response():writeStatus( 403 ) end return self end -- method to handle a PROPPATCH method function self:handlePropPatch( aContext ) aContext:response():writeStatus( 403 ) return self end -- method to handle a PUT method function self:handlePut( aContext ) local aPath = self:pathWithContext( aContext ) local anObject = self:objectWithPath( aPath, aContext ) local didExist = anObject:exists() anObject:setContent( aContext:request():content() ) if didExist == true then aContext:response():writeStatus( 200 ) else aContext:response():writeStatus( 201 ) end return self end return self