PTQ-0.0.6: src/CGI.hs
-----------------------------------------------------------------------------
-- |
-- Module : CGI
-- Copyright : Copyright (c) 2005,2006 Minero Aoki
-- License : LGPL (see COPYING)
--
-- Maintainer: masahiro.sakai@gmail.com
-- Stability : experimental
-- Portability : non-portable
{-# LANGUAGE CPP #-}
--
-- $Id: CGI.hs,v 1.2 2006/05/14 17:29:22 aamine Exp $
--
-- Copyright (c) 2005,2006 Minero Aoki
--
-- This program is free software.
-- You can distribute/modify this program under the terms of
-- the GNU LGPL, Lesser General Public License version 2.1.
-- For details of the GNU LGPL, see the file "COPYING".
--
module CGI
(runCGI,
HTTPRequest, varExist, lookupVar, lookupVars,
HTTPResponse(..), textContentType) where
import URLEncoding
import Data.Maybe
import Control.Monad
import System.IO
import System.IO.Error
import System.Environment
runCGI :: (HTTPRequest -> IO HTTPResponse) -> IO ()
runCGI f = do hSetBinaryMode stdin True
-- hSetBinaryMode stdout True
hSetEncoding stdout utf8
hSetNewlineMode stdout noNewlineTranslation
input <- getContents
env <- cgiEnvs
res@(HTTPResponse ctype body) <- f (parseCGIRequest env input)
putStr $ "Content-Type: " ++ ctype ++ "\r\n"
putStr "\r\n"
putStr body
cgiEnvs = return . catMaybes =<< mapM mGetEnvPair names
where
mGetEnvPair :: String -> IO (Maybe (String, String))
mGetEnvPair name =
catchIOError (return . Just . (,) name =<< getEnv name)
(const $ return Nothing)
names = [ "SERVER_NAME", "SERVER_PORT",
"SERVER_SOFTWARE", "SERVER_PROTOCOL",
"GATEWAY_INTERFACE", "SCRIPT_NAME", "REQUEST_METHOD",
"PATH_INFO", "PATH_TRANSLATED",
"CONTENT_TYPE", "CONTENT_LENGTH", "QUERY_STRING",
"HTTP_COOKIE", "HTTP_ACCEPT",
"REMOTE_HOST", "REMOTE_ADDR", "REMOTE_USER",
"AUTH_TYPE", "HTTPS" ]
data HTTPRequest = HTTPRequest { params :: [(String, String)] }
parseCGIRequest env input =
case method of
"GET" -> parseGET env
"POST" -> parsePOST env input
_ -> parseUnknown
where
method = getenv "REQUEST_METHOD" env
getenv key env = fromMaybe "" $ lookup key env
parseGET env = HTTPRequest (parseQueryString $ getenv "QUERY_STRING" env)
parsePOST env input = HTTPRequest (parseQueryString $ input)
-- FIXME
parseUnknown = HTTPRequest []
parseQueryString = map splitKV . splitQueryString
splitQueryString = splitBy (\c -> c == ';' || c == '&')
splitKV kv = case break (== '=') kv of
(k, ('=':v)) -> (decodeWord k, decodeWord v)
(k, "") -> (decodeWord k, "")
decodeWord = urldecode . decodePlus
decodePlus = map (\c -> if c == '+' then ' ' else c)
splitBy :: (Char -> Bool) -> String -> [String]
splitBy _ [] = []
splitBy f str = word : splitBy f cont
where
(word, cont') = break f str
cont = case cont' of
[] -> ""
(c:cs) -> cs
varExist :: String -> HTTPRequest -> Bool
varExist key = isJust . lookupVar key
lookupVar :: String -> HTTPRequest -> Maybe String
lookupVar key = lookup key . params
lookupVars :: String -> HTTPRequest -> [String]
lookupVars key = lookupAll key . params
lookupAll :: Eq a => a -> [(a,b)] -> [b]
lookupAll key = map snd . filter ((== key) . fst)
data HTTPResponse = HTTPResponse {
resContentType :: String,
resBody :: String
}
textContentType typ encoding = concat [typ, "; charset=\"", encoding, "\""]