MFlow-0.2.0.4: src/MFlow/FileServer.hs
{- | A file server for frequently accessed files, such are static web pages and image decorations, icons etc
that are cached (memoized) according with the "Data.TCache" policies in the program space. This avoid the blocking of
the efficient GHC threads by frequent IO calls.So it enhances the performance
in the context of heavy concurrence.
It uses 'Data.TCache.Memoization'.
The caching-uncaching follows the `setPersist` criteria.
-}
-----------------------------------------------------------------------------
--
-- Module : FileServer
-- Copyright :
-- License : BSD3
--
-- Maintainer : agocorona@gmail.com
-- Stability : experimental
-- Portability :
--
--
--
-----------------------------------------------------------------------------
{-# OPTIONS -XScopedTypeVariables #-}
module MFlow.FileServer (setFilesPath, serveFile
) where
import MFlow
import MFlow.Cookies(contentHtml)
import Control.Monad.State
import Data.TCache.Memoization
import MFlow.Forms.XHtml
import System.Directory
import Data.ByteString.Lazy.Char8 as B(readFile,concat,append,pack,empty)
import Control.Exception as CE
import Data.List
import System.IO.Unsafe
import Data.IORef
import Data.Monoid
rfilesPath= unsafePerformIO $ newIORef "files/"
-- | Set the path of the files in the web server. The links to the files are relative to it
setFilesPath :: String -> IO ()
setFilesPath path= writeIORef rfilesPath path
pathPrm= "path"
fileServe :: Flow
fileServe = stateless $ \env -> do
case lookup pathPrm env of
Nothing -> error " no file specified"
Just path' ->do
when(let hpath= head path' in hpath == '/' || hpath =='\\') $ error noperm
when(not(".." `isSuffixOf` path') && ".." `isInfixOf` path') $ error noperm
filesPath <- readIORef rfilesPath
let path= filesPath ++ path'
serveFile path
-- isDirectory <- doesDirectoryExist path -- !> path
-- case isDirectory of
-- True -> do
-- dir <- directory1 $ path ++ "/"
-- return $ HttpData (setMime "text/html") [] dir
-- False -> servefile path
where
dropBack ".."= ".."
dropBack path
| "../" `isPrefixOf` revpath =reverse . maybetail $ dropWhile (/= '/') $ drop 3 revpath
| otherwise= path
where
revpath= reverse path
-- maybetail ""= "."
maybetail xs= tail xs
noperm= "no permissions"
stringServer mime str= stateless
-- | Is the flow to be added to the list in order to stream any file from the filesystem
-- for example, images
--
-- This app includes the fileServe flow:
--
-- @
-- main= do
-- addFileServerWF
-- addMessageFlows messageFlows
-- run 80 hackMessageFlow
-- adminLoop
-- where
-- messageFlows= [(\"noscript\" , transient $ runFlow showStories)
-- ,("\admin\" , transient $ runFlow admin)
-- ,("\mail\" , transient $ runFlow mail)]@
--
-- | Add the fileServer to the list of server flows
-- it is mandatory if you want to use the file service
addFileServerWF= addMessageFlows [("file", fileServe)]
-- | Creates the url of file path. To be used in ordinary links to files.
-- in Text.XHtml, a image would be embeded as
--
-- > image ![src $ linkFile imagepath]
--
-- in HSP:
--
-- > <img src=(linkFile imagepath)\>
-- | Given the relative path of a file, it return the content of the @href@ element in a html link
linkFile :: String -> String
linkFile path= "file?path=" <> path
directory :: Flow
directory = stateless $ \_ -> do
path <- readIORef rfilesPath
directory1 path
directory1 path = do
fs <- getDirectoryContents path
return $ HttpData [contentHtml][] $ B.concat [btag "a" [("href",linkFile ( path ++ file))] (B.pack file) `append` btag "br" [] B.empty | file <- fs]