packages feed

espial-0.0.39: src/Handler/Locales.hs

module Handler.Locales (getLocalesFileR, isSafePiece, isWithinDir) where

import Data.Char (isAlphaNum)
import Import
import System.Directory (canonicalizePath, doesFileExist)
import System.FilePath (pathSeparator)

-- | Serve a locale JSON file, ignoring the cache-busting hash segment.
getLocalesFileR :: Text -> Texts -> Handler TypedContent
getLocalesFileR _hash pieces = do
  unless (not (null pieces) && all isSafePiece pieces) notFound
  app <- getYesod
  let staticDir = appStaticDir (appSettings app)
      localesDir = staticDir </> "locales"
      path = foldl' (</>) localesDir (map unpack pieces)
  safe <- liftIO $ isWithinDir localesDir path
  unless safe notFound
  sendFile typeJson path

isWithinDir :: FilePath -> FilePath -> IO Bool
isWithinDir dir path = do
  isFile <- doesFileExist path
  if not isFile
    then pure False
    else do
      canonDir <- canonicalizePath dir
      canonPath <- canonicalizePath path
      pure ((canonDir <> [pathSeparator]) `isPrefixOf` canonPath)

isSafePiece :: Text -> Bool
isSafePiece t = not (null t) && headMay t /= Just '.' && all isSafeChar t
  where
    isSafeChar :: Char -> Bool
    isSafeChar c = isAlphaNum c || c `elem` ("-_." :: String)