module HTML(HtmlExp(..),HtmlPage(..),PageParam(..),
HtmlForm(..),FormParam(..),CookieParam(..),
CgiRef,idOfCgiRef,CgiEnv,HtmlHandler,
HtmlElem,Form,
defaultEncoding, defaultBackground,
form,standardForm,answerText,
cookieForm,getCookies,
page,standardPage,pageEnc,pageCSS,addPageParam,
formEnc,formCSS,addFormParam,
htxt,htxts,hempty,nbsp,h1,h2,h3,h4,h5,
par,emphasize,bold,italic,code,
center,blink,teletype,pre,verbatim,address,href,anchor,
ulist,olist,litem,dlist,table,headedTable,addHeadings,
hrule,breakline,image,
styleSheet,style,textstyle,blockstyle,inline,block,
redirect,expires,
button,resetbutton,imageButton,coordinates,
textfield,password,textarea,checkbox,checkedbox,
radio_main,radio_main_off,radio_other,
selection,selectionInitial,multipleSelection,
hiddenfield,htmlQuote,addAttr,addAttrs,
showHtmlExps,showHtmlExp,showHtmlPage,
showHtmlDoc,showHtmlDocCSS,
runFormServerWithKey,runFormServerWithKeyAndFormParams,
intForm,intFormMain,
getUrlParameter,urlencoded2string,string2urlencoded,
showLatexExps,showLatexExp,showLatexDoc,showLatexDocs,
showLatexDocsWithPackages,showLatexDocWithPackages,
germanLatexDoc,addSound,addCookies) where
import System
import Char
import List
import Time
import HtmlCgi
import NamedSocket
import ReadNumeric(readNat,readHex)
import ReadShowTerm(showQTerm,readsQTerm)
import Unsafe(showAnyQExpression)
import Distribution(installDir)
import IO
import Profile
infixl 0 `addAttr`
infixl 0 `addAttrs`
infixl 0 `addPageParam`
infixl 0 `addFormParam`
defaultEncoding = "iso-8859-1"
defaultBackground = ("bgcolor","#ffffff")
data CgiRef = CgiRef String
idOfCgiRef :: CgiRef -> String
idOfCgiRef (CgiRef i) = i
type CgiEnv = CgiRef -> String
type HtmlHandler = CgiEnv -> IO HtmlForm
data HtmlExp =
HtmlText String
| HtmlStruct String [(String,String)] [HtmlExp]
| HtmlCRef HtmlExp CgiRef
| HtmlEvent HtmlExp HtmlHandler
HtmlElem :: String -> [(String,String)] -> HtmlExp
HtmlElem tag attrs = HtmlStruct tag attrs []
data HtmlForm =
HtmlForm String [FormParam] [HtmlExp]
| HtmlAnswer String String
data FormParam = FormCookie String String [CookieParam]
| FormCSS String
| FormJScript String
| FormOnSubmit String
| FormTarget String
| FormEnc String
| HeadInclude HtmlExp
| MultipleHandlers
| BodyAttr (String,String)
formEnc :: String -> FormParam
formEnc enc = FormEnc enc
formCSS :: String -> FormParam
formCSS css = FormCSS css
formCookie :: (String,String) -> FormParam
formCookie (n,v) = FormCookie n v []
data CookieParam = CookieExpire ClockTime
| CookieDomain String
| CookiePath String
| CookieSecure
form :: String -> [HtmlExp] -> HtmlForm
form title hexps = HtmlForm title [BodyAttr defaultBackground] hexps
Form :: String -> [HtmlExp] -> HtmlForm
Form = form
standardForm :: String -> [HtmlExp] -> HtmlForm
standardForm title hexps = form title (h1 [htxt title] : hexps)
cookieForm :: String -> [(String,String)] -> [HtmlExp] -> HtmlForm
cookieForm t cs he = HtmlForm t (map (\(n,v)->FormCookie n v []) cs) he
addCookies :: [(String,String)] -> HtmlForm -> HtmlForm
addCookies cs (HtmlForm t as hs) =
HtmlForm t (map (\ (n,v) -> FormCookie n v []) cs++as) hs
addCookies _ (HtmlAnswer _ _) =
error "addCookies: cannot add cookie to Html answer"
formatCookie (name,value,params) =
"Set-Cookie: " ++ name ++ "=" ++ string2urlencoded value ++
concatMap (\p->"; "++formatCookieParam p) params
formatCookieParam :: CookieParam -> String
formatCookieParam (CookieExpire e) = "expires=" ++ toCookieDateString e
formatCookieParam (CookieDomain d) = "domain=" ++ d
formatCookieParam (CookiePath p) = "path=" ++ p
formatCookieParam CookieSecure = "secure"
toCookieDateString :: ClockTime -> String
toCookieDateString time =
let (CalendarTime y mo d h mi s tz) = toUTCTime time
in (show d ++ "-" ++ shortMonths!!(mo-1) ++ "-" ++ show y ++ " " ++
toTimeString (CalendarTime y mo d h mi s tz) ++ " UTC")
where shortMonths = ["Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"]
answerText :: String -> HtmlForm
answerText = HtmlAnswer "text/plain"
addFormParam :: HtmlForm -> FormParam -> HtmlForm
addFormParam (HtmlForm title params hexps) param =
HtmlForm title (param:params) hexps
addFormParam (HtmlAnswer _ _) _ =
error "HTML.addFormParam: unable to add form parameter to general HTML Answer"
addFormParams :: HtmlForm -> [FormParam] -> HtmlForm
addFormParams hform [] = hform
addFormParams hform (fp:fps) = addFormParams (hform `addFormParam` fp) fps
redirect :: Int -> String -> HtmlForm -> HtmlForm
redirect secs url hform =
hform `addFormParam`
HeadInclude (HtmlStruct "meta" [("http-equiv","refresh"),
("content",show secs++"; URL="++url)] [])
expires :: Int -> HtmlForm -> HtmlForm
expires secs hform =
hform `addFormParam`
HeadInclude (HtmlStruct "meta" [("http-equiv","expires"),
("content",show secs)] [])
addSound :: String -> Bool -> HtmlForm -> HtmlForm
addSound soundfile loop (HtmlForm title params conts) =
HtmlForm title
(HeadInclude
(HtmlStruct "bgsound"
[("src",soundfile),
("loop",if loop then "infinite" else "1")] []):params)
(HtmlStruct "embed"
((if loop then [("loop","true")] else []) ++
[("src",soundfile),("autostart","true"), ("hidden","true"),
("height","0"), ("width","0")]) []:
conts)
addSound _ _ (HtmlAnswer _ _)
= error "HTML.addSound: unable to add sound to general HTML Answer"
data HtmlPage = HtmlPage String [PageParam] [HtmlExp]
data PageParam = PageEnc String
| PageCSS String
| PageJScript String
pageEnc :: String -> PageParam
pageEnc enc = PageEnc enc
pageCSS :: String -> PageParam
pageCSS css = PageCSS css
page :: String -> [HtmlExp] -> HtmlPage
page title hexps = HtmlPage title [PageEnc defaultEncoding] hexps
standardPage :: String -> [HtmlExp] -> HtmlPage
standardPage title hexps = page title (h1 [htxt title] : hexps)
addPageParam :: HtmlPage -> PageParam -> HtmlPage
addPageParam (HtmlPage title params hexps) param =
HtmlPage title (param:params) hexps
htxt :: String -> HtmlExp
htxt s = HtmlText (htmlQuote s)
htxts :: [String] -> [HtmlExp]
htxts = map htxt
hempty :: HtmlExp
hempty = HtmlText ""
nbsp :: HtmlExp
nbsp = HtmlText " "
h1 :: [HtmlExp] -> HtmlExp
h1 hexps = HtmlStruct "h1" [] hexps
h2 :: [HtmlExp] -> HtmlExp
h2 hexps = HtmlStruct "h2" [] hexps
h3 :: [HtmlExp] -> HtmlExp
h3 hexps = HtmlStruct "h3" [] hexps
h4 :: [HtmlExp] -> HtmlExp
h4 hexps = HtmlStruct "h4" [] hexps
h5 :: [HtmlExp] -> HtmlExp
h5 hexps = HtmlStruct "h5" [] hexps
par :: [HtmlExp] -> HtmlExp
par hexps = HtmlStruct "p" [] hexps
emphasize :: [HtmlExp] -> HtmlExp
emphasize hexps = HtmlStruct "em" [] hexps
bold :: [HtmlExp] -> HtmlExp
bold hexps = HtmlStruct "b" [] hexps
italic :: [HtmlExp] -> HtmlExp
italic hexps = HtmlStruct "i" [] hexps
code :: [HtmlExp] -> HtmlExp
code hexps = HtmlStruct "code" [] hexps
center :: [HtmlExp] -> HtmlExp
center hexps = HtmlStruct "center" [] hexps
blink :: [HtmlExp] -> HtmlExp
blink hexps = HtmlStruct "blink" [] hexps
teletype :: [HtmlExp] -> HtmlExp
teletype hexps = HtmlStruct "tt" [] hexps
pre :: [HtmlExp] -> HtmlExp
pre hexps = HtmlStruct "pre" [] hexps
verbatim :: String -> HtmlExp
verbatim s = HtmlStruct "pre" [] [HtmlText (htmlQuote s)]
address :: [HtmlExp] -> HtmlExp
address hexps = HtmlStruct "address" [] hexps
href :: String -> [HtmlExp] -> HtmlExp
href ref hexps = HtmlStruct "a" [("href",ref)] hexps
anchor :: String -> [HtmlExp] -> HtmlExp
anchor anc hexps = HtmlStruct "a" [("name",anc)] hexps
ulist :: [[HtmlExp]] -> HtmlExp
ulist items = HtmlStruct "ul" [] (map litem items)
olist :: [[HtmlExp]] -> HtmlExp
olist items = HtmlStruct "ol" [] (map litem items)
litem hexps = HtmlStruct "li" [] hexps
dlist :: [([HtmlExp],[HtmlExp])] -> HtmlExp
dlist items = HtmlStruct "dl" [] (concatMap ditem items)
where
ditem (hexps1,hexps2) = [HtmlStruct "dt" [] hexps1,
HtmlStruct "dd" [] hexps2]
table :: [[[HtmlExp]]] -> HtmlExp
table items = HtmlStruct "table" []
(map (\row->HtmlStruct "tr" []
(map (\item -> HtmlStruct "td" [] item) row)) items)
headedTable :: [[[HtmlExp]]] -> HtmlExp
headedTable = withinTable . table
where
withinTable (HtmlStruct "table" attrs (HtmlStruct "tr" rowAttrs row:rows)) =
HtmlStruct "table" attrs
(HtmlStruct "tr" rowAttrs (map addTh row):rows)
addTh x = case x of
(HtmlStruct "td" attrs conts) -> HtmlStruct "th" attrs conts
other -> other
addHeadings :: HtmlExp -> [[HtmlExp]] -> HtmlExp
addHeadings htable headings = case htable of
HtmlStruct "table" attrs rows ->
HtmlStruct "table" attrs
(HtmlStruct "tr" [] (map (\item->HtmlStruct "th" [] item) headings):rows)
_ -> htable
hrule :: HtmlExp
hrule = HtmlStruct "hr" [] []
breakline :: HtmlExp
breakline = HtmlStruct "br" [] []
image :: String -> String -> HtmlExp
image src alt = HtmlStruct "img" [("src",src),("alt",htmlQuote alt)] []
styleSheet :: String -> HtmlExp
styleSheet css = HtmlStruct "style" [("type","text/css")] [HtmlText css]
style :: String -> [HtmlExp] -> HtmlExp
style st hexps = HtmlStruct "span" [("class",st)] hexps
textstyle :: String -> String -> HtmlExp
textstyle st txt = HtmlStruct "span" [("class",st)] [htxt txt]
blockstyle :: String -> [HtmlExp] -> HtmlExp
blockstyle st hexps = HtmlStruct "div" [("class",st)] hexps
inline :: [HtmlExp] -> HtmlExp
inline hexps = HtmlStruct "span" [] hexps
block :: [HtmlExp] -> HtmlExp
block hexps = HtmlStruct "div" [] hexps
button :: String -> HtmlHandler -> HtmlExp
button label handler =
HtmlEvent
(HtmlStruct "input" [("type","submit"),("name","EVENT"),
("value",htmlQuote label)] [])
handler
resetbutton :: String -> HtmlExp
resetbutton label =
HtmlStruct "input" [("type","reset"),("value",htmlQuote label)] []
imageButton :: String -> HtmlHandler -> HtmlExp
imageButton src handler
= HtmlEvent
(HtmlStruct "input" [("type","image"),("name","EVENT"),("src",src)] [])
handler
textfield :: CgiRef -> String -> HtmlExp
textfield cref contents
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "input" [("type","text"),("name",ref),
("value",contents)] [])
cref
where ref free
password :: CgiRef -> HtmlExp
password cref
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "input" [("type","password"),("name",ref)] [])
cref
where
ref free
textarea :: CgiRef -> (Int,Int) -> String -> HtmlExp
textarea cref (height,width) contents
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "textarea" [("name",ref),
("rows",show height),("cols",show width)]
[htxt contents])
cref
where
ref free
checkbox :: CgiRef -> String -> HtmlExp
checkbox cref value
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "input" [("type","checkbox"),("name",ref),
("value",htmlQuote value)] [])
cref
where
ref free
checkedbox :: CgiRef -> String -> HtmlExp
checkedbox cref value
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "input" [("type","checkbox"),("name",ref),
("value",htmlQuote value),("checked","checked")] [])
cref
where
ref free
radio_main :: CgiRef -> String -> HtmlExp
radio_main cref value
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "input" [("type","radio"),("name",ref),
("value",htmlQuote value),("checked","yes")] [])
cref
where
ref free
radio_main_off :: CgiRef -> String -> HtmlExp
radio_main_off cref value
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "input" [("type","radio"),("name",ref),
("value",htmlQuote value)] [])
cref
where
ref free
radio_other :: CgiRef -> String -> HtmlExp
radio_other cref value
| cref =:= CgiRef ref
= HtmlStruct "input"
[("type","radio"),("name",ref),("value",htmlQuote value)] []
where
ref free
selection :: CgiRef -> [(String,String)] -> HtmlExp
selection cref menue
| cref =:= CgiRef ref
= HtmlCRef
(HtmlStruct "select" [("name",ref)]
((concat . map (\(n,v)->[HtmlStruct "option" [("value",v)] [htxt n]]))
menue))
cref
where
ref free
selectionInitial :: CgiRef -> [(String,String)] -> Int -> HtmlExp
selectionInitial cref sellist sel
| cref =:= CgiRef ref
= HtmlCRef (HtmlStruct "select" [("name",ref)] (selOption sellist sel))
cref
where
ref free
selOption [] _ = []
selOption ((n,v):nvs) i =
HtmlStruct "option"
([("value",v)] ++ if i==0 then [("selected","yes")] else [])
[htxt n] : selOption nvs (i-1)
multipleSelection :: CgiRef -> [(String,String,Bool)] -> HtmlExp
multipleSelection cref sellist
| cref =:= CgiRef ref
= HtmlCRef (HtmlStruct "select" [("name",ref),("multiple","yes")]
(map selOption sellist))
cref
where
ref free
selOption (n,v,flag) =
HtmlStruct "option"
([("value",v)] ++ if flag then [("selected","yes")] else [])
[htxt n]
hiddenfield :: String -> String -> HtmlExp
hiddenfield name value =
HtmlStruct "input" [("type","hidden"),("name",name),("value",value)] []
htmlQuote :: String -> String
htmlQuote [] = []
htmlQuote (c:cs) | c=='<' = "<" ++ htmlQuote cs
| c=='>' = ">" ++ htmlQuote cs
| c=='&' = "&" ++ htmlQuote cs
| c=='"' = """ ++ htmlQuote cs
| oc==228 = "ä" ++ htmlQuote cs
| oc==246 = "ö" ++ htmlQuote cs
| oc==252 = "ü" ++ htmlQuote cs
| oc==196 = "Ä" ++ htmlQuote cs
| oc==214 = "Ö" ++ htmlQuote cs
| oc==220 = "Ü" ++ htmlQuote cs
| oc==223 = "ß"++ htmlQuote cs
| otherwise = c : htmlQuote cs
where oc = ord c
addAttr :: HtmlExp -> (String,String) -> HtmlExp
addAttr hexp attr = addAttrs hexp [attr]
addAttrs :: HtmlExp -> [(String,String)] -> HtmlExp
addAttrs (HtmlText s) _ = HtmlText s
addAttrs (HtmlStruct tag attrs hexps) newattrs =
HtmlStruct tag (attrs++newattrs) hexps
addAttrs (HtmlEvent hexp handler) attrs =
HtmlEvent (addAttrs hexp attrs) handler
addAttrs (HtmlCRef hexp cref) attrs =
HtmlCRef (addAttrs hexp attrs) cref
type ShowS = String -> String
showString s = (s++)
showChar c = (c:)
nl = showChar '\n'
concatS [] = id
concatS xs@(_:_) = foldr1 (\ f g -> f . g) xs
showHtmlExps :: [HtmlExp] -> String
showHtmlExps hexps = showsHtmlExps hexps ""
showsHtmlExps :: [HtmlExp] -> ShowS
showsHtmlExps [] = id
showsHtmlExps (he:hes) = showsWithLnPrefix he . showsHtmlExps hes
where
showsWithLnPrefix hexp = let s = getText hexp
in if s/="" && isSpace (head s)
then nl . showString (tail s)
else showsHtmlExp hexp
getText :: HtmlExp -> String
getText (HtmlText s) = s
getText (HtmlStruct _ _ _) = ""
getText (HtmlEvent he _) = getText he
getText (HtmlCRef he _) = getText he
getTag :: HtmlExp -> String
getTag (HtmlText _) = ""
getTag (HtmlStruct tag _ _) = tag
getTag (HtmlEvent he _) = getTag he
getTag (HtmlCRef he _) = getTag he
tagWithLn t = t/="" &&
t `elem` ["br","p","li","ul","ol","dl","dt","dd","hr",
"h1","h2","h3","h4","h5","h6",
"html","title","head","body","form","table","tr","td"]
showHtmlExp :: HtmlExp -> String
showHtmlExp hexp = showsHtmlExp hexp ""
showsHtmlExp :: HtmlExp -> ShowS
showsHtmlExp (HtmlText s) = showString s
showsHtmlExp (HtmlStruct tag attrs hexps) =
let maybeLn = if tagWithLn tag then nl else id
in maybeLn .
(if null hexps && tag/="script"
then showsHtmlOpenTag tag attrs "/>"
else showsHtmlOpenTag tag attrs ">" . maybeLn . showExps hexps .
maybeLn . showString "</" . showString tag . showChar '>'
) . maybeLn
where
showExps = if tag=="pre" then concatS . map showsHtmlExp else showsHtmlExps
showsHtmlExp (HtmlEvent hexp _) = showsHtmlExp hexp
showsHtmlExp (HtmlCRef hexp _) = showsHtmlExp hexp
showsHtmlOpenTag :: String -> [(String,String)] -> String -> ShowS
showsHtmlOpenTag tag attrs close =
showChar '<' . showString tag .
concatS (map attr2string attrs) . showString close
where
attr2string (attr,value) = showChar ' ' . showString attr .
showString "=\"" . encodeQuotes value . showChar '"'
encodeQuotes [] = id
encodeQuotes (c:cs) | c=='"' = showString """ . encodeQuotes cs
| otherwise = showChar c . encodeQuotes cs
showHtmlDoc :: String -> [HtmlExp] -> String
showHtmlDoc title html = showHtmlPage (page title html)
showHtmlDocCSS :: String -> String -> [HtmlExp] -> String
showHtmlDocCSS title css html =
showHtmlPage (page title html `addPageParam` pageCSS css)
showHtmlPage :: HtmlPage -> String
showHtmlPage (HtmlPage title params html) =
htmlPrelude ++
showHtmlExp (HtmlStruct "html" htmlTagAttrs
[HtmlStruct "head" []
([HtmlStruct "title" [] [HtmlText title]] ++
concatMap param2html params),
HtmlStruct "body" [defaultBackground] html])
where
param2html (PageEnc enc) =
[HtmlStruct "meta" [("http-equiv","Content-Type"),
("content","text/html; charset="++enc)] []]
param2html (PageCSS css) =
[HtmlStruct "link" [("rel","stylesheet"),("type","text/css"),("href",css)]
[]]
param2html (PageJScript js) =
[HtmlStruct "script" [("type","text/javascript"),("src",js)] []]
htmlPrelude =
"<?xml version=\"1.0\" encoding=\""++defaultEncoding++"\"?>\n"++
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"++
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
htmlTagAttrs = [("xmlns","http://www.w3.org/1999/xhtml"),
("xml:lang","en"),("lang","en")]
getUrlParameter :: IO String
getUrlParameter = getEnviron "QUERY_STRING"
urlencoded2string :: String -> String
urlencoded2string [] = []
urlencoded2string (c:cs)
| c == '+' = ' ' : urlencoded2string cs
| c == '%' = chr (maybe 0 fst (readHex (take 2 cs)))
: urlencoded2string (drop 2 cs)
| otherwise = c : urlencoded2string cs
string2urlencoded :: String -> String
string2urlencoded [] = []
string2urlencoded (c:cs)
| isAlphaNum c = c : string2urlencoded cs
| c == ' ' = '+' : string2urlencoded cs
| otherwise = let oc = ord c
in '%' : int2hex(oc `div` 16) : int2hex(oc `mod` 16) : string2urlencoded cs
where
int2hex i = if i<10 then chr (ord '0' + i)
else chr (ord 'A' + i - 10)
getCookies :: IO [(String,String)]
getCookies =
do cookiestring <- getEnviron "HTTP_COOKIE"
return $ parseCookies cookiestring
parseCookies :: String -> [(String,String)]
parseCookies str = if str=="" then [] else
let (c1,cs) = break (==';') str
in parseCookie c1 :
parseCookies (dropWhile (==' ') (if cs=="" then "" else tail cs))
where
parseCookie s = let (name,evalue) = break (=='=') s in
(name,if evalue=="" then "" else urlencoded2string (tail evalue))
coordinates :: CgiEnv -> Maybe (Int,Int)
coordinates env = let x = env (CgiRef "x")
y = env (CgiRef "y")
in if x/="" && y/=""
then Just (tryReadNat 0 x, tryReadNat 0 y)
else Nothing
runFormServerWithKey :: String -> String -> IO HtmlForm -> IO ()
runFormServerWithKey url cgikey hformact =
runFormServerWithKeyAndFormParams url cgikey [] hformact
runFormServerWithKeyAndFormParams :: String -> String -> [FormParam]
-> IO HtmlForm -> IO ()
runFormServerWithKeyAndFormParams url cgikey formparams hformact = do
args <- getArgs
let (timeout,rargs) = stripTimeoutArg args
case rargs of
["-port",port,"-scriptkey",skey] -> startCgiServer timeout port skey
_ -> putErrLn $ "ERROR: cgi server called with illegal arguments"
where
stripTimeoutArg args = case args of
("-servertimeout":tos:rargs) ->
(tryReadNat defaultCgiServerTimeout tos, rargs)
_ -> (defaultCgiServerTimeout,args)
startCgiServer timeout port scriptkey = do
time <- getClockTime
(state,htmlstring) <- computeFormInStateAndEnv True url cgikey formparams
(initialServerState time) scriptkey hformact []
putStr htmlstring
hClose stdout
if isServerStateWithoutHandlers state
then done
else
do let portname = port++scriptkey
socket <- listenOn portname
ltime <- toCalendarTime time
putErrLn $ calendarTimeToString ltime ++
": server started on port " ++ portname
registerCgiServer url portname
serveCgiMessagesForForm timeout url cgikey portname formparams
hformact socket state
defaultCgiServerTimeout = 7200000
serveCgiMessagesForForm :: Int -> String -> String -> String -> [FormParam]
-> IO HtmlForm -> Socket -> ServerState -> IO ()
serveCgiMessagesForForm servertimeout url cgikey portname
fparams initform socket = serveCgiMessages
where
serveCgiMessages state =
if isServerStateWithoutHandlers state
then do
ltime <- getLocalTime
putErrLn $ calendarTimeToString ltime ++
": terminated due to empty handler list"
unregisterCgiServer portname
sClose socket
else waitForSocketAccept socket servertimeout >>=
maybe (do
ltime <- getLocalTime
putErrLn $ calendarTimeToString ltime ++
": terminated due to timeout"
unregisterCgiServer portname
sClose socket )
(\ (rhost,hdl) ->
if rhost == "localhost" || take 8 rhost == "127.0.0."
then readCgiServerMsg hdl >>=
maybe (hClose hdl >> serveCgiMessages state)
(serveCgiMessage state hdl)
else hClose hdl >> serveCgiMessages state )
serveCgiMessage _ hdl StopCgiServer = do
hClose hdl
ltime <- getLocalTime
putErrLn $ calendarTimeToString ltime ++
": server terminated by stop message"
unregisterCgiServer portname
sClose socket
serveCgiMessage state hdl CleanServer = do
hClose hdl
nstate <- cleanOldEventHandlers state
serveCgiMessages nstate
serveCgiMessage oldstate hdl GetLoad = do
state <- cleanOldEventHandlers oldstate
serverload <- getServerLoad state
hPutStrLn hdl serverload
hClose hdl
serveCgiMessages state
serveCgiMessage oldstate hdl SketchStatus = do
state <- cleanOldEventHandlers oldstate
serverstatus <- getServerStatus state
hPutStrLn hdl serverstatus
hClose hdl
serveCgiMessages state
serveCgiMessage state hdl SketchHandlers =
reportStatus state hdl sketchEventHandler
where
sketchEventHandler (key,time,_,_,gkey) = do
ltime <- toCalendarTime time
return $ "No. " ++ show key ++ " (" ++ showGroupKey gkey ++
"), expires at: " ++
calendarTimeToString ltime ++ "\n"
serveCgiMessage state hdl ShowStatus =
reportStatus state hdl showEventHandler
where
showEventHandler (key,time,_,(_,handler),gkey) = do
ltime <- toCalendarTime time
return $ "No. " ++ show key ++ " (" ++ showGroupKey gkey ++
"), expires at " ++
calendarTimeToString ltime ++ ": " ++
showAnyQExpression handler ++ "\n"
serveCgiMessage state hdl (CgiSubmit scriptenv formenv) = do
let scriptkey = maybe "" id (lookup "SCRIPTKEY" scriptenv)
mapIO_ (\(var,val) -> if var=="SCRIPTKEY" then done
else setEnviron var val)
scriptenv
if null formenv
then serveFormInEnv state scriptkey initform []
else do
(rstate,mfe) <- getNextFormAndCgiEnv state cgikey formenv
maybe (do urlparam <- getUrlParameter
hPutStrLn hdl (noHandlerPage url urlparam)
hClose hdl
serveCgiMessages rstate)
(\ (ioform,env) -> serveFormInEnv rstate scriptkey ioform env )
mfe
where
serveFormInEnv rstate scriptkey hformact cenv = do
(nstate,htmlstring) <- computeFormInStateAndEnv False url cgikey fparams
rstate scriptkey hformact cenv
hPutStrLn hdl htmlstring
hClose hdl
serveCgiMessages nstate
reportStatus state@(stime,maxkey,ctime,ehs) hdl eh2string = do
lstime <- toCalendarTime stime
lctime <- toCalendarTime ctime
ehsstrings <- mapIO eh2string ehs
hPutStrLn hdl $ "Started at: " ++ calendarTimeToString lstime ++ "\n" ++
"Next cleanup: " ++ calendarTimeToString lctime ++
" (maxkey: " ++ show maxkey ++")\n"++
"Current event handlers:\n" ++ concat ehsstrings
hClose hdl
serveCgiMessages state
computeFormInStateAndEnv isinitform url cgikey fparams state scriptkey
hformact cenv =
catchFail tryComputeForm
(getUrlParameter >>= \uparam -> return (state,errorAsHtml uparam))
where
errorAsHtml urlparam = addHtmlContentType isinitform $ showHtmlPage $
page "Server Error"
[h1 [htxt "Error: Failure during computation"],
par [htxt "Your request cannot be processed due to a run-time error. ",
htxt "You can try to ",
href (url ++ if null urlparam then "" else '?':urlparam)
[htxt "click here"],
htxt " to try again loading the web page or inform the web ",
htxt "administrator about this problem."]]
tryComputeForm = do
cform <- hformact
let (cookiestring,hform) = extractCookies cform
(htmlstring,evhs) <- showAnswerFormInEnv isinitform url scriptkey
(addFormParams hform fparams)
(getMaxFieldNr cenv + 1)
nstate <- storeEnvHandlers state
(formWithMultipleHandlers hform)
(encodeKey cgikey)
(filter (\ (t,_) -> t/="DEFAULT" && take 6 t /= "EVENT_") cenv)
evhs
seq (isList htmlstring) done
return (nstate, cookiestring++htmlstring)
isList [] = success
isList (_:xs) = isList xs
formWithMultipleHandlers :: HtmlForm -> Bool
formWithMultipleHandlers (HtmlAnswer _ _) = False
formWithMultipleHandlers (HtmlForm _ params _) = any (==MultipleHandlers) params
encodeKey :: String -> String
encodeKey = map mapchr . reverse . filter (not . isSpace)
where
mapchr c | oc<33 || oc>126 = c
| oc<114 = chr (oc+13)
| otherwise = chr (oc-81)
where oc = ord c
putErrLn s = hPutStrLn stderr s >> hFlush stderr
getNextFormAndCgiEnv :: ServerState -> String -> [(String,String)]
-> IO (ServerState, Maybe (IO HtmlForm,[(String,String)]))
getNextFormAndCgiEnv state cgikey newcenv = do
(nstate,mbh) <- retrieveEnvHandlers state (encodeKey cgikey)
(urlencoded2string (getFormEvent "" newcenv))
return $ maybe (nstate,Nothing)
(\ (oldcenv,handler) -> let cenv = newcenv++oldcenv in
(nstate, Just (handler (cgiGetValue cenv), cenv)))
mbh
showAnswerFormInEnv :: Bool -> String -> String -> HtmlForm -> Int
-> IO (String,[(HtmlHandler,String)])
showAnswerFormInEnv withlength url key hform@(HtmlForm _ _ _) crefnr = do
(htmlstring,evhs) <- showHtmlFormInEnv url key hform crefnr
return (addHtmlContentType withlength htmlstring, evhs)
showAnswerFormInEnv _ _ _ (HtmlAnswer ctype cont) _ = do
return ("Content-Type: "++ctype++"\n\n"++cont, [])
addHtmlContentType withlength htmlstring =
(if withlength
then "Connection: close\nContent-Length: " ++
show (length htmlstring) ++ "\n"
else "") ++
"Content-Type: text/html\n\n" ++ htmlstring
showHtmlFormInEnv :: String -> String -> HtmlForm -> Int
-> IO (String,[(HtmlHandler,String)])
showHtmlFormInEnv url key (HtmlForm ftitle fparams fhexp) crefnr = do
qstr <- getEnviron "QUERY_STRING"
(title,params,hexps,firsthandler,evhs) <-
htmlForm2html (HtmlForm ftitle fparams fhexp) crefnr
return (showForm [("SCRIPTKEY",key),("DEFAULT","EVENT_"++firsthandler)]
(if qstr=="" then url else url++"?"++qstr)
(HtmlForm title params hexps),
evhs)
extractCookies :: HtmlForm -> (String,HtmlForm)
extractCookies (HtmlAnswer ctype cont) = ("",HtmlAnswer ctype cont)
extractCookies (HtmlForm title params hexp) =
let cookiestring = if cookies==[]
then ""
else "Cache-control: no-cache=\"set-cookie\"\n" ++
concatMap ((++"\n") . formatCookie) cookies
in (cookiestring, HtmlForm title otherparams hexp)
where
(cookies,otherparams) = splitFormParams params
splitFormParams [] = ([],[])
splitFormParams (fparam:fps) =
let (cs,ops) = splitFormParams fps
in case fparam of
FormCookie n v ps -> ((n,v,ps):cs,ops)
_ -> (cs,fparam:ops)
getFormEvent :: String -> [(String,String)] -> String
getFormEvent deflt [] = deflt
getFormEvent deflt ((tag,val):tvs) =
if tag == "DEFAULT" then getFormEvent (drop 6 val) tvs else
if take 6 tag == "EVENT_" then urlencoded2string (drop 6 tag)
else getFormEvent deflt tvs
getMaxFieldNr :: [(String,String)] -> Int
getMaxFieldNr [] = 0
getMaxFieldNr ((name,_):env) =
if take 6 name == "FIELD_"
then max (tryReadNat 0 (drop 6 name)) (getMaxFieldNr env)
else getMaxFieldNr env
max x y = if x>y then x else y
tryReadNat :: Int -> String -> Int
tryReadNat d s = maybe d (\(i,rs)->if null rs then i else d) (readNat s)
cgiGetValue :: [(String,String)] -> CgiRef -> String
cgiGetValue cenv (CgiRef ref) =
concat (intersperse "\n" (map snd (filter ((ref==) . fst) cenv)))
htmlForm2html :: HtmlForm -> Int
-> IO (String,[FormParam],[HtmlExp],String,[(HtmlHandler,String)])
htmlForm2html (HtmlForm title params html) crefnr = do
let (htmlwithoutcrefs,newrefnr) = numberCgiRefs html crefnr
seq newrefnr done
let (transhtml, evhs, fh) = translateHandlers htmlwithoutcrefs
return (title, params, transhtml, fh, evhs)
numberCgiRefs :: [HtmlExp] -> Int -> ([HtmlExp],Int)
numberCgiRefs [] i = ([],i)
numberCgiRefs (HtmlText s : hexps) i =
let (nhexps,j) = numberCgiRefs hexps i
in (HtmlText s : nhexps, j)
numberCgiRefs (HtmlStruct tag attrs hexps1 : hexps2) i =
let (nhexps1,j) = numberCgiRefs hexps1 i
(nhexps2,k) = numberCgiRefs hexps2 j
in (HtmlStruct tag attrs nhexps1 : nhexps2, k)
numberCgiRefs (HtmlEvent (HtmlStruct tag attrs hes) handler : hexps) i =
let (nhexps,j) = numberCgiRefs hexps i
in (HtmlEvent (HtmlStruct tag attrs hes) handler : nhexps, j)
numberCgiRefs (HtmlCRef hexp (CgiRef ref) : hexps) i
| ref =:= ("FIELD_"++show i)
= let ([nhexp],j) = numberCgiRefs [hexp] (i+1)
(nhexps,k) = numberCgiRefs hexps j
in (nhexp : nhexps, k)
translateHandlers :: [HtmlExp] -> ([HtmlExp],[(HtmlHandler,String)],String)
translateHandlers [] = ([],[],"")
translateHandlers (HtmlText s : hexps) =
let (nhexps,evhs,fh) = translateHandlers hexps
in (HtmlText s : nhexps, evhs, fh)
translateHandlers (HtmlStruct tag attrs hexps1 : hexps2) =
let (nhexps1,evhs1,fh1) = translateHandlers hexps1
(nhexps2,evhs2,fh2) = translateHandlers hexps2
in (HtmlStruct tag attrs nhexps1 : nhexps2, evhs1++evhs2,
if fh1=="" then fh2 else fh1)
translateHandlers (HtmlEvent (HtmlStruct tag attrs hes) handler : hexps) =
let (nhexps,evhs,_) = translateHandlers hexps
fh = string2urlencoded key
in (HtmlStruct tag (changeAssoc attrs "name" ("EVENT_" ++ fh)) hes : nhexps,
(handler,key):evhs, fh)
where key free
showForm cenv url (HtmlForm title params html) =
htmlPrelude ++
showHtmlExp
(HtmlStruct "html" htmlTagAttrs
[HtmlStruct "head" [] ([HtmlStruct "title" [] [HtmlText title]] ++
concatMap param2html params),
HtmlStruct "body" bodyattrs
((if null url then id
else \he->[HtmlStruct "form"
([("method","post"),("action",url)]
++ onsubmitattr ++ targetattr)
he])
(
cenv2hidden cenv ++
html))])
where
param2html (FormEnc enc) =
[HtmlStruct "meta" [("http-equiv","Content-Type"),
("content","text/html; charset="++enc)] []]
param2html (FormCSS css) =
[HtmlStruct "link" [("rel","stylesheet"),("type","text/css"),("href",css)]
[]]
param2html (FormJScript js) =
[HtmlStruct "script" [("type","text/javascript"),("src",js)] []]
param2html (FormOnSubmit _) = []
param2html (FormTarget _) = []
param2html (HeadInclude hexp) = [hexp]
param2html MultipleHandlers = []
param2html (BodyAttr _) = []
bodyattrs = [ps | (BodyAttr ps) <- params]
onsubmit = [s | (FormOnSubmit s) <- params]
onsubmitattr = if null onsubmit then [] else [("onsubmit",head onsubmit)]
target = [s | (FormTarget s) <- params]
targetattr = if null target then [] else [("target",head target)]
env2html :: [(String,String)] -> [HtmlExp]
env2html env = concat (map (\(n,v)->[htxt (n++": "++v),breakline]) env)
cenv2hidden env = concat (map pair2hidden env)
where
pair2hidden (n,v)
| take 6 n == "EVENT_" = []
| take 6 n == "FIELD_" = [hiddenfield ('U':n) (string2urlencoded v)]
| otherwise = [hiddenfield n v]
changeAssoc :: [(tt,tv)] -> tt -> tv -> [(tt,tv)]
changeAssoc [] tag val = [(tag,val)]
changeAssoc ((tag1,val1):tvs) tag val =
if tag1 == tag then (tag,val) : tvs
else (tag1,val1) : changeAssoc tvs tag val
showLatexExps :: [HtmlExp] -> String
showLatexExps hexps = concat (map showLatexExp hexps)
showLatexExp :: HtmlExp -> String
showLatexExp (HtmlText s) = "{" ++ specialchars2tex s ++ "}"
showLatexExp (HtmlStruct tag attrs htmlexp)
| tag=="html" = showLatexExps htmlexp
| tag=="head" = ""
| tag=="body" = showLatexExps htmlexp
| tag=="form" = showLatexExps htmlexp
| tag=="h1" = "\\section*{" ++ showLatexExps htmlexp ++ "}\n"
| tag=="h2" = "\\subsection*{" ++ showLatexExps htmlexp ++ "}\n"
| tag=="h3" = "\\subsubsection*{" ++ showLatexExps htmlexp ++ "}\n"
| tag=="h4" = "\\paragraph*{" ++ showLatexExps htmlexp ++ "}\n"
| tag=="h5" = "\\subparagraph*{" ++ showLatexExps htmlexp ++ "}\n"
| tag=="p" = showLatexExps htmlexp ++ "\\par\n"
| tag=="b" = "{\\bf " ++ showLatexExps htmlexp ++ "}"
| tag=="em" = "\\emph{" ++ showLatexExps htmlexp ++ "}"
| tag=="i" = "{\\it " ++ showLatexExps htmlexp ++ "}"
| tag=="tt" = "{\\tt " ++ showLatexExps htmlexp ++ "}"
| tag=="code" = "{\\tt " ++ showLatexExps htmlexp ++ "}"
| tag=="center" = latexEnvironment "center" (showLatexExps htmlexp)
| tag=="pre" = latexEnvironment "verbatim" (showLatexExps htmlexp)
| tag=="font" = showLatexExps htmlexp
| tag=="address" = showLatexExps htmlexp
| tag=="blink" = showLatexExps htmlexp
| tag=="a" = showLatexExps htmlexp ++
maybe ""
(\url->"\\footnote{\\tt "++specialchars2tex url++"}\n")
(findHtmlAttr "href" attrs)
| tag=="ul" = latexEnvironment "itemize" (showLatexExps htmlexp)
| tag=="ol" = latexEnvironment "enumerate" (showLatexExps htmlexp)
| tag=="li" = "\\item\n" ++ showLatexExps htmlexp ++ "\n"
| tag=="dl" = latexEnvironment "description" (showLatexExps htmlexp)
| tag=="dt" = "\\item[" ++ showLatexExps htmlexp ++ "]~\\\\\n"
| tag=="dd" = showLatexExps htmlexp
| tag=="table" = attrLatexEnv "longtable" (latexTabFormat htmlexp)
(showLatexTableContents htmlexp)
| tag=="tr" = let cells = map showLatexExp htmlexp
in concat (intersperse " & " cells) ++ "\\\\\n"
| tag=="td" = showLatexExps htmlexp
| tag=="br" = "\\par\n"
| tag=="hr" = "\\vspace{2ex}\\hrule\n"
| tag=="img" = "{" ++ maybe "{\\tt<IMAGE>}" specialchars2tex
(findHtmlAttr "alt" attrs)
++ "}"
| tag=="input" && maybe "" id (findHtmlAttr "type" attrs) == "hidden" = ""
| otherwise = "{\\tt<"++tag++">}" ++ showLatexExps htmlexp ++
"{\\tt</"++tag++">}"
latexEnvironment :: String -> String -> String
latexEnvironment env content = attrLatexEnv env "" content
attrLatexEnv :: String -> String -> String -> String
attrLatexEnv env attr content
= "\\begin{"++env++"}"++attr++"\n"
++content
++"\n\\end{"++env++"}\n"
latexTabFormat :: [HtmlExp] -> String
latexTabFormat rows = "{" ++ replicate breadth 'l' ++ "}"
++ "\\setcounter{LTchunksize}{"++show (length rows+5)++"}%"
where
breadth = foldl max 0 (map getBreadth rows)
getBreadth :: HtmlExp -> Int
getBreadth row = case row of
HtmlStruct "tr" _ tds -> length tds
_ -> error "getBreadth: no row given"
showLatexTableContents :: [HtmlExp] -> String
showLatexTableContents hexps = concatMap showLatexTableContent hexps
showLatexTableContent :: HtmlExp -> String
showLatexTableContent (HtmlText s) = "{" ++ specialchars2tex s ++ "}"
showLatexTableContent (HtmlStruct tag attrs htmlexp)
| tag=="html" = showLatexTableContents htmlexp
| tag=="head" = ""
| tag=="body" = showLatexTableContents htmlexp
| tag=="form" = showLatexTableContents htmlexp
| tag=="p" = showLatexTableContents htmlexp ++ "\\par\n"
| tag=="b" = "{\\bf " ++ showLatexTableContents htmlexp ++ "}"
| tag=="em" = "\\emph{" ++ showLatexTableContents htmlexp ++ "}"
| tag=="i" = "{\\it " ++ showLatexTableContents htmlexp ++ "}"
| tag=="tt" = "{\\tt " ++ showLatexTableContents htmlexp ++ "}"
| tag=="font" = showLatexTableContents htmlexp
| tag=="address" = showLatexTableContents htmlexp
| tag=="blink" = showLatexTableContents htmlexp
| tag=="a" = showLatexTableContents htmlexp ++
maybe ""
(\url->"\\footnote{\\tt "++specialchars2tex url++"}\n")
(findHtmlAttr "href" attrs)
| tag=="tr" = let cells = map showLatexTableContent htmlexp
in concat (intersperse " & " cells) ++ "\\\\\n"
| tag=="td" = showLatexTableContents htmlexp
| tag=="br" = "\\par\n"
| tag=="hr" = "\\vspace{2ex}\\hrule\n"
| tag=="img" = "{" ++ maybe "{\\tt<IMAGE>}" specialchars2tex
(findHtmlAttr "alt" attrs)
++ "}"
| tag=="input" && maybe "" id (findHtmlAttr "type" attrs) == "hidden" = ""
| otherwise = "{\\tt<"++tag++">}" ++ showLatexTableContents htmlexp ++
"{\\tt</"++tag++">}"
findHtmlAttr :: String -> [(String,String)] -> Maybe String
findHtmlAttr _ [] = Nothing
findHtmlAttr atag ((t,f):attrs) =
if atag==t then Just f
else findHtmlAttr atag attrs
specialchars2tex [] = []
specialchars2tex (c:cs)
| c==chr 228 = "\\\"a" ++ specialchars2tex cs
| c==chr 246 = "\\\"o" ++ specialchars2tex cs
| c==chr 252 = "\\\"u" ++ specialchars2tex cs
| c==chr 196 = "\\\"A" ++ specialchars2tex cs
| c==chr 214 = "\\\"O" ++ specialchars2tex cs
| c==chr 220 = "\\\"U" ++ specialchars2tex cs
| c==chr 223 = "\\ss{}" ++ specialchars2tex cs
| c=='~' = "{\\char126}" ++ specialchars2tex cs
| c=='\\' = "{\\char92}" ++ specialchars2tex cs
| c=='<' = "{$<$}" ++ specialchars2tex cs
| c=='>' = "{$>$}" ++ specialchars2tex cs
| c=='_' = "\\_" ++ specialchars2tex cs
| c=='#' = "\\#" ++ specialchars2tex cs
| c=='$' = "\\$" ++ specialchars2tex cs
| c=='%' = "\\%" ++ specialchars2tex cs
| c=='{' = "\\{" ++ specialchars2tex cs
| c=='}' = "\\}" ++ specialchars2tex cs
| c=='&' = let (special,rest) = break (==';') cs
in if null rest
then "\\&" ++ specialchars2tex special
else htmlspecial2tex special ++
specialchars2tex (tail rest)
| otherwise = c : specialchars2tex cs
htmlspecial2tex special
| special=="Auml" = "{\\\"A}"
| special=="Euml" = "{\\\"E}"
| special=="Iuml" = "{\\\"I}"
| special=="Ouml" = "{\\\"O}"
| special=="Uuml" = "{\\\"U}"
| special=="auml" = "{\\\"a}"
| special=="euml" = "{\\\"e}"
| special=="iuml" = "{\\\"\\i}"
| special=="ouml" = "{\\\"o}"
| special=="uuml" = "{\\\"u}"
| special=="szlig" = "{\\ss}"
| special=="Aacute" = "{\\'A}"
| special=="Eacute" = "{\\'E}"
| special=="Iacute" = "{\\'I}"
| special=="Oacute" = "{\\'O}"
| special=="Uacute" = "{\\'U}"
| special=="aacute" = "{\\'a}"
| special=="eacute" = "{\\'e}"
| special=="iacute" = "{\\'\\i}"
| special=="oacute" = "{\\'o}"
| special=="uacute" = "{\\'u}"
| special=="Agrave" = "{\\`A}"
| special=="Egrave" = "{\\`E}"
| special=="Igrave" = "{\\`I}"
| special=="Ograve" = "{\\`O}"
| special=="Ugrave" = "{\\`U}"
| special=="agrave" = "{\\`a}"
| special=="egrave" = "{\\`e}"
| special=="igrave" = "{\\`\\i}"
| special=="ograve" = "{\\`o}"
| special=="ugrave" = "{\\`u}"
| special=="Acirc" = "{\\^A}"
| special=="Ecirc" = "{\\^E}"
| special=="Icirc" = "{\\^I}"
| special=="Ocirc" = "{\\^O}"
| special=="Ucirc" = "{\\^U}"
| special=="acirc" = "{\\^a}"
| special=="ecirc" = "{\\^e}"
| special=="icirc" = "{\\^\\i}"
| special=="ocirc" = "{\\^o}"
| special=="ucirc" = "{\\^u}"
| special=="Oslash" = "{\\O}"
| special=="oslash" = "{\\o}"
| special=="amp" = "{\\&}"
| special=="ntilde" = "{\\~n}"
| special=="otilde" = "{\\~o}"
| special=="ccedil" = "{\\c{c}}"
| special=="nbsp" = "~"
| special=="quot" = "\""
| special=="lt" = "{$<$}"
| special=="gt" = "{$>$}"
| otherwise = "\\&"++special++";"
showLatexDoc :: [HtmlExp] -> String
showLatexDoc htmlexps = showLatexDocs [htmlexps]
showLatexDocWithPackages :: [HtmlExp] -> [String] -> String
showLatexDocWithPackages hexps packages
= showLatexDocsWithPackages [hexps] packages
showLatexDocs :: [[HtmlExp]] -> String
showLatexDocs htmlexps_list = showLatexDocsWithPackages htmlexps_list []
showLatexDocsWithPackages :: [[HtmlExp]] -> [String] -> String
showLatexDocsWithPackages htmlexps_list packages =
"\\documentclass[12pt]{article}\n"++
concatMap (\p->"\\usepackage{"++p++"}\n") packages++
"\\usepackage{longtable}"++
"\\nonstopmode\n"++
"\\setlength{\\topmargin}{ -1.5cm}\n"++
"\\setlength{\\oddsidemargin}{0.0cm}\n"++
"\\setlength{\\evensidemargin}{0.0cm}\n"++
"\\setlength{\\marginparwidth}{0.0cm}\n"++
"\\setlength{\\marginparsep}{0.0cm}\n"++
"\\setlength{\\textwidth}{16.5cm}\n"++
"\\setlength{\\textheight}{24.0cm}\n"++
"\\pagestyle{empty}\n"++
"\\begin{document}\n\\sloppy\n"++
"\\addtolength{\\baselineskip}{0.0ex}\n"++
"\\setlength{\\parindent}{0.0ex}\n"++
"\\addtolength{\\parskip}{0.5ex}\n"++
concat (intersperse "\\newpage\n" (map showLatexExps htmlexps_list))++
"\\end{document}\n"
germanLatexDoc :: [HtmlExp] -> String
germanLatexDoc hexps = showLatexDocWithPackages hexps ["ngerman"]
intForm :: IO HtmlForm -> IO ()
intForm = intFormMain "" "" "" "" False ""
intFormMain :: String -> String -> String -> String ->
Bool -> String -> IO HtmlForm -> IO ()
intFormMain baseurl basecgi reldir cginame forever urlparam hformact = do
pid <- getPID
user <- getEnviron "USER"
home <- getEnviron "HOME"
let portname = "intcgi_" ++ show pid
socket <- listenOn portname
let cgiprogname = if null cginame then "cgitest_"++show pid++".cgi"
else cginame
url = (if null baseurl then "http://localhost/~"++user else baseurl)
++ "/" ++ reldir ++ "/" ++ cgiprogname
cgifile = (if null basecgi then home++"/public_html/" else basecgi++"/")++
(if null reldir then "" else reldir ++"/") ++ cgiprogname
cgikey = url++" 42"
installShowCgiEnvScript portname cgifile
setEnviron "QUERY_STRING" urlparam
time <- getClockTime
intFormInEnv url cgikey hformact hformact [] (initialServerState time)
forever socket
system ("rm "++cgifile) >> done
intFormInEnv :: String -> String -> IO HtmlForm -> IO HtmlForm
-> [(String,String)] -> ServerState -> Bool -> Socket -> IO ()
intFormInEnv url cgikey initform hformact cenv state forever socket = do
if null cenv then putStrLn ">>> Start initial web form..." else done
cform <- hformact
let (cookiestring,hform) = extractCookies cform
(htmlstring,evhs) <- showHtmlFormInEnv url "" (extendForm hform)
(getMaxFieldNr cenv + 1)
nstate <- storeEnvHandlers state
(formWithMultipleHandlers hform)
(encodeKey cgikey)
(filter (\ (t,_) -> t/="DEFAULT" && take 6 t /= "EVENT_") cenv)
evhs
showHtmlStringInBrowser (cookiestring++htmlstring)
if forever || formWithHandlers hform
then do putStrLn ">>> Waiting for web page submission..."
(_,hdl) <- socketAccept socket
mbmsg <- readCgiServerMsg hdl
maybe (intFormInEnv url cgikey initform hformact
cenv state forever socket)
(intFormProceed nstate hdl)
mbmsg
else putStrLn ">>> Final web page reached"
where
intFormProceed nstate hdl (CgiSubmit scriptenv newcenv) = do
hPutStrLn hdl answerTxt
hClose hdl
mapIO_ (\ (var,val) -> setEnviron var val) scriptenv
if null newcenv
then intFormInEnv url cgikey initform initform [] nstate forever socket
else do
(rstate,mfe) <- getNextFormAndCgiEnv nstate cgikey newcenv
maybe (putStrLn "ERROR: no submission handler")
(\ (ioform,env) -> intFormInEnv url cgikey initform ioform
env rstate forever socket)
mfe
answerTxt = "Content-Type: text/html\n\n" ++
showHtmlExp (italic [htxt "Waiting for next web form..."])
extendForm orgform =
orgform `addFormParam` HeadInclude (HtmlStruct "base" [("href",url)] [])
formWithHandlers (HtmlForm _ _ hexps) = hasHandlers hexps
where
hasHandlers :: [HtmlExp] -> Bool
hasHandlers [] = False
hasHandlers (HtmlText _ : hes) = hasHandlers hes
hasHandlers (HtmlStruct _ _ hes1 : hes2) =
hasHandlers hes1 || hasHandlers hes2
hasHandlers (HtmlCRef he _ : hes) = hasHandlers [he] || hasHandlers hes
hasHandlers (HtmlEvent _ _ : _) = True
showHtmlStringInBrowser htmlstring = do
pid <- getPID
let htmlfilename = "tmpcgiform_" ++ show pid ++ ".html"
writeFile htmlfilename htmlstring
system ("remote-netscape file:`pwd`/"++htmlfilename)
done
installShowCgiEnvScript :: String -> String -> IO ()
installShowCgiEnvScript portname cgifile = do
putStrLn ">>> Installing web script..."
putStrLn $ "for port name: "++portname
writeFile cgifile $ "#!/bin/sh\n"++
installDir++"/www/submitform \""++portname++"\"\n"
system ("chmod 755 "++cgifile)
done
type ServerState =
(ClockTime, Int, ClockTime,
[(Int,ClockTime,String,([(String,String)],HtmlHandler),Maybe Int)])
initialServerState :: ClockTime -> ServerState
initialServerState ctime = (ctime, 0, nextCleanup ctime, [])
isServerStateWithoutHandlers :: ServerState -> Bool
isServerStateWithoutHandlers (_,_,_,evhandlers) = null evhandlers
getServerLoad :: ServerState -> IO String
getServerLoad (stime,maxkey,_,evs) = do
ctime <- getClockTime
let busy = maxkey>500
|| (compareClockTime ctime (addMinutes 30 stime) == GT)
|| null evs
return (if busy then "busy" else "ready")
getServerStatus :: ServerState -> IO String
getServerStatus state@(stime,maxkey,_,evs) = do
busy <- getServerLoad state
lstime <- toCalendarTime stime
pinfos <- getProcessInfos
return $ "Status: " ++ busy ++ ", Maxkey: "++show maxkey ++ ", #Handlers: " ++
show (length evs) ++ ", Start time: " ++
calendarTimeToString lstime ++ "\n" ++
showMemInfo pinfos
showGroupKey :: Maybe Int -> String
showGroupKey Nothing = "multiple use"
showGroupKey (Just gk) = "group " ++ show gk
storeEnvHandlers :: ServerState -> Bool -> String -> [(String,String)]
-> [(HtmlHandler,String)] -> IO ServerState
storeEnvHandlers ostate multipleuse cgikey env handlerkeys = do
time <- getClockTime
cstate <- cleanOldEventHandlers ostate
let nstate = generateEventServerMessages
(if multipleuse then Nothing else Just (keyOfState cstate))
(eventHandlerExpiration time)
cstate
handlerkeys
seq nstate done
return nstate
where
generateEventServerMessages _ _ state [] = state
generateEventServerMessages groupkey expiredate state ((handler,hkey):evhs)
| show (keyOfState state) ++ ' ':showQTerm (toUTCTime expiredate) =:= hkey
= generateEventServerMessages
groupkey
expiredate
(storeNewEnvEventWithCgiKey groupkey expiredate state env handler)
evhs
keyOfState (_,key,_,_) = key
storeNewEnvEventWithCgiKey groupkey date (stime,maxkey,cleandate,ehs)
cenv info =
(stime,
if maxkey>30000 then 0 else maxkey+1,
cleandate,
(maxkey,date,cgikey,(cenv,info),groupkey):ehs)
cleanOldEventHandlers :: ServerState -> IO ServerState
cleanOldEventHandlers state@(_,_,_,[]) = return state
cleanOldEventHandlers state@(stime,maxkey,cleandate,ehs@(_:_)) = do
ctime <- getClockTime
if compareClockTime ctime cleandate == LT
then return state
else do
let currentehs = filter (isNotExpired ctime) ehs
noehs = length ehs
nocurrentehs = length currentehs
if nocurrentehs < noehs
then do
ltime <- toCalendarTime ctime
putErrLn $ calendarTimeToString ltime ++ ": cleanup " ++
"(number of handlers: old = "++ show noehs ++ " / " ++
"current = "++ show nocurrentehs ++ ")"
else done
return (stime,maxkey, nextCleanup ctime, currentehs)
where
isNotExpired time (_,etime,_,_,_) = compareClockTime time etime == LT
retrieveEnvHandlers :: ServerState -> String -> String
-> IO (ServerState,Maybe ([(String,String)],HtmlHandler))
retrieveEnvHandlers state cgikey skey =
let (numstring,datestring) = break (==' ') skey
dateps = readsQTerm datestring
num = tryReadNat (-1) numstring
in if null datestring || null dateps || num < 0
then return (state,Nothing)
else let (newstate,info) =
getEnvEventWithCgiKey state num (fst (head dateps))
in seq newstate (return (newstate, info))
where
getEnvEventWithCgiKey oldstate@(stime,maxkey,cleandate,ehs) key date =
maybe (oldstate,Nothing)
(\ (evhdlr,groupkey) ->
maybe (oldstate, Just evhdlr)
(\gk -> let newehs = deleteEv gk ehs
in seq newehs ((stime,maxkey,cleandate,newehs),
Just evhdlr))
groupkey )
(searchEv ehs)
where
searchEv [] = Nothing
searchEv ((n,t,c,i,gk):es) =
if key==n && date == toUTCTime t
then if c==cgikey then Just (i,gk) else Nothing
else searchEv es
deleteEv _ [] = []
deleteEv groupkey (ev@(_,_,_,_,Nothing):es) =
let des = deleteEv groupkey es in seq des (ev : des)
deleteEv groupkey (ev@(_,_,_,_,Just gk):es) =
if groupkey==gk
then deleteEvInGroup groupkey es
else let des = deleteEv groupkey es in seq des (ev : des)
deleteEvInGroup _ [] = []
deleteEvInGroup _ (ev@(_,_,_,_,Nothing):es) = ev : es
deleteEvInGroup groupkey (ev@(_,_,_,_,Just gk):es) =
if groupkey==gk
then deleteEvInGroup groupkey es
else ev : es
eventHandlerExpiration :: ClockTime -> ClockTime
eventHandlerExpiration = addHours 1
nextCleanup :: ClockTime -> ClockTime
nextCleanup = addMinutes 5