Network/HTTP/Cookie.hs

-----------------------------------------------------------------------------
-- |
-- Module : Network.HTTP.Cookie
-- Copyright : See LICENSE file
-- License : BSD
-- 
-- Maintainer : Ganesh Sittampalam <http@projects.haskell.org>
-- Stability : experimental
-- Portability : non-portable (not tested)
--
-- This module provides the data types and functions for working with HTTP cookies.
-- Right now, it contains mostly functionality needed by 'Network.Browser'.
-- 
-----------------------------------------------------------------------------
module Network.HTTP.Cookie
 ( Cookie(..)
 , cookieMatch -- :: (String,String) -> Cookie -> Bool

 -- functions for translating cookies and headers.
 , cookiesToHeader -- :: [Cookie] -> Header
 , processCookieHeaders -- :: String -> [Header] -> ([String], [Cookie])
 ) where

import Network.HTTP.Headers

import Data.Char
import Data.List
import Data.Maybe

import Text.ParserCombinators.Parsec
 ( Parser, char, many, many1, satisfy, parse, option, try
 , (<|>), sepBy1
 )

------------------------------------------------------------------
----------------------- Cookie Stuff -----------------------------
------------------------------------------------------------------

-- | @Cookie@ is the Haskell representation of HTTP cookie values.
-- See its relevant specs for authoritative details.
data Cookie 
 = MkCookie 
 { ckDomain :: String
 , ckName :: String
 , ckValue :: String
 , ckPath :: Maybe String
 , ckComment :: Maybe String
 , ckVersion :: Maybe String
 }
 deriving(Show,Read)

instance Eq Cookie where
 a == b = ckDomain a == ckDomain b 
 && ckName a == ckName b 
 && ckPath a == ckPath b

-- | @cookieToHeaders ck@ serialises @Cookie@s to an HTTP request header.
cookiesToHeader :: [Cookie] -> Header
cookiesToHeader cs = Header HdrCookie (mkCookieHeaderValue cs)

-- | Turn a list of cookies into a key=value pair list, separated by
-- semicolons.
mkCookieHeaderValue :: [Cookie] -> String
mkCookieHeaderValue = intercalate "; " . map mkCookieHeaderValue1
 where
 mkCookieHeaderValue1 c = ckName c ++ "=" ++ ckValue c

-- | @cookieMatch (domain,path) ck@ performs the standard cookie
-- match wrt the given domain and path. 
cookieMatch :: (String, String) -> Cookie -> Bool
cookieMatch (dom,path) ck =
 ckDomain ck `isSuffixOf` dom &&
 case ckPath ck of
 Nothing -> True
 Just p -> p `isPrefixOf` path


-- | @processCookieHeaders dom hdrs@ 
processCookieHeaders :: String -> [Header] -> ([String], [Cookie])
processCookieHeaders dom hdrs = foldr (headerToCookies dom) ([],[]) hdrs

-- | @headerToCookies dom hdr acc@ 
headerToCookies :: String -> Header -> ([String], [Cookie]) -> ([String], [Cookie])
headerToCookies dom (Header HdrSetCookie val) (accErr, accCookie) = 
 case parse cookies "" val of
 Left{} -> (val:accErr, accCookie)
 Right x -> (accErr, x ++ accCookie)
 where
 cookies :: Parser [Cookie]
 cookies = sepBy1 cookie (char ',')

 cookie :: Parser Cookie
 cookie =
 do name <- word
 _ <- spaces_l
 _ <- char '='
 _ <- spaces_l
 val1 <- cvalue
 args <- cdetail
 return $ mkCookie name val1 args

 cvalue :: Parser String
 
 spaces_l = many (satisfy isSpace)

 cvalue = quotedstring <|> many1 (satisfy $ not . (==';')) <|> return ""
 
 -- all keys in the result list MUST be in lower case
 cdetail :: Parser [(String,String)]
 cdetail = many $
 try (do _ <- spaces_l
 _ <- char ';'
 _ <- spaces_l
 s1 <- word
 _ <- spaces_l
 s2 <- option "" (char '=' >> spaces_l >> cvalue)
 return (map toLower s1,s2)
 )

 mkCookie :: String -> String -> [(String,String)] -> Cookie
 mkCookie nm cval more = 
	 MkCookie { ckName = nm
 , ckValue = cval
 , ckDomain = map toLower (fromMaybe dom (lookup "domain" more))
 , ckPath = lookup "path" more
 , ckVersion = lookup "version" more
 , ckComment = lookup "comment" more
 }
headerToCookies _ _ acc = acc

 


word, quotedstring :: Parser String
quotedstring =
 do _ <- char '"' -- "
 str <- many (satisfy $ not . (=='"'))
 _ <- char '"'
 return str

word = many1 (satisfy (\x -> isAlphaNum x || x=='_' || x=='.' || x=='-' || x==':'))

AltStyle によって変換されたページ (->オリジナル) /