Hawk-0.0.2: src/Hawk/Controller/Static.hs
{-# LANGUAGE TemplateHaskell #-}
module Hawk.Controller.Static
( error404
, error404Response
, error401
, error401Response
, error500
, error500Response
, staticRequest
) where
import Hawk.Controller.Types
import Hawk.Controller.Responses
import Hawk.Controller.Mime
import System.Directory (doesFileExist, getPermissions, Permissions(readable))
import System.FilePath (takeExtension)
import Data.ByteString.Lazy.UTF8 (fromString)
import qualified Data.ByteString.Lazy as BS (readFile)
import Data.List (isInfixOf)
import Control.Monad.Either
import Control.Monad.Reader (asks)
import Data.Default
import Hawk.Controller.Util.Uri (unescapeUri)
import qualified System.Log.Logger as Logger
import System.Log.Logger.TH (deriveLoggers)
$(deriveLoggers "Logger" [Logger.WARNING])
-- | return a 404 File not found error. I the file could not be found raise a error500
error404 :: EnvController Response
error404 = asks ((++) "/" . error404file . configuration)
>>= tryStatic
>>= maybe (warningM "could not find error 404 file" >> error500) (return . setStatus 404)
error404Response :: StateController a
error404Response = lift (lift error404) >>= returnLeft
error401 :: EnvController Response
error401 = asks ((++) "/" . error401file . configuration)
>>= tryStatic
>>= maybe (warningM "could not find error 401 file" >> error500) (return . setStatus 401 . addCustomHeaders [("WWW-Authenticate", "Basic")])
error401Response :: StateController a
error401Response = lift (lift error401) >>= returnLeft
-- | return a 500 internal server error
error500 :: EnvController Response
error500 = asks ((++) "/" . error500file . configuration)
>>= tryStatic
>>= maybe (warningM "could not find error 500 file" >> return (errorResponse m)) (return . setStatus 500)
where
m = fromString "Internal Server Error 500"
error500Response :: StateController a
error500Response = lift (lift error500) >>= returnLeft
staticRequest :: EnvController Response -> EnvController Response
staticRequest contr = do
path <- asks $ unescapeUri . pathInfo . request
isStatic <- tryStatic path
case isStatic of
Nothing -> contr
Just r -> return r
-- | try to deliver static content from the public dir
tryStatic :: String -> EnvController (Maybe Response)
tryStatic path =
if ".." `isInfixOf` path -- TODO make a real check if dir is sub dir
then return $ Just $ forbiddenResponse (fromString path) def
else do
let path' = if path == "/" then "/index.xhtml" else path
pDir <- asks $ publicDir . configuration
let fullPath = pDir ++ path'
fileExists <- liftIO $ doesFileExist fullPath
if fileExists
then do
canRead <- liftIO $ liftM readable $ getPermissions fullPath
if canRead
then do
file <- liftIO $ BS.readFile fullPath
return $ Just $ cachedResponse 600 (getMimeType $ takeExtension fullPath) file def
else return $ Just $ forbiddenResponse (fromString path) def
else return Nothing