module Network.Mail.Parse.Decoders.FormatDecoders (qpDec, decodeB64) where -- Copyright: (c) Magnus Therning, 2012 -- License: BSD3, found in the LICENSE file -- Copied from the Sandi library import Foreign import Foreign.C.Types import System.IO.Unsafe as U import qualified Data.ByteString as BS import qualified Data.ByteString.Unsafe as BSU import Data.ByteString.Unsafe foreign import ccall "static qp.h qp_dec_c" c_qp_dec :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt foreign import ccall "static b64.h b64_dec_part_c" c_b64_dec_part :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO CInt foreign import ccall "static b64.h b64_dec_final_c" c_b64_dec_final :: Ptr Word8 -> CSize -> Ptr Word8 -> Ptr CSize -> IO CInt castEnum :: (Enum a, Enum b) => a -> b castEnum = toEnum . fromEnum qpDec' :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString) qpDec' bs = U.unsafePerformIO $ BSU.unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do outBuf <- mallocBytes inLen alloca $ \ pOutLen -> alloca $ \ pRemBuf -> alloca $ \ pRemLen -> do poke pOutLen (castEnum inLen) r <- c_qp_dec (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen outLen <- peek pOutLen newOutBuf <- reallocBytes outBuf (castEnum outLen) remBuf <- peek pRemBuf remLen <- peek pRemLen remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen) outBs <- BSU.unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf) if r == 0 then return $ Right (outBs, remBs) else return $ Left (outBs, remBs) qpDec :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString qpDec bs = case qpDec' bs of Right a@(d, r) -> if BS.null r then Right d else Left a Left a -> Left a b64DecodePart :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) (BS.ByteString, BS.ByteString) b64DecodePart bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do let maxOutLen = inLen `div` 4 * 3 outBuf <- mallocBytes maxOutLen alloca $ \ pOutLen -> alloca $ \ pRemBuf -> alloca $ \ pRemLen -> do poke pOutLen (castEnum maxOutLen) r <- c_b64_dec_part (castPtr inBuf) (castEnum inLen) outBuf pOutLen pRemBuf pRemLen outLen <- peek pOutLen newOutBuf <- reallocBytes outBuf (castEnum outLen) remBuf <- peek pRemBuf remLen <- peek pRemLen remBs <- BS.packCStringLen (castPtr remBuf, castEnum remLen) outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf) if r == 0 then return $ Right (outBs, remBs) else return $ Left (outBs, remBs) b64DecodeFinal :: BS.ByteString -> Maybe BS.ByteString b64DecodeFinal bs = U.unsafePerformIO $ unsafeUseAsCStringLen bs $ \ (inBuf, inLen) -> do outBuf <- mallocBytes 3 alloca $ \ pOutLen -> do r <- c_b64_dec_final (castPtr inBuf) (castEnum inLen) outBuf pOutLen if r == 0 then do outLen <- peek pOutLen newOutBuf <- reallocBytes outBuf (castEnum outLen) outBs <- unsafePackCStringFinalizer newOutBuf (castEnum outLen) (free newOutBuf) return $ Just outBs else free outBuf >> return Nothing decodeB64 :: BS.ByteString -> Either (BS.ByteString, BS.ByteString) BS.ByteString decodeB64 bs = either Left (\ (first, rest) -> maybe (Left (first, rest)) (\ fin -> Right (first `BS.append` fin)) (b64DecodeFinal rest)) (b64DecodePart bs)