module ToHtml(toHtml) where import Data.Maybe(listToMaybe,mapMaybe) import Control.Monad(mplus,guard) import HtmlParser2 import URL import URLencode(encode) import Http import Html import HtmlTags import HtmlFuns(extractElements,mapHtmlChars,mapHtmlTags) import HtmlConOps import Gemini --import ParsOps(Error(..)) import MimeMessage as M(parseMessage,MimeMessage(..),orig,normal,contentType,parseParams) import DecodeText(convertMessageBody,decodeTextMsg',decodeMsg) import Dew(convertCharset') import HeaderNames as HN import Utils2(words',mix,strToLower,pairwith,apFst,apSnd,aboth,chopList,breakAt) -- debugging: --import Debug.Trace(trace) --import MimeMessage(showMessage) --import HtmlPrinter(printHtml) toHtml = apFst (apSnd snd) . pairwith toHtml' . apSnd decodeBody where decodeBody httpResp@HttpResp {respHdrs=hdrs,respBody=body} = ((ct,ocset),httpResp{respBody=cbody}) where ct = M.contentType hdrs (_,(ocset,cbody)) = convertMessageBody hdrs body toHtml' (url@(URL optproto _ _ path _), (ty,HttpResp {respHdrs=hdrs, respBody=body})) = {-trace (printHtml html)-} html where proto = case optproto of Just p -> p ; _ -> "" -- !! html = case proto of "gopher" -> case gopherType path of '1' -> gopherDir (stripDots' body) '7' -> gopherDir (stripDots' body) '0' -> plain2html (stripDots body) 'h' -> parsHtml (snd ty) (stripDots body) t -> plain2html ("Unknown gopher document type: "++[t]) _ -> msg2html path ty hdrs body stripDot ('.':cs) = cs stripDot cs = cs stripDots = unlines.stripDots' stripDots' = map stripDot.lines gopherType "" = '1' gopherType "/" = '1' gopherType ('/':c:_) = c msg2html path ((mimetype,params),ocset) hdrs msgbody = case mimetype of "text/html" -> parsHtml ocset msgbody "text/plain" -> plain2html msgbody "text/gemini" -> gemini2html msgbody "message/rfc822" -> rfc822msg2html msgbody "" -> byext2html "file" -> byext2html "nlst" -> [body [dir [[href s [txt s]]|s<-lines msgbody]]] "list" -> [body [menu [(listEntry s)|s<-lines msgbody]]] ctype -> case breakAt '/' ctype of ("image",_) -> image2html ctype hdrs msgbody ("text",_) -> plain2html ("Content-Type: "++ctype++"\n\n"++msgbody) ("multipart",subty) -> multipart2html subty bndry msgbody where bndry = lookup "boundary" ps _ -> plain2html ("Content-Type: "++ctype) where ps = parseParams params byext2html = case extension path of "mosaic-hotlist-default" -> hotlist2html (lines msgbody) "htm" -> parsHtml ocset msgbody "html" -> parsHtml ocset msgbody "newsrc" -> newsrc2html msgbody e -> plain2html ({-("Unknown extension: "++e):"":-}msgbody) extension = afterlast False '.' . afterlast True '/' afterlast b c s = case break (==c) (reverse s) of (e,"") -> if b then s else "" (e,_) -> reverse e listEntry s = let ws = words' s droplast = init --reverse.tail.reverse prelen = 16 -- number of words before file name (pre,filepost) = splitAt prelen ws (filews,post) = case break (=="->") filepost of (ws,p@("->":_)) -> (droplast ws,p) (ws,_) -> (ws,[]) file = concat filews txt' = txt . nbsp . concat nbsp = map nbsp1 nbsp1 ' ' = '\xa0' nbsp1 c = c in [ctx NOBR [ctx TT [txt' pre], href file [HtmlChars file], txt' post -- maybe empty ]] parsHtml ocset s' = case parseHtml s of Right html -> maybe convMetaCharset (const id) ocset html Left (es,ctx) -> [body [dl [dt [txt "Syntax error before:"],dd [txt (take 120 ctx++"...")], dt [txt "Expected:"],dd [txt (mix es ", "++".")]], hr, pre [txt s]]] where s = fixCR s' convMetaCharset html = maybe id convert mcs html where mcs = listToMaybe . mapMaybe charset . extractElements META . take 1 $ extractElements HEAD html charset (HtmlCommand (META,attrs)) = short `mplus` long `mplus` httpEquiv where short = lookupAttr "CHARSET" attrs long = do n <- lookupAttr "NAME" attrs guard $ strToLower n == "charset" lookupAttr "CONTENT" attrs httpEquiv = do n <- lookupAttr "HTTP-EQUIV" attrs guard $ strToLower n == "content-type" v <- lookupAttr "CONTENT" attrs lookup "charset" (parseParams v) charset _ = Nothing convert = maybe id conv . convertCharset' conv (_,f) = mapHtmlChars ((:[]) . txt . f) . mapHtmlTags tf where tf (t,as) = (t,mapAttrs (aboth f) as) image2html ty hdrs msgbody = [body [p [txt alt,br,imgalt ("data:"++ty++","++encode imgdata) alt]]] where MimeMsg _ imgdata = decodeMsg (MimeMsg hdrs msgbody) alt = ty++", "++show (length imgdata)++" bytes" plain2html s = [body [pre [txt s]]] gopherDir ls = [body [menu entries]] where entries = map htmlEntry ls htmlEntry "" = [] -- ?? htmlEntry (t:cs) = case chopList (breakAt '\t') cs of name:path:host:port:_ -> let proto = if t=='8' then "telnet" else "gopher" u = url proto host port ('/':t:path) in txt (showType t++": "): if t=='7' then [txt name,cmd' ISINDEX [("ACTION",u)]] else [href u [txt name]] _ -> [txt "???"] showType t = --assoc id [t] gopherTypes t maybe [t] id (lookup t gopherTypes) url proto host port path = url2str $ URL (Just proto) (Just host) p path Nothing where p = case port of "70" -> Nothing _ -> Just (read port) gopherTypes = [('0',"Text"), ('1',"Menu"), ('2',"CSO "), ('3',"ERR "), ('4',"BinHex"), ('5',"PCBin"), ('6',"uuencoded"), ('7',"Search"), ('8',"Telnet"), ('9',"Binary"), ('g',"GIF "), ('I',"Image"), ('T',"3270"), ('h',"Html")] hotlist2html ls = [body [ctx H1 [HtmlChars "Mosaic Hostlist"], ul entries]] where entries = (map entry.drop 1.pairs) ls pairs (x1:x2:xs) = (x1,x2):pairs xs pairs _ = [] entry (urlline,title) = li [href url [HtmlChars title]] where url = head (words urlline) multipart2html subty Nothing msgbody = plain2html ("Content-Type: multipart/"++subty) multipart2html subty (Just boundary) msgbody = case subty of "mixed" -> all_parts "parallel" -> all_parts "signed" -> all_parts "related" -> all_parts -- hmm "alternative" -> part2html (last parts) -- hmm _ -> plain2html ("Content-Type: multipart/"++subty) where all_parts = mix (map part2html (drop 1 parts)) [hr] part2html part = rfc822msg2html' False part parts = splitMultipart boundary msgbody splitMultipart boundary = map unlines . dropFromLast . chopList (breakAt ("--"++boundary)) . lines where dropFromLast = reverse . dropLast . reverse dropLast (p:ps) = (reverse . dropit $ reverse p):ps dropLast [] = [] dropit ("":l:ls) | l=="--"++boundary++"--" = ls dropit ls = ls rfc822msg2html msgbody = [body (rfc822msg2html' True msgbody)] rfc822msg2html' showHdrs msgbody = --trace msgbody $ case decodeTextMsg' (parseMessage msgbody) of MimeMsg hdrs msgbody -> [htmlHdrs hdrs|showHdrs]++msg2html "" (ct,Nothing) hdrs msgbody where ct = M.contentType hdrs --(_,(ocs,cbody)) = convertMessageBody hdrs msgbody htmlHdrs hdrs = ctx' TABLE [("CELLSPACING","0")] (concatMap htmlHdr hdrs) where htmlHdr (h,r) = let h' = orig h th' = ctx' TH [("ALIGN","RIGHT"),("VALIGN","TOP")] in if h `elem` interestingHeaders then [ctx TR [th' [HtmlChars (h'++":")],ctx TD (augment h r)]] else [] augment h r = case normal h of "newsgroups" -> newsrefs r "references" -> newsrefs r "subject" -> [ctx STRONG [HtmlChars r]] "from" -> [ctx ADDRESS [HtmlChars r]] _ -> [HtmlChars r] newsrefs = (:[]) . p . flip mix [HtmlChars ", "].map newsref.words.map cs newsref ref = [href ("news:"++ref) [HtmlChars ref]] cs ',' = ' ' cs '<' = ' ' cs '>' = ' ' cs c = c newsrc2html newsrc = html where groups = map fst $ filter subscribed $ map (break (`elem` ":!")) $ lines newsrc where subscribed = (==":") . take 1 . snd html = [body [ul (map grouplink groups)]] grouplink group = li [href ("news:"++group) [txt group]] -- interestingHeaders = [subject,from,date,to,cc,HN.contentType,contentTransferEncoding, references,organization,newsgroups] fixCR ('\r':'\n':s) = '\n':fixCR s fixCR ('\r':s) = '\n':fixCR s fixCR (c:s) = c:fixCR s fixCR "" = ""