module Network.Protocol.Cookie
(
Cookie (Cookie)
, emptyShort
, empty
, cookie
, setCookie
, CookieShort (CookieShort)
, toShort
, name
, value
, comment
, commentURL
, discard
, domain
, maxAge
, expires
, path
, port
, secure
, version
, Cookies
, unCookies
, gather
, pickCookie
)
where
import Prelude hiding ((.), id)
import Control.Category
import Control.Monad (join)
import Data.Label
import qualified Data.Label.Abstract as A
import Data.Maybe
import Data.Char
import Data.Monoid
import Safe
import Data.List
import Network.Protocol.Uri.Query
import qualified Data.Map as M
data Cookie =
Cookie
{ _name :: String
, _value :: String
, _comment :: Maybe String
, _commentURL :: Maybe String
, _discard :: Maybe String
, _domain :: Maybe String
, _maxAge :: Maybe Int
, _expires :: Maybe String
, _path :: Maybe String
, _port :: [Int]
, _secure :: Bool
, _version :: Int
} deriving(Show, Read, Eq)
$(mkLabels [''Cookie])
data CookieShort =
CookieShort
{ _s_name :: String
, _s_value :: String
} deriving (Show, Eq)
$(mkLabels [''CookieShort])
toShort :: Cookie -> CookieShort
toShort c = CookieShort (get name c) (get value c)
emptyShort :: CookieShort
emptyShort = CookieShort "" ""
empty :: Cookie
empty = Cookie "" "" Nothing Nothing Nothing Nothing Nothing Nothing Nothing [] False 0
showSetCookie :: Cookie -> ShowS
showSetCookie c =
pair (get name c) (get value c)
. opt "comment" (get comment c)
. opt "commentURL" (get commentURL c)
. opt "discard" (get discard c)
. opt "domain" (get domain c)
. opt "maxAge" (fmap show $ get maxAge c)
. opt "expires" (get expires c)
. opt "path" (get path c)
. lst "port" (map show $ get port c)
. bool "secure" (get secure c)
. opt "version" (optval $ get version c)
where
attr a = showString a
val v = showString ("=" ++ v)
end = showString "; "
single a = attr a . end
pair a v = attr a . val v . end
opt a = maybe id (pair a)
lst _ [] = id
lst a xs = pair a $ intercalate "," xs
bool _ False = id
bool a True = single a
optval 0 = Nothing
optval i = Just (show i)
(!$) a b = b $ a
infixr 0 !$
parseSetCookie :: String -> Cookie
parseSetCookie s =
let p = fw (keyValues ";" "=") s
in Cookie
{ _name = p !$ headMay >>> fmap fst >>> fromMaybe ""
, _value = p !$ headMay >>> fmap snd >>> fromMaybe ""
, _comment = p !$ lookup "comment"
, _commentURL = p !$ lookup "commentURL"
, _discard = p !$ lookup "discard"
, _domain = p !$ lookup "domain"
, _maxAge = p !$ lookup "maxAge" >>> fmap readMay >>> join
, _expires = p !$ lookup "expires"
, _path = p !$ lookup "path"
, _port = p !$ lookup "port" >>> maybe [] (readDef [1])
, _secure = p !$ lookup "secure" >>> maybe False (const True)
, _version = p !$ lookup "version" >>> maybe 1 (readDef 1)
}
showCookie :: CookieShort -> String
showCookie c = _s_name c ++ "=" ++ _s_value c
parseCookie :: String -> CookieShort
parseCookie s =
let p = fw (values "=") s
in emptyShort
{ _s_name = atDef "" p 0
, _s_value = atDef "" p 1
}
setCookie :: Bijection (->) String Cookie
setCookie = Bij parseSetCookie (flip showSetCookie "")
cookie :: Bijection (->) String [CookieShort]
cookie = (A.liftBij $ Bij parseCookie showCookie) . values ";"
data Cookies = Cookies { _unCookies :: M.Map String Cookie }
deriving (Eq, Show, Read)
emptyC = Cookies M.empty
instance Monoid Cookies where
mempty = emptyC
mappend (Cookies a) (Cookies b) = Cookies (a`mappend`b)
$(mkLabels [''Cookies])
pickCookie :: String -> Cookies :-> Maybe Cookie
pickCookie n = lookupL (map toLower n) . unCookies where
lookupL k = lens (M.lookup k) (flip M.alter k . const)
fromList :: [Cookie] -> Cookies
fromList = Cookies . M.fromList . map (\a -> (map toLower $ get name a, a))
toList :: Cookies -> [Cookie]
toList = map snd . M.toList . get unCookies
gather :: Bijection (->) [Cookie] Cookies
gather = Bij fromList toList