import qualified Data.Ascii as A
import Data.Attoparsec.Char8 as P
import qualified Data.ByteString.Char8 as C8
+import Data.Map (Map)
import Data.Monoid.Unicode
+import Data.Text (Text)
import Network.HTTP.Lucu.Parser.Http
import Network.HTTP.Lucu.Utils
import Prelude hiding (min)
data MIMEType = MIMEType {
mtMajor ∷ !CIAscii
, mtMinor ∷ !CIAscii
- , mtParams ∷ ![ (CIAscii, Ascii) ]
+ , mtParams ∷ !(Map CIAscii Text)
} deriving (Eq, Show)
-- |Convert a 'MIMEType' to 'Ascii'.
DoAndIfThenElse
, OverloadedStrings
, ScopedTypeVariables
- , UnboxedTuples
, UnicodeSyntax
#-}
module Network.HTTP.Lucu.MultipartForm
import Data.Attoparsec.Char8
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as LS
-import Data.Char
-import Data.List
+import Data.Char
+import Data.List
+import Data.Map (Map)
import Data.Maybe
import Data.Monoid.Unicode
import Data.Text (Text)
-import Network.HTTP.Lucu.Headers
-import Network.HTTP.Lucu.Parser.Http
-import Network.HTTP.Lucu.Response
-import Network.HTTP.Lucu.Utils
+import Network.HTTP.Lucu.Headers
+import Network.HTTP.Lucu.Parser.Http
+import Network.HTTP.Lucu.RFC2231
+import Network.HTTP.Lucu.Response
+import Network.HTTP.Lucu.Utils
import Prelude.Unicode
-- |This data type represents a form value and possibly an uploaded
data ContDispo
= ContDispo {
dType ∷ !CIAscii
- , dParams ∷ ![(CIAscii, Ascii)]
+ , dParams ∷ !(Map CIAscii Text)
}
printContDispo ∷ ContDispo → Ascii
= A.fromAsciiBuilder $
( A.toAsciiBuilder (A.fromCIAscii $ dType d)
⊕
- ( if null $ dParams d then
- (∅)
- else
- A.toAsciiBuilder "; " ⊕
- joinWith "; " (map printPair $ dParams d) ) )
- where
- printPair ∷ (CIAscii, Ascii) → AsciiBuilder
- printPair (name, value)
- = A.toAsciiBuilder (A.fromCIAscii name) ⊕
- A.toAsciiBuilder "=" ⊕
- ( if BS.any ((¬) ∘ isToken) $ A.toByteString value then
- quoteStr value
- else
- A.toAsciiBuilder value )
+ printParams (dParams d) )
multipartFormP ∷ Ascii → Parser [(Text, FormData)]
multipartFormP boundary
--- /dev/null
+{-# LANGUAGE
+ OverloadedStrings
+ , UnicodeSyntax
+ #-}
+-- |Provide facilities to encode/decode MIME parameter values in
+-- character sets other than US-ASCII. See:
+-- http://www.faqs.org/rfcs/rfc2231.html
+module Network.HTTP.Lucu.RFC2231
+ ( printParams
+-- , paramsP
+ )
+ where
+import Data.Ascii (Ascii, CIAscii, AsciiBuilder)
+import qualified Data.Ascii as A
+import Data.Bits
+import qualified Data.ByteString.Char8 as BS
+import Data.Map (Map)
+import qualified Data.Map as M
+import Data.Monoid.Unicode
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Text.Encoding
+import Data.Word
+import Network.HTTP.Lucu.Parser.Http
+import Network.HTTP.Lucu.Utils
+import Prelude.Unicode
+
+printParams ∷ Map CIAscii Text → AsciiBuilder
+printParams params
+ | M.null params = (∅)
+ | otherwise = A.toAsciiBuilder "; " ⊕
+ joinWith "; " (map printPair $ M.toList params)
+ where
+ printPair ∷ (CIAscii, Text) → AsciiBuilder
+ printPair (name, value)
+ | T.any (> '\xFF') value
+ = printPairInUTF8 name value
+ | otherwise
+ = printPairInAscii name (A.unsafeFromByteString $ encodeUtf8 value)
+
+ printPairInUTF8 ∷ CIAscii → Text → AsciiBuilder
+ printPairInUTF8 name value
+ = A.toAsciiBuilder (A.fromCIAscii name) ⊕
+ A.toAsciiBuilder "*=utf-8''" ⊕
+ escapeUnsafeChars (encodeUtf8 value) (∅)
+
+ printPairInAscii ∷ CIAscii → Ascii → AsciiBuilder
+ printPairInAscii name value
+ = A.toAsciiBuilder (A.fromCIAscii name) ⊕
+ A.toAsciiBuilder "=" ⊕
+ if BS.any ((¬) ∘ isToken) (A.toByteString value) then
+ quoteStr value
+ else
+ A.toAsciiBuilder value
+
+ escapeUnsafeChars ∷ BS.ByteString → AsciiBuilder → AsciiBuilder
+ escapeUnsafeChars bs b
+ = case BS.uncons bs of
+ Nothing → b
+ Just (c, bs')
+ | isToken c → escapeUnsafeChars bs' $
+ b ⊕ A.toAsciiBuilder (A.unsafeFromString [c])
+ | otherwise → escapeUnsafeChars bs' $
+ b ⊕ toHex (fromIntegral $ fromEnum c)
+
+ toHex ∷ Word8 → AsciiBuilder
+ toHex o = A.toAsciiBuilder "%" ⊕
+ A.toAsciiBuilder (A.unsafeFromString [ toHex' (o `shiftR` 8)
+ , toHex' (o .&. 0x0F) ])
+
+ toHex' ∷ Word8 → Char
+ toHex' o
+ | o ≤ 0x09 = toEnum $ fromIntegral $ fromEnum '0' + fromIntegral o
+ | otherwise = toEnum $ fromIntegral $ fromEnum 'A' + fromIntegral (o - 0x0A)
+
+{-
+decode ∷ [(CIAscii, Ascii)] → Map CIAscii Text
+{-# INLINEABLE decode #-}
+decode = error "FIXME: not implemented"
+-}
\ No newline at end of file