| 
					
				 | 
			
			
				@@ -0,0 +1,146 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{-# LANGUAGE OverloadedStrings #-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+module Data.SMTP.Mime ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  module Data.SMTP.Types.Mime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  module Data.SMTP.Parser.Mime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  getMimeData, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reencodeMessage, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  requiredEncoding, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  splitEach 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ) where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.SMTP.Types.Mime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.SMTP.Parser.Mime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.SMTP.Types.Resource 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.SMTP.Parser.Resource 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Text.StringConvert 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.Attoparsec.ByteString.Char8.Extras as P 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.Attoparsec.ByteString.Char8 as A 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.Char as C 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.ByteString (ByteString) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.Word8 (Word8) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.Word8 as W 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.ByteString as BS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.ByteString.Char8 as C8 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.ByteString.Lazy as LBS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.ByteString.Base64.Lazy as B64 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import qualified Data.ByteString.Lazy.Search as LSearch 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+--import qualified Data.ByteString.Search as SSearch 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import Data.Default.Class 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+getMimeData :: [Header] -> (ContentTypeHeader, TransferEncoding) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+getMimeData hh = (ct, te) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ct = case getHeader hh contentTypeHeaderName of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Nothing -> def 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Just (Header (_, cth)) -> case A.parseOnly parseContentType (s cth) of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Left _ -> def 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Right (ctv, ctp) -> ContentTypeHeader ctv ctp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    te = case getHeader hh transferEncodingHeaderName of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Nothing -> def 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      Just (Header (_, teh)) -> case A.parseOnly parseTransferEncoding (C8.pack teh) of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Left _ -> def 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Right t -> t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{- | 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Required encoding for the given data. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Will evaluate the entire data, thus using a lot of memory. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+7/8 bit mime is available for data where all lines are shorter then 1000 bytes. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+7 bit mime is available ofr that where no byte is larger than 128 or equal 0. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+othersiwe data requires binarymime. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+requiredEncoding :: ResourceData -> BodyEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+requiredEncoding dt = case requiredFeatures 0 $ LBS.unpack dt of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (False, False) -> B7BitEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (False, True) -> B8BitEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  (True, _) -> BBinaryEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    requiredFeatures :: Int -> [Word8] -> (Bool, Bool) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    requiredFeatures _ [] = (False, False) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    requiredFeatures len (c:cc) = let 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      nlen = if c == (fromIntegral . C.ord $ '\n') then 0 else len+1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (ll, ee) = requiredFeatures nlen cc 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      in (len >= 1000 || ll, c == 0 || c >= 128 || ee) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+reencodeMessage :: BodyEncoding -> ResourceData -> ResourceData 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+reencodeMessage toEncoding dt = LBS.concat [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if newEncoding == fromEncoding then LBS.fromStrict $ BS.concat dthh else replaceEncodingHeader newEncoding dthh, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  "\r\n", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  reencodeBody' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (dthh, hh, dtb) = takeHeaders dt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (ct, fromEncoding) = getMimeData hh 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fromEncoding' = transferToBody fromEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    (message, multiPart, separator) = case ct of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (ContentTypeHeader (MultiPartMime _) (ContentTypeParameters (Just sep) _ _)) -> (False, True, C8.pack sep) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (ContentTypeHeader (MessageMime _) _ ) -> (True, False, "") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      _ -> (False, False, "") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    newEncoding :: TransferEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    newEncoding = case (fromEncoding', toEncoding, multiPart) of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (_, BBinaryEncoding, _) -> fromEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (BBinaryEncoding, B8BitEncoding, True) -> IdentityEncoding B7BitEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (_, B8BitEncoding, _) -> fromEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (_, B7BitEncoding, True) -> IdentityEncoding B7BitEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (BBinaryEncoding, B7BitEncoding, False) -> Base64EncodedBody 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      (B8BitEncoding, B7BitEncoding, False) -> Base64EncodedBody 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      _ -> fromEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    newEncoding' = transferToBody newEncoding 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reencodeBody' :: ResourceData 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    reencodeBody' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      | newEncoding == fromEncoding = dtb 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      | message = reencodeBody newEncoding dtb 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      | multiPart = concatParts separator . map (reencodeMessage newEncoding') . splitParts separator $ dtb 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      | otherwise = reencodeBody newEncoding dtb 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+reencodeBody :: TransferEncoding -> ResourceData -> ResourceData 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+reencodeBody (IdentityEncoding _) dt = dt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+reencodeBody Base64EncodedBody dt = LBS.intercalate "\r\n" $ splitEach 76 $ B64.encode dt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+reencodeBody QPEncodedBody dt = dt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+splitEach :: Int -> LBS.ByteString -> [LBS.ByteString] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+splitEach n t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  | LBS.null t = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  | otherwise = let (p, pp) = LBS.splitAt (fromIntegral n) t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                in p : splitEach n pp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+splitParts :: ByteString -> LBS.ByteString -> [LBS.ByteString] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+splitParts sep dt = map manageChunk chunks 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sep' = BS.concat ["\r\n--", sep] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    chunks = LSearch.split sep' dt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    manageChunk = LBS.dropWhile (== P.asW8 '-') . LBS.dropWhile A.isHorizontalSpace . LBS.dropWhile A.isEndOfLine 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+concatParts :: ByteString -> [LBS.ByteString] -> LBS.ByteString 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+concatParts sep = joinChunks (LBS.fromStrict sep) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    joinChunks _ [] = "" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    joinChunks _ [c] = c 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    joinChunks sep' [c1, c2] = LBS.concat [c1, "\r\n--", sep', "--\r\n", c2] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    joinChunks sep' (c:cc2) = LBS.concat [c, "\r\n--", sep', "\r\n", joinChunks sep' cc2] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+replaceEncodingHeader :: TransferEncoding -> [ByteString] -> LBS.ByteString 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+replaceEncodingHeader enc = LBS.concat . map LBS.fromStrict . echoAndReplace 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  where 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    h = transferEncodingHeaderName 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    echoAndReplace [] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    echoAndReplace (l:ll) = if isPrefixCI h l 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            then BS.concat [C8.pack h, ": ", C8.pack . show $ enc, "\r\n"] : 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 dropWhile (\x -> BS.null x || (W.isSpace . BS.head $ x)) ll 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            else l : echoAndReplace ll 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+isPrefixCI :: String -> ByteString -> Bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+isPrefixCI [] _ = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+isPrefixCI (c:cc) bs = case BS.uncons bs of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Nothing -> False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  Just (h, t) -> (C.toUpper . C.chr . fromIntegral $ h) == C.toUpper c && isPrefixCI cc t 
			 |