{-# LANGUAGE OverloadedStrings #-}
module Network.Gopher.Util.Gophermap (
parseGophermap
, GophermapEntry (..)
, Gophermap (..)
, gophermapToDirectoryResponse
) where
import Prelude hiding (take, takeWhile)
import Network.Gopher.Types
import Network.Gopher.Util
import Control.Applicative (many, (<$>), (<|>))
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Reader (ask)
import Data.Attoparsec.ByteString
import Data.ByteString (ByteString (), append, empty, pack, singleton, unpack)
import Data.Maybe (fromMaybe)
import qualified Data.String.UTF8 as U
import Data.Word (Word8 ())
gophermapToDirectoryResponse :: Gophermap -> GopherResponse
gophermapToDirectoryResponse entries =
MenuResponse (map gophermapEntryToMenuItem entries)
gophermapEntryToMenuItem :: GophermapEntry -> GopherMenuItem
gophermapEntryToMenuItem (GophermapEntry ft desc path host port) =
Item ft desc (fromMaybe (uDecode desc) path) host port
fileTypeChars :: [Char]
fileTypeChars = "0123456789+TgIih"
data GophermapEntry = GophermapEntry
GopherFileType ByteString
(Maybe FilePath) (Maybe ByteString) (Maybe Integer)
deriving (Show, Eq)
type Gophermap = [GophermapEntry]
parseGophermap :: Parser Gophermap
parseGophermap = many parseGophermapLine
if' :: Bool -> a -> a -> a
if' True a _ = a
if' False _ b = b
gopherFileTypeChar :: Parser Word8
gopherFileTypeChar = satisfy (inClass fileTypeChars)
parseGophermapLine :: Parser GophermapEntry
parseGophermapLine = emptyGophermapline <|>
regularGophermapline <|>
infoGophermapline <|>
gophermaplineWithoutFileTypeChar
infoGophermapline :: Parser GophermapEntry
infoGophermapline = do
text <- takeWhile (notInClass "\t\r\n")
endOfLine'
return $ GophermapEntry InfoLine
text
Nothing
Nothing
Nothing
regularGophermapline :: Parser GophermapEntry
regularGophermapline = do
fileTypeChar <- gopherFileTypeChar
text <- itemValue
pathString <- optionalValue
host <- optionalValue
portString <- optionalValue
endOfLine'
return $ GophermapEntry (charToFileType fileTypeChar)
text
(santinizeIfNotUrl . fst . U.decode . unpack <$> pathString)
host
(byteStringToPort <$> portString)
emptyGophermapline :: Parser GophermapEntry
emptyGophermapline = do
endOfLine'
return emptyInfoLine
where emptyInfoLine = GophermapEntry InfoLine (pack []) Nothing Nothing Nothing
gophermaplineWithoutFileTypeChar :: Parser GophermapEntry
gophermaplineWithoutFileTypeChar = do
text <- itemValue
pathString <- optionalValue
host <- optionalValue
portString <- optionalValue
endOfLine'
return $ GophermapEntry InfoLine
text
(santinizeIfNotUrl . fst . U.decode . unpack <$> pathString)
host
(byteStringToPort <$> portString)
byteStringToPort :: ByteString -> Integer
byteStringToPort s = fromIntegral . read . fst . U.decode . unpack $ s
optionalValue :: Parser (Maybe ByteString)
optionalValue = option Nothing $ do
satisfy (inClass "\t")
Just <$> itemValue
itemValue :: Parser ByteString
itemValue = takeTill (inClass "\t\r\n")
endOfLine' :: Parser ()
endOfLine' = (word8 10 >> return ()) <|> (string "\r\n" >> return ())