11 import qualified Codec.Binary.UTF8.String as UTF8
13 import Control.Arrow.ArrowList
15 import Control.Monad.Trans
16 import Network.HTTP.Lucu
17 import Network.HTTP.Lucu.Utils
18 import Network.URI hiding (path)
19 import Rakka.Authorization
20 import Rakka.Environment
21 import Rakka.Validation
22 import System.Log.Logger
23 import Text.XML.HXT.Arrow.ReadDocument
24 import Text.XML.HXT.Arrow.WriteDocument
25 import Text.XML.HXT.Arrow.XmlIOStateArrow
26 import Text.XML.HXT.DOM.TypeDefs
27 import Text.XML.HXT.DOM.XmlKeywords
31 logger = "Rakka.Resource"
35 -- "/foo" ==> "/foo.html"
36 -- "/foo/" ==> "/foo.html"
37 -- "/foo.bar/" ==> "/foo.bar"
38 -- "/foo.bar" ==> "/foo.bar"
39 canonicalizeURI :: Resource ()
41 = do uri <- getRequestURI
42 let newURI = uri { uriPath = "/" ++ joinWith "/" newPath }
43 newPath = case [x | x <- splitBy (== '/') (uriPath uri), x /= ""] of
45 path -> case break (== '.') $ last path of
46 (_, "") -> let basePieces = reverse $ tail $ reverse path
49 basePieces ++ [lastPiece ++ ".html"]
52 $ abort MovedPermanently
53 [("Location", uriToString id newURI $ "")]
57 runIdempotentA :: IOSArrow () (Resource c) -> Resource c
60 [rsrc] <- liftIO $ runX ( setErrorMsgHandler False fail
69 runXmlA :: Environment -> FilePath -> IOSArrow XmlTree (Resource c) -> Resource c
70 runXmlA env schemaPath a
71 = do inputA <- getInputXmlA env schemaPath
72 [rsrc] <- liftIO $ runX ( inputA
74 setErrorMsgHandler False fail
81 -- well-formed でない時は 400 Bad Request になり、valid でない時は 422
82 -- Unprocessable Entity になる。入力の型が XML でない時は 415
83 -- Unsupported Media Type を返す。
84 getInputXmlA :: Environment -> FilePath -> Resource (IOSArrow b XmlTree)
85 getInputXmlA env schemaPath
86 = do reader <- getInputReader
87 validator <- getValidator env schemaPath
88 return ( setErrorMsgHandler False (abort BadRequest [] . Just)
92 setErrorMsgHandler False (abort UnprocessableEntitiy [] . Just)
98 getInputReader :: Resource (IOSArrow b XmlTree)
100 = do mimeType <- getContentType
103 -> getFailingReader BadRequest [] (Just "Missing Content-Type")
104 Just (MIMEType "text" "xml" _)
106 Just (MIMEType "application" "xml" _)
109 -> getFailingReader UnsupportedMediaType [] (Just $ "Unsupported media type: " ++ show t)
112 = do req <- input defaultLimit
113 liftIO $ debugM logger req
114 return $ readString [ (a_validate , v_0)
115 , (a_check_namespaces , v_1)
116 , (a_remove_whitespace, v_0)
117 ] (UTF8.decodeString req)
118 getFailingReader code headers msg
119 = return $ proc _ -> abortA -< (code, (headers, msg))
122 getEntityType :: Resource MIMEType
124 = do uri <- getRequestURI
125 let ext = reverse $ takeWhile (/= '.') $ reverse $ uriPath uri
126 case lookup ext extMap of
127 Just mType -> return mType
128 Nothing -> abort NotFound [] (Just $ "Unsupported entity type: " ++ ext)
130 extMap :: [(String, MIMEType)]
131 extMap = [ ("html", read "application/xhtml+xml")
132 , ( "rdf", read "application/rss+xml" )
133 , ( "xml", read "text/xml" )
137 outputXmlPage :: XmlTree -> [(MIMEType, IOSArrow XmlTree XmlTree)] -> Resource ()
138 outputXmlPage tree formatters
139 = do mType <- getEntityType
141 let formatter = case lookup mType formatters of
144 [resultStr] <- liftIO $ runX ( setErrorMsgHandler False fail
150 writeDocumentToString [ (a_indent, v_1) ]
155 outputXmlPage' :: XmlTree -> IOSArrow XmlTree XmlTree -> Resource ()
156 outputXmlPage' tree toXHTML
157 = outputXmlPage tree [(MIMEType "application" "xhtml+xml" [], toXHTML)]
160 getUserID :: Environment -> Resource (Maybe String)
162 = do auth <- getAuthorization
164 Just (BasicAuthCredential userID password)
165 -> do valid <- isValidPair (envAuthDB env) userID password