{-# LANGUAGE OverloadedStrings #-}
module File (mighty, progName) where
import Control.Applicative
import qualified Data.ByteString.Char8 as S
import qualified Data.ByteString.Lazy.Char8 as L
import Data.List
import Data.Time
import Data.Time.Clock.POSIX
import Network.TCPInfo
import Network.Web.Server
import Network.Web.Server.Basic
import Network.Web.URI
import System.Directory
import System.FilePath
import System.IO
import System.Posix.Files
import URLMap
progName :: String
progName = "Mighttpd"
progVersion :: String
progVersion = "0.3.0"
progNameVersion :: String
progNameVersion = progName ++ " " ++ progVersion
----------------------------------------------------------------
mighty :: WebConfig -> URLMap -> Handle -> TCPInfo -> IO ()
mighty wcnf umap hdl tcpinfo = do
let bcnf = BasicConfig { obtain = fileGet
, info = fileInfo
, mapper = fileMapper umap
, serverName = S.pack progNameVersion
, tcpInfo = tcpinfo
}
connection hdl (basicServer bcnf) wcnf
----------------------------------------------------------------
lookupFileMap :: URLMap -> URL -> Path
lookupFileMap [] _ = None
lookupFileMap ((from,to):xs) url
| from `isPrefixOf` url = toPath to $ drop (length from) url
| otherwise = lookupFileMap xs url
where
toPath (File dir) restPath = File $ dir </> restPath
toPath (CGI dir _ urlPath) progParam = CGI prog param scriptName
where
(prog',param) = break (\x -> x == '?' || x == '/') progParam
prog = dir </> prog'
scriptName = urlPath </> prog'
toPath _ _ = error "toPath"
fileMapper :: URLMap -> URI -> Path
fileMapper umap uri = fileMapper' (lookupFileMap umap url)
where
url = unEscapeString . S.unpack . toURLwoPort $ uri
fileMapper' None = None
fileMapper' cgi@(CGI _ _ _) = cgi
fileMapper' (File file)
| hasTrailingPathSeparator file = File $ file </> "index.html"
| otherwise = File file
fileGet :: FilePath -> Maybe (Integer,Integer) -> IO L.ByteString
fileGet file Nothing = openFile file ReadMode >>= L.hGetContents
fileGet file (Just (skip,len)) = do
h <- openFile file ReadMode
hSeek h AbsoluteSeek skip
L.take (fromIntegral len) <$> L.hGetContents h
fileInfo :: FilePath -> IO (Maybe (Integer, UTCTime))
fileInfo file = do
exist <- doesFileExist file
if exist
then do
fs <- getFileStatus file
let size = fromIntegral . fileSize $ fs
mtime = posixSecondsToUTCTime . realToFrac . modificationTime $ fs
return $ Just (size, mtime)
else return Nothing