-- | Stolen from rack: serves files below the +root+ given, according to the -- path info of the Rack request. module Hack.Contrib.Middleware.File (file) where import Data.Default import Data.List (isInfixOf) import Data.Maybe import Hack import Hack.Contrib.Constants import Hack.Contrib.Mime import Hack.Contrib.Response import Hack.Contrib.Utils import MPS import Prelude hiding ((.), (^), (>), (+), (/), (-), readFile) import System.Directory import System.FilePath import qualified Data.ByteString.Lazy.Char8 as B file :: Maybe String -> Middleware file root _ = \env -> do let path = env.path_info .unescape_uri if ".." `isInfixOf` path then forbidden else path.serve root serve :: Maybe String -> String -> IO Response serve root fname = do cwd <- getCurrentDirectory let my_root = root.fromMaybe cwd let path = my_root / makeRelative "/" fname exist <- doesFileExist path if not exist then path.not_found else do can_read <- path.getPermissions ^ readable if not can_read then path.no_permission else path.serving where serving path = do content <- path.B.readFile size <- path.b2u.file_size ^ from_i mtime_str <- path.b2u.file_mtime ^ httpdate let default_content_type = "application/octet-stream" let safe_lookup = lookup_mime_type > fromMaybe default_content_type let content_type = path.takeExtension.safe_lookup return - def .set_body content .set_content_length size .set_content_type content_type .set_last_modified mtime_str .set_status 200 no_permission :: String -> IO Response no_permission path = return $ def .set_status 404 .set_content_type _TextPlain .set_content_length (msg.length) .set_body (B.pack msg) where msg = "No permission: " ++ path ++ "\n" not_found :: String -> IO Response not_found path = return $ def .set_status 404 .set_content_type _TextPlain .set_content_length (msg.length) .set_body (B.pack msg) where msg = "File not found: " ++ path ++ "\n" forbidden :: IO Response forbidden = return - def .set_status 403 .set_content_type _TextPlain .set_content_length (msg.length) .set_body (B.pack msg) where msg = "Forbidden\n"