5 module Network.HTTP.Lucu.MultipartForm
11 import qualified Data.ByteString.Char8 as C8
12 import qualified Data.ByteString.Lazy.Char8 as L8
15 import Network.HTTP.Lucu.Abortion
16 import Network.HTTP.Lucu.Headers
17 import Network.HTTP.Lucu.Parser
18 import Network.HTTP.Lucu.Parser.Http
19 import Network.HTTP.Lucu.Response
20 import Network.HTTP.Lucu.Utils
23 data Part = Part Headers L8.ByteString
25 -- |This data type represents a form value and possibly an uploaded
29 fdFileName :: Maybe String
30 , fdContent :: L8.ByteString
33 instance HasHeaders Part where
34 getHeaders (Part hs _) = hs
35 setHeaders (Part _ b) hs = Part hs b
38 data ContDispo = ContDispo String [(String, String)]
40 instance Show ContDispo where
41 show (ContDispo dType dParams)
46 "; " ++ joinWith "; " (map showPair dParams)
48 showPair :: (String, String) -> String
49 showPair (name, value)
50 = name ++ "=" ++ if any (not . isToken) value then
56 multipartFormP :: String -> Parser [(String, FormData)]
57 multipartFormP boundary
58 = do parts <- many (partP boundary)
64 return $ map partToFormPair parts
67 partP :: String -> Parser Part
71 _ <- crlf -- バウンダリの末尾に -- が付いてゐたらここで fail する。
73 body <- bodyP boundary
77 bodyP :: String -> Parser L8.ByteString
79 = do body <- manyChar $
80 do notFollowedBy $ ( crlf >>
88 partToFormPair :: Part -> (String, FormData)
89 partToFormPair part@(Part _ body)
90 = let name = partName part
91 fname = partFileName part
98 partName :: Part -> String
99 partName = getName' . getContDispoFormData
101 getName' :: ContDispo -> String
102 getName' dispo@(ContDispo _ dParams)
103 = case find ((== "name") . map toLower . fst) dParams of
104 Just (_, name) -> name
106 -> abortPurely BadRequest []
107 (Just $ "form-data without name: " ++ show dispo)
110 partFileName :: Part -> Maybe String
111 partFileName = getFileName' . getContDispoFormData
113 getFileName' :: ContDispo -> Maybe String
114 getFileName' (ContDispo _ dParams)
115 = do (_, fileName) <- find ((== "filename") . map toLower . fst) dParams
118 getContDispoFormData :: Part -> ContDispo
119 getContDispoFormData part
120 = let dispo@(ContDispo dType _) = getContDispo part
122 if map toLower dType == "form-data" then
125 abortPurely BadRequest []
126 (Just $ "Content-Disposition type is not form-data: " ++ dType)
129 getContDispo :: Part -> ContDispo
131 = case getHeader (C8.pack "Content-Disposition") part of
133 -> abortPurely BadRequest []
134 (Just "There is a part without Content-Disposition in the multipart/form-data.")
136 -> case parse contDispoP (L8.fromChunks [dispoStr]) of
137 (# Success dispo, _ #)
140 -> abortPurely BadRequest []
141 (Just $ "Unparsable Content-Disposition: " ++ C8.unpack dispoStr)
144 contDispoP :: Parser ContDispo
145 contDispoP = do dispoType <- token
146 params <- allowEOF $ many paramP
147 return $ ContDispo dispoType params
149 paramP :: Parser (String, String)
150 paramP = do _ <- many lws
155 value <- token <|> quotedStr