import qualified Data.ByteString.Lazy as B
import Data.Char
import Data.Dynamic
+import Data.Int
import qualified Data.Map as M
import Data.Time.Clock.POSIX
import Data.Time.Format
import System.FilePath
import System.IO
import System.IO.Error (isDoesNotExistError, isPermissionError)
+import System.IO.Unsafe
import System.Locale
import System.Posix
import System.Posix.Handle
(".avi", "video/x-msvideo"),
(".css", "text/css"),
(".doc", "application/msword"),
+ (".docm", "application/vnd.ms-word.document.macroEnabled.12"),
+ (".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
+ (".dot", "application/msword"),
+ (".dotm", "application/vnd.ms-word.template.macroEnabled.12"),
+ (".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"),
(".gif", "image/gif"),
(".htm", "text/html"),
(".html", "text/html"),
(".mp3", "audio/mpeg"),
(".mpeg", "video/mpeg"),
(".mpg", "video/mpeg"),
+ (".odb", "application/vnd.oasis.opendocument.database"),
+ (".odc", "application/vnd.oasis.opendocument.chart"),
+ (".odf", "application/vnd.oasis.opendocument.formula"),
+ (".odg", "application/vnd.oasis.opendocument.graphics"),
+ (".odi", "application/vnd.oasis.opendocument.image"),
+ (".odm", "application/vnd.oasis.opendocument.text-master"),
+ (".odp", "application/vnd.oasis.opendocument.presentation"),
+ (".ods", "application/vnd.oasis.opendocument.spreadsheet"),
+ (".odt", "application/vnd.oasis.opendocument.text"),
+ (".otf", "application/octet-stream"),
+ (".otg", "application/vnd.oasis.opendocument.graphics-template"),
+ (".oth", "application/vnd.oasis.opendocument.text-web"),
+ (".otp", "application/vnd.oasis.opendocument.presentation-template"),
+ (".ots", "application/vnd.oasis.opendocument.spreadsheet-template"),
+ (".ott", "application/vnd.oasis.opendocument.text-template"),
(".pdf", "application/pdf"),
(".png", "image/png"),
+ (".pot", "application/vnd.ms-powerpoint"),
+ (".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"),
+ (".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"),
+ (".ppa", "application/vnd.ms-powerpoint"),
+ (".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"),
+ (".pps", "application/vnd.ms-powerpoint"),
+ (".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"),
+ (".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"),
(".ppt", "application/vnd.ms-powerpoint"),
+ (".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"),
+ (".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"),
(".ps", "application/postscript"),
(".svg", "image/svg+xml"),
(".swf", "application/x-shockwave-flash"),
(".tgz", "application/x-gzip"),
(".tif", "image/tiff"),
(".tiff", "image/tiff"),
+ (".ttf", "application/octet-stream"),
(".wav", "audio/x-wav"),
(".wmv", "video/x-ms-wmv"),
(".xaml", "application/xaml+xml"),
(".xap", "application/x-silverlight-app"),
(".xhtml", "application/xhtml+xml"),
+ (".xla", "application/vnd.ms-excel"),
+ (".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"),
(".xls", "application/vnd.ms-excel"),
+ (".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"),
+ (".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"),
+ (".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
+ (".xlt", "application/vnd.ms-excel"),
+ (".xltm", "application/vnd.ms-excel.template.macroEnabled.12"),
+ (".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"),
(".xml", "text/xml"),
(".xsl", "text/xml"),
(".zip", "application/zip")
(checkIfRange mTime >>=) $ maybe (return Nothing) $ \() -> do
case parseRange range size of
Just (a, b) | a <= b -> return $ Just (a, b)
- _ -> throw BadRange
+ Just _ -> throw BadRange
+ Nothing -> return Nothing
outputAll :: Handle -> FileOffset -> CGI CGIResult
outputAll h size = do
setHeader "Content-Length" $ show size
outputFPS =<< liftIO (B.hGetContents h)
+-- | Lazily read a given number of bytes from the handle into a
+-- 'ByteString', then close the handle.
+hGetClose :: Handle -> Int64 -> IO B.ByteString
+hGetClose h len = do
+ contents <- B.hGetContents h
+ end <- unsafeInterleaveIO (hClose h >> return B.empty)
+ return (B.append (B.take len contents) end)
+
outputRange :: Handle -> FileOffset -> Maybe (FileOffset, FileOffset) -> CGI CGIResult
outputRange h size Nothing = outputAll h size
outputRange h size (Just (a, b)) = do
"bytes " ++ show a ++ "-" ++ show b ++ "/" ++ show size
setHeader "Content-Length" $ show len
liftIO $ hSeek h AbsoluteSeek (fromIntegral a)
- outputFPS =<< liftIO (B.hGet h (fromIntegral len))
+ outputFPS =<< liftIO (hGetClose h (fromIntegral len))
serveFile :: FilePath -> CGI CGIResult
serveFile file = (`catch` outputMyError) $ do