diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,5 @@
-Version 0.3.2: released 2009-12-21
-
-* sub-point release.
-* extend exports of Codec.MIME.Parse to include misc
-  local utility functions: parseMIMEMessage, parseContentType
+Version 0.3.2: released 2009-12-21
+
+* sub-point release.
+* extend exports of Codec.MIME.Parse to include misc
+  local utility functions: parseMIMEMessage, parseContentType
diff --git a/Codec/MIME/Base64.hs b/Codec/MIME/Base64.hs
--- a/Codec/MIME/Base64.hs
+++ b/Codec/MIME/Base64.hs
@@ -1,147 +1,147 @@
---------------------------------------------------------------------
--- |
--- Module    : Codec.MIME.Base64
--- Copyright : (c) 2006-2009, Galois, Inc. 
--- License   : BSD3
---
--- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
--- Stability : provisional
--- Portability: portable
---
--- 
--- Base64 decoding and encoding routines, multiple entry
--- points for either depending on use and level of control
--- wanted over the encoded output (and its input form on the
--- decoding side.)
--- 
---------------------------------------------------------------------
-module Codec.MIME.Base64 
-        ( encodeRaw         -- :: Bool -> String -> [Word8]
-        , encodeRawString   -- :: Bool -> String -> String
-        , encodeRawPrim     -- :: Bool -> Char -> Char -> [Word8] -> String
-
-        , formatOutput      -- :: Int    -> Maybe String -> String -> String
-
-        , decode            -- :: String -> [Word8]
-        , decodeToString    -- :: String -> String
-        , decodePrim        -- :: Char -> Char -> String -> [Word8]
-        ) where
-
-import Data.Bits
-import Data.Char
-import Data.Word
-import Data.Maybe
-
-encodeRawString :: Bool -> String -> String
-encodeRawString trail xs = encodeRaw trail (map (fromIntegral.ord) xs)
-
--- | @formatOutput n mbLT str@ formats @str@, splitting it
--- into lines of length @n@. The optional value lets you control what
--- line terminator sequence to use; the default is CRLF (as per MIME.)
-formatOutput :: Int -> Maybe String -> String -> String
-formatOutput n mbTerm str
- | n <= 0    = error ("Codec.MIME.Base64.formatOutput: negative line length " ++ show n)
- | otherwise = chop n str
-   where
-     crlf :: String
-     crlf = fromMaybe "\r\n" mbTerm
-
-     chop _ "" = ""
-     chop i xs =
-       case splitAt i xs of
-         (as,"") -> as
-         (as,bs) -> as ++ crlf ++ chop i bs
-
-encodeRaw :: Bool -> [Word8] -> String
-encodeRaw trail bs = encodeRawPrim trail '+' '/' bs
-
--- | @encodeRawPrim@ lets you control what non-alphanum characters to use
--- (The base64url variation uses @*@ and @-@, for instance.)
--- No support for mapping these to multiple characters in the output though.
-encodeRawPrim :: Bool -> Char -> Char -> [Word8] -> String
-encodeRawPrim trail ch62 ch63 ls = encoder ls
- where
-  trailer xs ys
-   | not trail = xs
-   | otherwise = xs ++ ys
-  f = fromB64 ch62 ch63 
-  encoder []    = []
-  encoder [x]   = trailer (take 2 (encode3 f x 0 0 "")) "=="
-  encoder [x,y] = trailer (take 3 (encode3 f x y 0 "")) "="
-  encoder (x:y:z:ws) = encode3 f x y z (encoder ws)
-
-encode3 :: (Word8 -> Char) -> Word8 -> Word8 -> Word8 -> String -> String
-encode3 f a b c rs = 
-     f (low6 (w24 `shiftR` 18)) :
-     f (low6 (w24 `shiftR` 12)) :
-     f (low6 (w24 `shiftR` 6))  :
-     f (low6 w24) : rs
-   where
-    w24 :: Word32
-    w24 = (fromIntegral a `shiftL` 16) +
-          (fromIntegral b `shiftL` 8)  + 
-           fromIntegral c
-
-decodeToString :: String -> String
-decodeToString str = map (chr.fromIntegral) $ decode str
-
-decode :: String -> [Word8]
-decode str = decodePrim '+' '/' str
-
-decodePrim :: Char -> Char -> String -> [Word8]
-decodePrim ch62 ch63 str =  decoder $ takeUntilEnd str
- where
-  takeUntilEnd "" = []
-  takeUntilEnd ('=':_) = []
-  takeUntilEnd (x:xs) = 
-    case toB64 ch62 ch63 x of
-      Nothing -> takeUntilEnd xs
-      Just b  -> b : takeUntilEnd xs
-
-decoder :: [Word8] -> [Word8]
-decoder [] = []
-decoder [x] = take 1 (decode4 x 0 0 0 [])
-decoder [x,y] = take 1 (decode4 x y 0 0 []) -- upper 4 bits of second val are known to be 0.
-decoder [x,y,z] = take 2 (decode4 x y z 0 [])
-decoder (x:y:z:w:xs) = decode4 x y z w (decoder xs)
-
-decode4 :: Word8 -> Word8 -> Word8 -> Word8 -> [Word8] -> [Word8]
-decode4 a b c d rs =
-  (lowByte (w24 `shiftR` 16)) :
-  (lowByte (w24 `shiftR` 8))  :
-  (lowByte w24) : rs
- where
-  w24 :: Word32
-  w24 =
-   (fromIntegral a) `shiftL` 18 .|.
-   (fromIntegral b) `shiftL` 12 .|.
-   (fromIntegral c) `shiftL` 6  .|.
-   (fromIntegral d)
-
-toB64 :: Char -> Char -> Char -> Maybe Word8
-toB64 a b ch
-  | ch >= 'A' && ch <= 'Z' = Just (fromIntegral (ord ch - ord 'A'))
-  | ch >= 'a' && ch <= 'z' = Just (26 + fromIntegral (ord ch - ord 'a'))
-  | ch >= '0' && ch <= '9' = Just (52 + fromIntegral (ord ch - ord '0'))
-  | ch == a = Just 62
-  | ch == b = Just 63
-  | otherwise = Nothing
-
-fromB64 :: Char -> Char -> Word8 -> Char
-fromB64 ch62 ch63 x 
-  | x < 26    = chr (ord 'A' + xi)
-  | x < 52    = chr (ord 'a' + (xi-26))
-  | x < 62    = chr (ord '0' + (xi-52))
-  | x == 62   = ch62
-  | x == 63   = ch63
-  | otherwise = error ("fromB64: index out of range " ++ show x)
- where
-  xi :: Int
-  xi = fromIntegral x
-
-low6 :: Word32 -> Word8
-low6 x = fromIntegral (x .&. 0x3f)
-
-lowByte :: Word32 -> Word8
-lowByte x = (fromIntegral x) .&. 0xff
-
+--------------------------------------------------------------------
+-- |
+-- Module    : Codec.MIME.Base64
+-- Copyright : (c) 2006-2009, Galois, Inc. 
+-- License   : BSD3
+--
+-- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
+-- Stability : provisional
+-- Portability: portable
+--
+-- 
+-- Base64 decoding and encoding routines, multiple entry
+-- points for either depending on use and level of control
+-- wanted over the encoded output (and its input form on the
+-- decoding side.)
+-- 
+--------------------------------------------------------------------
+module Codec.MIME.Base64 
+        ( encodeRaw         -- :: Bool -> String -> [Word8]
+        , encodeRawString   -- :: Bool -> String -> String
+        , encodeRawPrim     -- :: Bool -> Char -> Char -> [Word8] -> String
+
+        , formatOutput      -- :: Int    -> Maybe String -> String -> String
+
+        , decode            -- :: String -> [Word8]
+        , decodeToString    -- :: String -> String
+        , decodePrim        -- :: Char -> Char -> String -> [Word8]
+        ) where
+
+import Data.Bits
+import Data.Char
+import Data.Word
+import Data.Maybe
+
+encodeRawString :: Bool -> String -> String
+encodeRawString trail xs = encodeRaw trail (map (fromIntegral.ord) xs)
+
+-- | @formatOutput n mbLT str@ formats @str@, splitting it
+-- into lines of length @n@. The optional value lets you control what
+-- line terminator sequence to use; the default is CRLF (as per MIME.)
+formatOutput :: Int -> Maybe String -> String -> String
+formatOutput n mbTerm str
+ | n <= 0    = error ("Codec.MIME.Base64.formatOutput: negative line length " ++ show n)
+ | otherwise = chop n str
+   where
+     crlf :: String
+     crlf = fromMaybe "\r\n" mbTerm
+
+     chop _ "" = ""
+     chop i xs =
+       case splitAt i xs of
+         (as,"") -> as
+         (as,bs) -> as ++ crlf ++ chop i bs
+
+encodeRaw :: Bool -> [Word8] -> String
+encodeRaw trail bs = encodeRawPrim trail '+' '/' bs
+
+-- | @encodeRawPrim@ lets you control what non-alphanum characters to use
+-- (The base64url variation uses @*@ and @-@, for instance.)
+-- No support for mapping these to multiple characters in the output though.
+encodeRawPrim :: Bool -> Char -> Char -> [Word8] -> String
+encodeRawPrim trail ch62 ch63 ls = encoder ls
+ where
+  trailer xs ys
+   | not trail = xs
+   | otherwise = xs ++ ys
+  f = fromB64 ch62 ch63 
+  encoder []    = []
+  encoder [x]   = trailer (take 2 (encode3 f x 0 0 "")) "=="
+  encoder [x,y] = trailer (take 3 (encode3 f x y 0 "")) "="
+  encoder (x:y:z:ws) = encode3 f x y z (encoder ws)
+
+encode3 :: (Word8 -> Char) -> Word8 -> Word8 -> Word8 -> String -> String
+encode3 f a b c rs = 
+     f (low6 (w24 `shiftR` 18)) :
+     f (low6 (w24 `shiftR` 12)) :
+     f (low6 (w24 `shiftR` 6))  :
+     f (low6 w24) : rs
+   where
+    w24 :: Word32
+    w24 = (fromIntegral a `shiftL` 16) +
+          (fromIntegral b `shiftL` 8)  + 
+           fromIntegral c
+
+decodeToString :: String -> String
+decodeToString str = map (chr.fromIntegral) $ decode str
+
+decode :: String -> [Word8]
+decode str = decodePrim '+' '/' str
+
+decodePrim :: Char -> Char -> String -> [Word8]
+decodePrim ch62 ch63 str =  decoder $ takeUntilEnd str
+ where
+  takeUntilEnd "" = []
+  takeUntilEnd ('=':_) = []
+  takeUntilEnd (x:xs) = 
+    case toB64 ch62 ch63 x of
+      Nothing -> takeUntilEnd xs
+      Just b  -> b : takeUntilEnd xs
+
+decoder :: [Word8] -> [Word8]
+decoder [] = []
+decoder [x] = take 1 (decode4 x 0 0 0 [])
+decoder [x,y] = take 1 (decode4 x y 0 0 []) -- upper 4 bits of second val are known to be 0.
+decoder [x,y,z] = take 2 (decode4 x y z 0 [])
+decoder (x:y:z:w:xs) = decode4 x y z w (decoder xs)
+
+decode4 :: Word8 -> Word8 -> Word8 -> Word8 -> [Word8] -> [Word8]
+decode4 a b c d rs =
+  (lowByte (w24 `shiftR` 16)) :
+  (lowByte (w24 `shiftR` 8))  :
+  (lowByte w24) : rs
+ where
+  w24 :: Word32
+  w24 =
+   (fromIntegral a) `shiftL` 18 .|.
+   (fromIntegral b) `shiftL` 12 .|.
+   (fromIntegral c) `shiftL` 6  .|.
+   (fromIntegral d)
+
+toB64 :: Char -> Char -> Char -> Maybe Word8
+toB64 a b ch
+  | ch >= 'A' && ch <= 'Z' = Just (fromIntegral (ord ch - ord 'A'))
+  | ch >= 'a' && ch <= 'z' = Just (26 + fromIntegral (ord ch - ord 'a'))
+  | ch >= '0' && ch <= '9' = Just (52 + fromIntegral (ord ch - ord '0'))
+  | ch == a = Just 62
+  | ch == b = Just 63
+  | otherwise = Nothing
+
+fromB64 :: Char -> Char -> Word8 -> Char
+fromB64 ch62 ch63 x 
+  | x < 26    = chr (ord 'A' + xi)
+  | x < 52    = chr (ord 'a' + (xi-26))
+  | x < 62    = chr (ord '0' + (xi-52))
+  | x == 62   = ch62
+  | x == 63   = ch63
+  | otherwise = error ("fromB64: index out of range " ++ show x)
+ where
+  xi :: Int
+  xi = fromIntegral x
+
+low6 :: Word32 -> Word8
+low6 x = fromIntegral (x .&. 0x3f)
+
+lowByte :: Word32 -> Word8
+lowByte x = (fromIntegral x) .&. 0xff
+
diff --git a/Codec/MIME/Decode.hs b/Codec/MIME/Decode.hs
--- a/Codec/MIME/Decode.hs
+++ b/Codec/MIME/Decode.hs
@@ -1,76 +1,76 @@
---------------------------------------------------------------------
--- |
--- Module    : Codec.MIME.Decode
--- Copyright : (c) 2006-2009, Galois, Inc. 
--- License   : BSD3
---
--- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
--- Stability : provisional
--- Portability: portable
---
--- 
--- 
---------------------------------------------------------------------
-
-module Codec.MIME.Decode where
-
-import Data.Char
-
-import Codec.MIME.QuotedPrintable as QP
-import Codec.MIME.Base64 as Base64
-
--- | @decodeBody enc str@ decodes @str@ according to the scheme
--- specified by @enc@. Currently, @base64@ and @quoted-printable@ are
--- the only two encodings supported. If you supply anything else
--- for @enc@, @decodeBody@ returns @str@.
--- 
-decodeBody :: String -> String -> String
-decodeBody enc body =
- case map toLower enc of
-   "base64"           -> map (chr.fromIntegral) $ Base64.decode body
-   "quoted-printable" -> QP.decode body
-   _ -> body
-
--- Decoding of RFC 2047's "encoded-words" production
--- (as used in email-headers and some HTTP header cases
--- (AtomPub's Slug: header))
-decodeWord :: String -> Maybe (String, String)
-decodeWord str =
-  case str of
-   '=':'?':xs ->
-     case dropLang $ break (\ch -> ch =='?' || ch == '*') xs of
-       (cs,_:x:'?':bs)
-         | isKnownCharset (map toLower cs) ->
-           case toLower x of
-             'q' -> decodeQ cs (break (=='?') bs)
-             'b' -> decodeB cs (break (=='?') bs)
-             _   -> Nothing
-       _ -> Nothing
-   _ -> Nothing
- where
-  isKnownCharset cs = cs `elem` ["iso-8859-1", "us-ascii"]
-
-   -- ignore RFC 2231 extension of permitting a language tag to be supplied
-   -- after the charset.
-  dropLang (as,'*':bs) = (as,dropWhile (/='?') bs)
-  dropLang (as,bs) = (as,bs)
-
-  decodeQ cset (fs,'?':'=':rs) = Just (fromCharset cset (QP.decode fs),rs)
-  decodeQ _ _ = Nothing
-
-  decodeB cset (fs,'?':'=':rs) =
-    Just (fromCharset cset (Base64.decodeToString fs),rs)
-  decodeB _ _ = Nothing
-
-  fromCharset _cset cs = cs
-
-decodeWords :: String -> String
-decodeWords "" = ""
-decodeWords (x:xs)
- | isSpace x = x : decodeWords xs
- | otherwise =
-  case decodeWord (x:xs) of
-    Nothing -> x : decodeWords xs
-    Just (as,bs) -> as ++ decodeWords bs
-
-
+--------------------------------------------------------------------
+-- |
+-- Module    : Codec.MIME.Decode
+-- Copyright : (c) 2006-2009, Galois, Inc. 
+-- License   : BSD3
+--
+-- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
+-- Stability : provisional
+-- Portability: portable
+--
+-- 
+-- 
+--------------------------------------------------------------------
+
+module Codec.MIME.Decode where
+
+import Data.Char
+
+import Codec.MIME.QuotedPrintable as QP
+import Codec.MIME.Base64 as Base64
+
+-- | @decodeBody enc str@ decodes @str@ according to the scheme
+-- specified by @enc@. Currently, @base64@ and @quoted-printable@ are
+-- the only two encodings supported. If you supply anything else
+-- for @enc@, @decodeBody@ returns @str@.
+-- 
+decodeBody :: String -> String -> String
+decodeBody enc body =
+ case map toLower enc of
+   "base64"           -> map (chr.fromIntegral) $ Base64.decode body
+   "quoted-printable" -> QP.decode body
+   _ -> body
+
+-- Decoding of RFC 2047's "encoded-words" production
+-- (as used in email-headers and some HTTP header cases
+-- (AtomPub's Slug: header))
+decodeWord :: String -> Maybe (String, String)
+decodeWord str =
+  case str of
+   '=':'?':xs ->
+     case dropLang $ break (\ch -> ch =='?' || ch == '*') xs of
+       (cs,_:x:'?':bs)
+         | isKnownCharset (map toLower cs) ->
+           case toLower x of
+             'q' -> decodeQ cs (break (=='?') bs)
+             'b' -> decodeB cs (break (=='?') bs)
+             _   -> Nothing
+       _ -> Nothing
+   _ -> Nothing
+ where
+  isKnownCharset cs = cs `elem` ["iso-8859-1", "us-ascii"]
+
+   -- ignore RFC 2231 extension of permitting a language tag to be supplied
+   -- after the charset.
+  dropLang (as,'*':bs) = (as,dropWhile (/='?') bs)
+  dropLang (as,bs) = (as,bs)
+
+  decodeQ cset (fs,'?':'=':rs) = Just (fromCharset cset (QP.decode fs),rs)
+  decodeQ _ _ = Nothing
+
+  decodeB cset (fs,'?':'=':rs) =
+    Just (fromCharset cset (Base64.decodeToString fs),rs)
+  decodeB _ _ = Nothing
+
+  fromCharset _cset cs = cs
+
+decodeWords :: String -> String
+decodeWords "" = ""
+decodeWords (x:xs)
+ | isSpace x = x : decodeWords xs
+ | otherwise =
+  case decodeWord (x:xs) of
+    Nothing -> x : decodeWords xs
+    Just (as,bs) -> as ++ decodeWords bs
+
+
diff --git a/Codec/MIME/Parse.hs b/Codec/MIME/Parse.hs
--- a/Codec/MIME/Parse.hs
+++ b/Codec/MIME/Parse.hs
@@ -1,268 +1,268 @@
---------------------------------------------------------------------
--- |
--- Module    : Codec.MIME.Pare
--- Copyright : (c) 2006-2009, Galois, Inc. 
--- License   : BSD3
---
--- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
--- Stability : provisional
--- Portability: portable
---
--- Parsing MIME content.
--- 
---------------------------------------------------------------------
-module Codec.MIME.Parse
-  ( parseMIMEBody    -- :: [(String,String)] -> String -> MIMEValue
-  , parseMIMEType    -- :: String -> Maybe Type
-  , parseMIMEMessage -- :: String -> MIMEValue
-
-  , parseHeaders     -- :: String -> ([(String,String)], String)
-  , parseMultipart   -- :: Type -> String -> (MIMEValue, String)
-  , parseContentType -- :: String -> Maybe Type
-  , splitMulti       -- :: String -> String -> ([MIMEValue], String)
-  ) where
-
-import Codec.MIME.Type
-import Codec.MIME.Decode
-
-import Data.Char
-import Data.Maybe
-import Data.List
-import Debug.Trace ( trace )
-
-parseMIMEBody :: [(String,String)] -> String -> MIMEValue
-parseMIMEBody headers_in body =
-  case mimeType mty of
-    Multipart{} -> fst (parseMultipart mty body)
-    Message{}   -> fst (parseMultipart mty body)
-    _           -> nullMIMEValue 
-                    { mime_val_type    = mty
-	            , mime_val_disp    = parseContentDisp headers
-	            , mime_val_content = Single (processBody headers body)
- 	            }
-
- where headers = [ (map toLower k,v) | (k,v) <- headers_in ]
-       mty = fromMaybe defaultType
-                       (parseContentType =<< lookupField "content-type" headers)
-defaultType :: Type
-defaultType = Type { mimeType   = Text "plain"
-                   , mimeParams = [("charset", "us-ascii")]
-                   }
-
-parseContentDisp :: [(String,String)] -> Maybe Disposition
-parseContentDisp headers =
-  (processDisp . dropFoldingWSP) =<< lookupField "content-disposition" headers
-  where
-  processDisp "" = Nothing
-  processDisp xs = Just $
-    case break (\ch -> isSpace ch || ch == ';') xs of
-      (as,"") -> Disposition { dispType = toDispType (map toLower as)
-                             , dispParams = []
-                             }
-      (as,bs) -> Disposition { dispType = toDispType (map toLower as)
-                             , dispParams = processParams (parseParams bs)
-                             }
-
-  processParams = map procP
-    where
-    procP (as,val)
-      | "name" == asl              = Name val
-      | "filename" == asl          = Filename val
-      | "creation-date" == asl     = CreationDate val
-      | "modification-date" == asl = ModDate val
-      | "read-date" == asl         = ReadDate val
-      | "size" == asl              = Size val
-      | otherwise                  = OtherParam (map toLower as) val
-      where asl = map toLower as
-
-  toDispType t = case t of
-                   "inline"     -> DispInline
-                   "attachment" -> DispAttachment
-                   "form-data"  -> DispFormData
-                   _            -> DispOther t
-
-processBody :: [(String,String)] -> String -> String
-processBody headers body =
-  case lookupField "content-transfer-encoding" headers of
-    Nothing -> body
-    Just v  -> decodeBody v body
-
-parseMIMEMessage :: String -> MIMEValue
-parseMIMEMessage entity =
-  case parseHeaders entity of
-   (as,bs) -> parseMIMEBody as bs
-
-parseHeaders :: String -> ([(String,String)], String)
-parseHeaders str =
-  case findFieldName "" str of
-    Left (nm, rs) -> parseFieldValue nm (dropFoldingWSP rs)
-    Right body    -> ([],body)
- where
-  findFieldName _acc "" = Right ""
-  findFieldName _acc ('\r':'\n':xs) = Right xs
-  findFieldName acc (':':xs) = Left (reverse (dropWhile isHSpace acc), xs)
-  findFieldName acc (x:xs) = findFieldName (x:acc) xs
-
-  parseFieldValue nm xs =
-    case takeUntilCRLF xs of
-      (as,"") -> ([(nm,as)],"")
-      (as,bs) -> let (zs,ys) = parseHeaders bs in ((nm,as):zs,ys)
-
-parseMultipart :: Type -> String -> (MIMEValue, String)
-parseMultipart mty body =
-  case lookupField "boundary" (mimeParams mty) of
-    Nothing -> trace ("Multipart mime type, " ++ showType mty ++
-      ", has no required boundary parameter. Defaulting to text/plain") $
-      (nullMIMEValue{ mime_val_type = defaultType
-                    , mime_val_disp = Nothing
-		    , mime_val_content = Single body
-		    }, "")
-    Just bnd -> (nullMIMEValue { mime_val_type = mty
-                               , mime_val_disp = Nothing
-			       , mime_val_content = Multi vals
-			       }, rs)
-      where (vals,rs) = splitMulti bnd body
-
-splitMulti :: String -> String -> ([MIMEValue], String)
-splitMulti bnd body_in =
-  -- Note: we insert a CRLF if it looks as if the boundary string starts
-  -- right off the bat.  No harm done if this turns out to be incorrect.
-  let body = case body_in of
-                '-':'-':_ -> ('\r':'\n':body_in)
-                _ -> body_in
-  in case untilMatch dashBoundary body of
-       Nothing           -> ([],"")
-       Just ('-':'-':xs) -> ([],xs)
-       Just xs           -> splitMulti1 (dropTrailer xs)
-
- where
-  dashBoundary = ("\r\n--" ++ bnd)
-
-  splitMulti1 xs =
-    case matchUntil dashBoundary xs of
-      ("","") -> ([],"")
-      (as,"") -> ([parseMIMEMessage as],"")
-      (as,'-':'-':bs) -> ([parseMIMEMessage as], dropTrailer bs)
-      (as,bs) -> let (zs,ys) = splitMulti1 (dropTrailer bs)
-                 in ((parseMIMEMessage as) : zs,ys)
-
-  dropTrailer xs =
-    case dropWhile isHSpace xs of
-      '\r':'\n':xs1 -> xs1
-      xs1 -> xs1 -- hmm, flag an error?
-
-parseMIMEType :: String -> Maybe Type
-parseMIMEType = parseContentType
-
-parseContentType :: String -> Maybe Type
-parseContentType str =
-   case break (=='/') (dropFoldingWSP str) of
-       (maj,_:minor) ->
-          case break (\ ch -> isHSpace ch || isTSpecial ch) minor of
-            (as,bs)  -> 
-               Just Type { mimeType = toType maj as
-                         , mimeParams = parseParams (dropWhile isHSpace bs)
-                         }
-       _ -> trace ("unable to parse content-type: " ++ show str) $ Nothing
- where
-  toType a b = case lookupField (map toLower a) mediaTypes of
-                 Just ctor -> ctor b
-                 _ -> Other a b
-
-parseParams :: String -> [(String,String)]
-parseParams "" = []
-parseParams (';':xs) =
-    case break (=='=') (dropFoldingWSP xs) of
-      (_,[]) -> []
-      (nm_raw,_:vs) ->
-        case vs of
-          '"':vs1 ->
-             case break (=='"') vs1 of
-               (val,"") -> [(nm,val)]
-               (val,_:zs) -> (nm,val):parseParams (dropWhile isHSpace zs)
-          _ -> case break (\ ch -> isHSpace ch || isTSpecial ch) vs of
-                 (val,zs) -> (nm,val):parseParams (dropWhile isHSpace zs)
-       where
-        nm = map toLower nm_raw
-
-parseParams cs = trace ("Codec.MIME.Parse.parseParams: curious param value -- " ++ show cs) []
-
-mediaTypes :: [(String, String -> MIMEType)]
-mediaTypes =
-  [ ("multipart",   (Multipart . toMultipart))
-  , ("application", Application)
-  , ("audio",       Audio)
-  , ("image",       Image)
-  , ("message",     Message)
-  , ("model",       Model)
-  , ("text",        Text)
-  , ("video",       Video)
-  ]
- where toMultipart b = fromMaybe other (lookupField (map toLower b) multipartTypes)
-          where other = case b of
-                          'x':'-':_ -> Extension b
-                          _ -> OtherMulti b
-
-multipartTypes :: [(String, Multipart)]
-multipartTypes =
-  [ ("alternative", Alternative)
-  , ("byteranges",  Byteranges)
-  , ("digest",      Digest)
-  , ("encrypted",   Encrypted)
-  , ("form-data",   FormData)
-  , ("mixed",       Mixed)
-  , ("parallel",    Parallel)
-  , ("related",     Related)
-  , ("signed",      Signed)
-  ]
-
-untilMatch :: String -> String -> Maybe String
-untilMatch str xs = go str xs
-  where go ""     rs      = Just rs
-        go _      ""      = Nothing
-        go (a:as) (b:bs)  = if a == b then go as bs else go str bs
-
-matchUntil :: String -> String -> (String, String)
-matchUntil _   "" = ("", "")
-matchUntil str xs
-    -- slow, but it'll do for now.
-  | str `isPrefixOf` xs = ("", drop (length str) xs)
-matchUntil str (x:xs) = let (as,bs) = matchUntil str xs in (x:as,bs)
-
-isHSpace :: Char -> Bool
-isHSpace c = c == ' ' || c == '\t'
-
-isTSpecial :: Char -> Bool
-isTSpecial x = x `elem` "()<>@,;:\\\"/[]?=" -- "
-
-dropFoldingWSP :: String -> String
-dropFoldingWSP "" = ""
-dropFoldingWSP (x:xs)
- | isHSpace x = dropFoldingWSP xs
-dropFoldingWSP ('\r':'\n':x:xs)
- | isHSpace x = dropFoldingWSP xs
-dropFoldingWSP (x:xs) = x:xs
-
-takeUntilCRLF :: String -> (String, String)
-takeUntilCRLF str = go "" str
- where
-  go acc "" = (reverse (dropWhile isHSpace acc), "")
-  go acc ('\r':'\n':x:xs)
-   | isHSpace x = go (' ':acc) xs
-   | otherwise  = (reverse (dropWhile isHSpace acc), x:xs)
-  go acc (x:xs) = go (x:acc) xs
-
--- case in-sensitive lookup of field names or attributes\/parameters.
-lookupField :: String -> [(String,a)] -> Maybe a
-lookupField n ns = 
-   -- assume that inputs have been mostly normalized already 
-   -- (i.e., lower-cased), but should the lookup fail fall back
-   -- to a second try where we do normalize before giving up.
-  case lookup n ns of
-    x@Just{} -> x
-    Nothing  -> 
-      let nl = map toLower n in
-      case find (\ (y,_) -> nl == map toLower y) ns of
-        Nothing -> Nothing
-	Just (_,x)  -> Just x
-      
+--------------------------------------------------------------------
+-- |
+-- Module    : Codec.MIME.Pare
+-- Copyright : (c) 2006-2009, Galois, Inc. 
+-- License   : BSD3
+--
+-- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
+-- Stability : provisional
+-- Portability: portable
+--
+-- Parsing MIME content.
+-- 
+--------------------------------------------------------------------
+module Codec.MIME.Parse
+  ( parseMIMEBody    -- :: [(String,String)] -> String -> MIMEValue
+  , parseMIMEType    -- :: String -> Maybe Type
+  , parseMIMEMessage -- :: String -> MIMEValue
+
+  , parseHeaders     -- :: String -> ([(String,String)], String)
+  , parseMultipart   -- :: Type -> String -> (MIMEValue, String)
+  , parseContentType -- :: String -> Maybe Type
+  , splitMulti       -- :: String -> String -> ([MIMEValue], String)
+  ) where
+
+import Codec.MIME.Type
+import Codec.MIME.Decode
+
+import Data.Char
+import Data.Maybe
+import Data.List
+import Debug.Trace ( trace )
+
+parseMIMEBody :: [(String,String)] -> String -> MIMEValue
+parseMIMEBody headers_in body =
+  case mimeType mty of
+    Multipart{} -> fst (parseMultipart mty body)
+    Message{}   -> fst (parseMultipart mty body)
+    _           -> nullMIMEValue 
+                    { mime_val_type    = mty
+	            , mime_val_disp    = parseContentDisp headers
+	            , mime_val_content = Single (processBody headers body)
+ 	            }
+
+ where headers = [ (map toLower k,v) | (k,v) <- headers_in ]
+       mty = fromMaybe defaultType
+                       (parseContentType =<< lookupField "content-type" headers)
+defaultType :: Type
+defaultType = Type { mimeType   = Text "plain"
+                   , mimeParams = [("charset", "us-ascii")]
+                   }
+
+parseContentDisp :: [(String,String)] -> Maybe Disposition
+parseContentDisp headers =
+  (processDisp . dropFoldingWSP) =<< lookupField "content-disposition" headers
+  where
+  processDisp "" = Nothing
+  processDisp xs = Just $
+    case break (\ch -> isSpace ch || ch == ';') xs of
+      (as,"") -> Disposition { dispType = toDispType (map toLower as)
+                             , dispParams = []
+                             }
+      (as,bs) -> Disposition { dispType = toDispType (map toLower as)
+                             , dispParams = processParams (parseParams bs)
+                             }
+
+  processParams = map procP
+    where
+    procP (as,val)
+      | "name" == asl              = Name val
+      | "filename" == asl          = Filename val
+      | "creation-date" == asl     = CreationDate val
+      | "modification-date" == asl = ModDate val
+      | "read-date" == asl         = ReadDate val
+      | "size" == asl              = Size val
+      | otherwise                  = OtherParam (map toLower as) val
+      where asl = map toLower as
+
+  toDispType t = case t of
+                   "inline"     -> DispInline
+                   "attachment" -> DispAttachment
+                   "form-data"  -> DispFormData
+                   _            -> DispOther t
+
+processBody :: [(String,String)] -> String -> String
+processBody headers body =
+  case lookupField "content-transfer-encoding" headers of
+    Nothing -> body
+    Just v  -> decodeBody v body
+
+parseMIMEMessage :: String -> MIMEValue
+parseMIMEMessage entity =
+  case parseHeaders entity of
+   (as,bs) -> parseMIMEBody as bs
+
+parseHeaders :: String -> ([(String,String)], String)
+parseHeaders str =
+  case findFieldName "" str of
+    Left (nm, rs) -> parseFieldValue nm (dropFoldingWSP rs)
+    Right body    -> ([],body)
+ where
+  findFieldName _acc "" = Right ""
+  findFieldName _acc ('\r':'\n':xs) = Right xs
+  findFieldName acc (':':xs) = Left (reverse (dropWhile isHSpace acc), xs)
+  findFieldName acc (x:xs) = findFieldName (x:acc) xs
+
+  parseFieldValue nm xs =
+    case takeUntilCRLF xs of
+      (as,"") -> ([(nm,as)],"")
+      (as,bs) -> let (zs,ys) = parseHeaders bs in ((nm,as):zs,ys)
+
+parseMultipart :: Type -> String -> (MIMEValue, String)
+parseMultipart mty body =
+  case lookupField "boundary" (mimeParams mty) of
+    Nothing -> trace ("Multipart mime type, " ++ showType mty ++
+      ", has no required boundary parameter. Defaulting to text/plain") $
+      (nullMIMEValue{ mime_val_type = defaultType
+                    , mime_val_disp = Nothing
+		    , mime_val_content = Single body
+		    }, "")
+    Just bnd -> (nullMIMEValue { mime_val_type = mty
+                               , mime_val_disp = Nothing
+			       , mime_val_content = Multi vals
+			       }, rs)
+      where (vals,rs) = splitMulti bnd body
+
+splitMulti :: String -> String -> ([MIMEValue], String)
+splitMulti bnd body_in =
+  -- Note: we insert a CRLF if it looks as if the boundary string starts
+  -- right off the bat.  No harm done if this turns out to be incorrect.
+  let body = case body_in of
+                '-':'-':_ -> ('\r':'\n':body_in)
+                _ -> body_in
+  in case untilMatch dashBoundary body of
+       Nothing           -> ([],"")
+       Just ('-':'-':xs) -> ([],xs)
+       Just xs           -> splitMulti1 (dropTrailer xs)
+
+ where
+  dashBoundary = ("\r\n--" ++ bnd)
+
+  splitMulti1 xs =
+    case matchUntil dashBoundary xs of
+      ("","") -> ([],"")
+      (as,"") -> ([parseMIMEMessage as],"")
+      (as,'-':'-':bs) -> ([parseMIMEMessage as], dropTrailer bs)
+      (as,bs) -> let (zs,ys) = splitMulti1 (dropTrailer bs)
+                 in ((parseMIMEMessage as) : zs,ys)
+
+  dropTrailer xs =
+    case dropWhile isHSpace xs of
+      '\r':'\n':xs1 -> xs1
+      xs1 -> xs1 -- hmm, flag an error?
+
+parseMIMEType :: String -> Maybe Type
+parseMIMEType = parseContentType
+
+parseContentType :: String -> Maybe Type
+parseContentType str =
+   case break (=='/') (dropFoldingWSP str) of
+       (maj,_:minor) ->
+          case break (\ ch -> isHSpace ch || isTSpecial ch) minor of
+            (as,bs)  -> 
+               Just Type { mimeType = toType maj as
+                         , mimeParams = parseParams (dropWhile isHSpace bs)
+                         }
+       _ -> trace ("unable to parse content-type: " ++ show str) $ Nothing
+ where
+  toType a b = case lookupField (map toLower a) mediaTypes of
+                 Just ctor -> ctor b
+                 _ -> Other a b
+
+parseParams :: String -> [(String,String)]
+parseParams "" = []
+parseParams (';':xs) =
+    case break (=='=') (dropFoldingWSP xs) of
+      (_,[]) -> []
+      (nm_raw,_:vs) ->
+        case vs of
+          '"':vs1 ->
+             case break (=='"') vs1 of
+               (val,"") -> [(nm,val)]
+               (val,_:zs) -> (nm,val):parseParams (dropWhile isHSpace zs)
+          _ -> case break (\ ch -> isHSpace ch || isTSpecial ch) vs of
+                 (val,zs) -> (nm,val):parseParams (dropWhile isHSpace zs)
+       where
+        nm = map toLower nm_raw
+
+parseParams cs = trace ("Codec.MIME.Parse.parseParams: curious param value -- " ++ show cs) []
+
+mediaTypes :: [(String, String -> MIMEType)]
+mediaTypes =
+  [ ("multipart",   (Multipart . toMultipart))
+  , ("application", Application)
+  , ("audio",       Audio)
+  , ("image",       Image)
+  , ("message",     Message)
+  , ("model",       Model)
+  , ("text",        Text)
+  , ("video",       Video)
+  ]
+ where toMultipart b = fromMaybe other (lookupField (map toLower b) multipartTypes)
+          where other = case b of
+                          'x':'-':_ -> Extension b
+                          _ -> OtherMulti b
+
+multipartTypes :: [(String, Multipart)]
+multipartTypes =
+  [ ("alternative", Alternative)
+  , ("byteranges",  Byteranges)
+  , ("digest",      Digest)
+  , ("encrypted",   Encrypted)
+  , ("form-data",   FormData)
+  , ("mixed",       Mixed)
+  , ("parallel",    Parallel)
+  , ("related",     Related)
+  , ("signed",      Signed)
+  ]
+
+untilMatch :: String -> String -> Maybe String
+untilMatch str xs = go str xs
+  where go ""     rs      = Just rs
+        go _      ""      = Nothing
+        go (a:as) (b:bs)  = if a == b then go as bs else go str bs
+
+matchUntil :: String -> String -> (String, String)
+matchUntil _   "" = ("", "")
+matchUntil str xs
+    -- slow, but it'll do for now.
+  | str `isPrefixOf` xs = ("", drop (length str) xs)
+matchUntil str (x:xs) = let (as,bs) = matchUntil str xs in (x:as,bs)
+
+isHSpace :: Char -> Bool
+isHSpace c = c == ' ' || c == '\t'
+
+isTSpecial :: Char -> Bool
+isTSpecial x = x `elem` "()<>@,;:\\\"/[]?=" -- "
+
+dropFoldingWSP :: String -> String
+dropFoldingWSP "" = ""
+dropFoldingWSP (x:xs)
+ | isHSpace x = dropFoldingWSP xs
+dropFoldingWSP ('\r':'\n':x:xs)
+ | isHSpace x = dropFoldingWSP xs
+dropFoldingWSP (x:xs) = x:xs
+
+takeUntilCRLF :: String -> (String, String)
+takeUntilCRLF str = go "" str
+ where
+  go acc "" = (reverse (dropWhile isHSpace acc), "")
+  go acc ('\r':'\n':x:xs)
+   | isHSpace x = go (' ':acc) xs
+   | otherwise  = (reverse (dropWhile isHSpace acc), x:xs)
+  go acc (x:xs) = go (x:acc) xs
+
+-- case in-sensitive lookup of field names or attributes\/parameters.
+lookupField :: String -> [(String,a)] -> Maybe a
+lookupField n ns = 
+   -- assume that inputs have been mostly normalized already 
+   -- (i.e., lower-cased), but should the lookup fail fall back
+   -- to a second try where we do normalize before giving up.
+  case lookup n ns of
+    x@Just{} -> x
+    Nothing  -> 
+      let nl = map toLower n in
+      case find (\ (y,_) -> nl == map toLower y) ns of
+        Nothing -> Nothing
+	Just (_,x)  -> Just x
+      
diff --git a/Codec/MIME/QuotedPrintable.hs b/Codec/MIME/QuotedPrintable.hs
--- a/Codec/MIME/QuotedPrintable.hs
+++ b/Codec/MIME/QuotedPrintable.hs
@@ -1,66 +1,66 @@
---------------------------------------------------------------------
--- |
--- Module    : Codec.MIME.QuotedPrintable
--- Copyright : (c) 2006-2009, Galois, Inc. 
--- License   : BSD3
---
--- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
--- Stability : provisional
--- Portability:
---
--- To and from QP content encoding.
---
---------------------------------------------------------------------
-module Codec.MIME.QuotedPrintable 
-       ( decode -- :: String -> String
-       , encode -- :: String -> String
-       ) where
-
-import Data.Char
-
--- | 'decode' incoming quoted-printable content, stripping
--- out soft line breaks and translating @=XY@ sequences
--- into their decoded byte\/octet. The output encoding\/representation 
--- is still a String, not a sequence of bytes.
-decode :: String -> String
-decode "" = ""
-decode ('=':'\r':'\n':xs) = decode xs -- soft line break.
-decode ('=':x1:x2:xs)
- | isHexDigit x1 && isHexDigit x2 =
-    chr (digitToInt x1 * 16 + digitToInt x2) : decode xs
-decode ('=':xs) = '=':decode xs
-              -- make it explicit that we propagate other '=' occurrences.
-decode (x1:xs) = x1:decode xs
-
--- | 'encode' converts a sequence of characeter _octets_ into
--- quoted-printable form; suitable for transmission in MIME
--- payloads. Note the stress on _octets_; it is assumed that
--- you have already converted Unicode into a <=8-bit encoding
--- (UTF-8, most likely.)
-encode :: String -> String
-encode xs = encodeLength 0 xs
-
--- | @encodeLength llen str@ is the worker function during encoding.
--- The extra argument @llen@ tracks the current column for the line
--- being processed. Soft line breaks are inserted if a line exceeds
--- a max length.
-encodeLength :: Int -> String -> String
-encodeLength _ "" = ""
-encodeLength n (x:xs)
- | n >= 72  = '=':'\r':'\n':encodeLength 0 (x:xs)
-encodeLength _ ('=':xs) 
- = '=':'3':'D':encodeLength 0 xs
-encodeLength n (x:xs)
- | ox >= 0x100 = error ("QuotedPrintable.encode: encountered > 8 bit character: " ++ show (x,ox))
- | n >= 72     = '=':'\r':'\n':encodeLength 0 (x:xs)
- | ox >= 0x21 && ox <= 0x7e = x : encodeLength (n+1) xs
- | ox == 0x09 || ox == 0x20 = x : encodeLength (n+1) xs
- | otherwise = '=':showH (ox `div` 0x10): showH (ox `mod` 0x10):encodeLength (n+3) xs
- where
-  ox = ord x
-  showH v
-   | v < 10    = chr (ord_0 + v)
-   | otherwise = chr (ord_A + (v-10))
-   
-  ord_0 = ord '0'
-  ord_A = ord 'A'
+--------------------------------------------------------------------
+-- |
+-- Module    : Codec.MIME.QuotedPrintable
+-- Copyright : (c) 2006-2009, Galois, Inc. 
+-- License   : BSD3
+--
+-- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
+-- Stability : provisional
+-- Portability:
+--
+-- To and from QP content encoding.
+--
+--------------------------------------------------------------------
+module Codec.MIME.QuotedPrintable 
+       ( decode -- :: String -> String
+       , encode -- :: String -> String
+       ) where
+
+import Data.Char
+
+-- | 'decode' incoming quoted-printable content, stripping
+-- out soft line breaks and translating @=XY@ sequences
+-- into their decoded byte\/octet. The output encoding\/representation 
+-- is still a String, not a sequence of bytes.
+decode :: String -> String
+decode "" = ""
+decode ('=':'\r':'\n':xs) = decode xs -- soft line break.
+decode ('=':x1:x2:xs)
+ | isHexDigit x1 && isHexDigit x2 =
+    chr (digitToInt x1 * 16 + digitToInt x2) : decode xs
+decode ('=':xs) = '=':decode xs
+              -- make it explicit that we propagate other '=' occurrences.
+decode (x1:xs) = x1:decode xs
+
+-- | 'encode' converts a sequence of characeter _octets_ into
+-- quoted-printable form; suitable for transmission in MIME
+-- payloads. Note the stress on _octets_; it is assumed that
+-- you have already converted Unicode into a <=8-bit encoding
+-- (UTF-8, most likely.)
+encode :: String -> String
+encode xs = encodeLength 0 xs
+
+-- | @encodeLength llen str@ is the worker function during encoding.
+-- The extra argument @llen@ tracks the current column for the line
+-- being processed. Soft line breaks are inserted if a line exceeds
+-- a max length.
+encodeLength :: Int -> String -> String
+encodeLength _ "" = ""
+encodeLength n (x:xs)
+ | n >= 72  = '=':'\r':'\n':encodeLength 0 (x:xs)
+encodeLength _ ('=':xs) 
+ = '=':'3':'D':encodeLength 0 xs
+encodeLength n (x:xs)
+ | ox >= 0x100 = error ("QuotedPrintable.encode: encountered > 8 bit character: " ++ show (x,ox))
+ | n >= 72     = '=':'\r':'\n':encodeLength 0 (x:xs)
+ | ox >= 0x21 && ox <= 0x7e = x : encodeLength (n+1) xs
+ | ox == 0x09 || ox == 0x20 = x : encodeLength (n+1) xs
+ | otherwise = '=':showH (ox `div` 0x10): showH (ox `mod` 0x10):encodeLength (n+3) xs
+ where
+  ox = ord x
+  showH v
+   | v < 10    = chr (ord_0 + v)
+   | otherwise = chr (ord_A + (v-10))
+   
+  ord_0 = ord '0'
+  ord_A = ord 'A'
diff --git a/Codec/MIME/Type.hs b/Codec/MIME/Type.hs
--- a/Codec/MIME/Type.hs
+++ b/Codec/MIME/Type.hs
@@ -1,187 +1,187 @@
---------------------------------------------------------------------
--- |
--- Module    : Codec.MIME.Type
--- Copyright : (c) 2006-2009, Galois, Inc. 
--- License   : BSD3
---
--- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
--- Stability : provisional
--- Portability: portable
---
--- 
--- Representing MIME types and values.
--- 
---------------------------------------------------------------------
-module Codec.MIME.Type where
-
-import Data.List ( concatMap, isSuffixOf )
-
-data Type
- = Type
-    { mimeType   :: MIMEType
-    , mimeParams :: [(String,String)]
-    } deriving ( Show, Ord, Eq )
-
--- | The @null@ MIME record type value; currently a @text/plain@.
-nullType :: Type
-nullType = Type
-    { mimeType   = Text "plain"
-    , mimeParams = []
-    }
-
-showType :: Type -> String
-showType t = showMIMEType (mimeType t) ++ showMIMEParams (mimeParams t)
-
-showMIMEParams :: [(String,String)] -> String
-showMIMEParams ps = concatMap showP ps
- where 
-  showP (a,b) = ';':' ':a ++ '=':'"':b ++ "\""
-
-
-data MIMEType
- = Application SubType
- | Audio       SubType
- | Image       SubType
- | Message     SubType
- | Model       SubType
- | Multipart   Multipart
- | Text        TextType
- | Video       SubType
- | Other       String SubType
-   deriving ( Show, Ord, Eq )
-
-showMIMEType :: MIMEType -> String
-showMIMEType t = 
- case t of
-   Application s -> "application/"++s
-   Audio s       -> "audio/"++s
-   Image s       -> "image/"++s
-   Message s     -> "message/"++s
-   Model s       -> "model/"++s
-   Multipart s   -> "multipart/"++showMultipart s
-   Text s        -> "text/"++s
-   Video s       -> "video/"++s
-   Other a b     -> a ++ '/':b
-
--- | a (type, subtype) MIME pair.
-data MIMEPair
- = MIMEPair String SubType
-   deriving ( Eq )
-
-showMIMEPair :: MIMEPair -> String
-showMIMEPair (MIMEPair a b) = a ++ '/':b
-
--- | default subtype representation.
-type SubType = String
-
--- | subtype for text content; currently just a string.
-type TextType = SubType
-
-subTypeString :: Type -> String
-subTypeString t = 
-  case break (=='/') (showMIMEType (mimeType t)) of
-   (_,"") -> ""
-   (_,_:bs) -> bs
-
-majTypeString :: Type -> String
-majTypeString t = 
-  case break (=='/') (showMIMEType (mimeType t)) of
-   (as,_) -> as
-
-data Multipart
- = Alternative
- | Byteranges
- | Digest
- | Encrypted
- | FormData
- | Mixed
- | Parallel
- | Related
- | Signed
- | Extension  String  -- ^ e.g., 'x-foo' (i.e., includes the 'x-' bit)
- | OtherMulti String  -- unrecognized\/uninterpreted.
-                      -- (e.g., appledouble, voice-message, etc.)
-   deriving ( Show, Ord, Eq )
-
-isXmlBased :: Type -> Bool
-isXmlBased t = 
-  case mimeType t of
-     Multipart{} -> False
-     _ -> "+xml" `isSuffixOf` subTypeString t
-
-isXmlType :: Type -> Bool
-isXmlType t = isXmlBased t ||
-  case mimeType t of
-    Application s -> s `elem` xml_media_types
-    Text s        -> s `elem` xml_media_types
-    _             -> False
- where
-    -- Note: xml-dtd isn't considered an XML type here.
-  xml_media_types :: [String]
-  xml_media_types = 
-    [ "xml"
-    , "xml-external-parsed-entity"
-    ]
-  
-
-showMultipart :: Multipart -> String
-showMultipart m = 
- case m of
-   Alternative -> "alternative"
-   Byteranges  -> "byteranges"
-   Digest      -> "digest"
-   Encrypted   -> "encrypted"
-   FormData    -> "form-data"
-   Mixed       -> "mixed"
-   Parallel    -> "parallel"
-   Related     -> "related"
-   Signed      -> "signed"
-   Extension e -> e
-   OtherMulti e -> e
-   
-type Content = String
-
-data MIMEValue = MIMEValue
-      { mime_val_type     :: Type
-      , mime_val_disp     :: Maybe Disposition
-      , mime_val_content  :: MIMEContent
-      , mime_val_headers  :: [(String,String)]
-      , mime_val_inc_type :: Bool
-      } deriving ( Show, Eq )
-
-nullMIMEValue :: MIMEValue
-nullMIMEValue = MIMEValue
-      { mime_val_type     = nullType
-      , mime_val_disp     = Nothing
-      , mime_val_content  = Multi []
-      , mime_val_headers  = []
-      , mime_val_inc_type = True
-      } 
-
-data MIMEContent 
-  = Single Content
-  | Multi [MIMEValue]
-    deriving (Eq,Show)
-   
-data Disposition
- = Disposition
-     { dispType   :: DispType
-     , dispParams :: [DispParam]
-     } deriving ( Show, Eq )
-
-data DispType
- = DispInline
- | DispAttachment
- | DispFormData
- | DispOther String
-   deriving ( Show, Eq)
-
-data DispParam
- = Name String
- | Filename String
- | CreationDate String
- | ModDate String
- | ReadDate String
- | Size String
- | OtherParam String String
-   deriving ( Show, Eq)
+--------------------------------------------------------------------
+-- |
+-- Module    : Codec.MIME.Type
+-- Copyright : (c) 2006-2009, Galois, Inc. 
+-- License   : BSD3
+--
+-- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
+-- Stability : provisional
+-- Portability: portable
+--
+-- 
+-- Representing MIME types and values.
+-- 
+--------------------------------------------------------------------
+module Codec.MIME.Type where
+
+import Data.List ( concatMap, isSuffixOf )
+
+data Type
+ = Type
+    { mimeType   :: MIMEType
+    , mimeParams :: [(String,String)]
+    } deriving ( Show, Ord, Eq )
+
+-- | The @null@ MIME record type value; currently a @text/plain@.
+nullType :: Type
+nullType = Type
+    { mimeType   = Text "plain"
+    , mimeParams = []
+    }
+
+showType :: Type -> String
+showType t = showMIMEType (mimeType t) ++ showMIMEParams (mimeParams t)
+
+showMIMEParams :: [(String,String)] -> String
+showMIMEParams ps = concatMap showP ps
+ where 
+  showP (a,b) = ';':' ':a ++ '=':'"':b ++ "\""
+
+
+data MIMEType
+ = Application SubType
+ | Audio       SubType
+ | Image       SubType
+ | Message     SubType
+ | Model       SubType
+ | Multipart   Multipart
+ | Text        TextType
+ | Video       SubType
+ | Other       String SubType
+   deriving ( Show, Ord, Eq )
+
+showMIMEType :: MIMEType -> String
+showMIMEType t = 
+ case t of
+   Application s -> "application/"++s
+   Audio s       -> "audio/"++s
+   Image s       -> "image/"++s
+   Message s     -> "message/"++s
+   Model s       -> "model/"++s
+   Multipart s   -> "multipart/"++showMultipart s
+   Text s        -> "text/"++s
+   Video s       -> "video/"++s
+   Other a b     -> a ++ '/':b
+
+-- | a (type, subtype) MIME pair.
+data MIMEPair
+ = MIMEPair String SubType
+   deriving ( Eq )
+
+showMIMEPair :: MIMEPair -> String
+showMIMEPair (MIMEPair a b) = a ++ '/':b
+
+-- | default subtype representation.
+type SubType = String
+
+-- | subtype for text content; currently just a string.
+type TextType = SubType
+
+subTypeString :: Type -> String
+subTypeString t = 
+  case break (=='/') (showMIMEType (mimeType t)) of
+   (_,"") -> ""
+   (_,_:bs) -> bs
+
+majTypeString :: Type -> String
+majTypeString t = 
+  case break (=='/') (showMIMEType (mimeType t)) of
+   (as,_) -> as
+
+data Multipart
+ = Alternative
+ | Byteranges
+ | Digest
+ | Encrypted
+ | FormData
+ | Mixed
+ | Parallel
+ | Related
+ | Signed
+ | Extension  String  -- ^ e.g., 'x-foo' (i.e., includes the 'x-' bit)
+ | OtherMulti String  -- unrecognized\/uninterpreted.
+                      -- (e.g., appledouble, voice-message, etc.)
+   deriving ( Show, Ord, Eq )
+
+isXmlBased :: Type -> Bool
+isXmlBased t = 
+  case mimeType t of
+     Multipart{} -> False
+     _ -> "+xml" `isSuffixOf` subTypeString t
+
+isXmlType :: Type -> Bool
+isXmlType t = isXmlBased t ||
+  case mimeType t of
+    Application s -> s `elem` xml_media_types
+    Text s        -> s `elem` xml_media_types
+    _             -> False
+ where
+    -- Note: xml-dtd isn't considered an XML type here.
+  xml_media_types :: [String]
+  xml_media_types = 
+    [ "xml"
+    , "xml-external-parsed-entity"
+    ]
+  
+
+showMultipart :: Multipart -> String
+showMultipart m = 
+ case m of
+   Alternative -> "alternative"
+   Byteranges  -> "byteranges"
+   Digest      -> "digest"
+   Encrypted   -> "encrypted"
+   FormData    -> "form-data"
+   Mixed       -> "mixed"
+   Parallel    -> "parallel"
+   Related     -> "related"
+   Signed      -> "signed"
+   Extension e -> e
+   OtherMulti e -> e
+   
+type Content = String
+
+data MIMEValue = MIMEValue
+      { mime_val_type     :: Type
+      , mime_val_disp     :: Maybe Disposition
+      , mime_val_content  :: MIMEContent
+      , mime_val_headers  :: [(String,String)]
+      , mime_val_inc_type :: Bool
+      } deriving ( Show, Eq )
+
+nullMIMEValue :: MIMEValue
+nullMIMEValue = MIMEValue
+      { mime_val_type     = nullType
+      , mime_val_disp     = Nothing
+      , mime_val_content  = Multi []
+      , mime_val_headers  = []
+      , mime_val_inc_type = True
+      } 
+
+data MIMEContent 
+  = Single Content
+  | Multi [MIMEValue]
+    deriving (Eq,Show)
+   
+data Disposition
+ = Disposition
+     { dispType   :: DispType
+     , dispParams :: [DispParam]
+     } deriving ( Show, Eq )
+
+data DispType
+ = DispInline
+ | DispAttachment
+ | DispFormData
+ | DispOther String
+   deriving ( Show, Eq)
+
+data DispParam
+ = Name String
+ | Filename String
+ | CreationDate String
+ | ModDate String
+ | ReadDate String
+ | Size String
+ | OtherParam String String
+   deriving ( Show, Eq)
diff --git a/Codec/MIME/Utils.hs b/Codec/MIME/Utils.hs
--- a/Codec/MIME/Utils.hs
+++ b/Codec/MIME/Utils.hs
@@ -1,32 +1,32 @@
---------------------------------------------------------------------
--- |
--- Module    : Codec.MIME.Utils
--- Copyright : (c) 2006-2009, Galois, Inc. 
--- License   : BSD3
---
--- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
--- Stability : provisional
--- Portability: portable
---
--- Extracting content from MIME values and types.
--- 
---------------------------------------------------------------------
-module Codec.MIME.Utils
-  ( findMultipartNamed -- :: String -> MIMEValue -> Maybe MIMEValue
-  )  where
-
-import Codec.MIME.Type
-import Data.List ( find )
-import Control.Monad ( msum )
-
--- | Given a parameter name, locate it within a MIME value,
--- returning the corresponding (sub) MIME value.
-findMultipartNamed :: String -> MIMEValue -> Maybe MIMEValue
-findMultipartNamed nm mv =
- case mime_val_content mv of
-   Multi ms  -> msum (map (findMultipartNamed nm) ms)
-   Single {} -> do cd <- mime_val_disp mv
-                   find (withDispName nm) (dispParams cd)
-                   return mv
- where withDispName a (Name b) = a == b
-       withDispName _ _ = False
+--------------------------------------------------------------------
+-- |
+-- Module    : Codec.MIME.Utils
+-- Copyright : (c) 2006-2009, Galois, Inc. 
+-- License   : BSD3
+--
+-- Maintainer: Sigbjorn Finne <sigbjorn.finne@gmail.com>
+-- Stability : provisional
+-- Portability: portable
+--
+-- Extracting content from MIME values and types.
+-- 
+--------------------------------------------------------------------
+module Codec.MIME.Utils
+  ( findMultipartNamed -- :: String -> MIMEValue -> Maybe MIMEValue
+  )  where
+
+import Codec.MIME.Type
+import Data.List ( find )
+import Control.Monad ( msum )
+
+-- | Given a parameter name, locate it within a MIME value,
+-- returning the corresponding (sub) MIME value.
+findMultipartNamed :: String -> MIMEValue -> Maybe MIMEValue
+findMultipartNamed nm mv =
+ case mime_val_content mv of
+   Multi ms  -> msum (map (findMultipartNamed nm) ms)
+   Single {} -> do cd <- mime_val_disp mv
+                   find (withDispName nm) (dispParams cd)
+                   return mv
+ where withDispName a (Name b) = a == b
+       withDispName _ _ = False
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,27 @@
-Copyright (c) Galois, Inc. 2006-2008
-
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. Neither the name of the author nor the names of his contributors
-   may be used to endorse or promote products derived from this software
-   without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
+Copyright (c) Galois, Inc. 2006-2008
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,4 +1,4 @@
-module Main where
-
-import Distribution.Simple
-main = defaultMain
+module Main where
+
+import Distribution.Simple
+main = defaultMain
diff --git a/mime.cabal b/mime.cabal
--- a/mime.cabal
+++ b/mime.cabal
@@ -1,29 +1,24 @@
-name:               mime
-version:            0.3.2
-synopsis:           Working with MIME types.
-description:        Working with MIME types.
-category:           Codec
-license:            BSD3
-license-file:       LICENSE
-author:             Sigbjorn Finne, Galois, Inc.
-maintainer:         Sigbjorn Finne <sigbjorn.finne@gmail.com>
-Copyright:          (c) 2006-2009 Galois Inc.
-cabal-version:      >= 1.2.0
-build-type:         Simple
-Extra-Source-Files: CHANGES
-
-flag split-base
-
-library
-  if flag(split-base)
-    build-depends:   base >= 3 && <= 4
-  else
-    build-depends:   base >= 2 && <= 4
-
-  exposed-modules: Codec.MIME.Type
-                   Codec.MIME.Parse
-                   Codec.MIME.Utils
-                   Codec.MIME.Base64
-                   Codec.MIME.Decode
-                   Codec.MIME.QuotedPrintable
-  ghc-options:     -Wall
+name:               mime
+version:            0.3.3
+synopsis:           Working with MIME types.
+description:        Working with MIME types.
+category:           Codec
+license:            BSD3
+license-file:       LICENSE
+author:             Sigbjorn Finne, Galois, Inc.
+maintainer:         Sigbjorn Finne <sigbjorn.finne@gmail.com>
+Copyright:          (c) 2006-2009 Galois Inc.
+cabal-version:      >= 1.2.0
+build-type:         Simple
+Extra-Source-Files: CHANGES
+
+library
+  build-depends:   base >= 3 && < 5
+
+  exposed-modules: Codec.MIME.Type
+                   Codec.MIME.Parse
+                   Codec.MIME.Utils
+                   Codec.MIME.Base64
+                   Codec.MIME.Decode
+                   Codec.MIME.QuotedPrintable
+  ghc-options:     -Wall
