{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}

{- |
Module      : Data.PEM.Writer
License     : BSD-style
Copyright   : (c) 2010-2018 Vincent Hanquez <vincent@snarc.org>
Stability   : experimental
Portability : portable
-}

module Data.PEM.Writer
  ( pemWriteBS
  , pemWriteLBS
  ) where


#if MIN_VERSION_base64(1,0,0)
import           Data.Base64.Types ( extractBase64 )
#endif
import           Data.ByteString ( ByteString )
import qualified Data.ByteString as B
import           Data.ByteString.Base64 ( encodeBase64' )
import qualified Data.ByteString.Char8 as BC
import qualified Data.ByteString.Lazy as L
import           Data.PEM.Types ( PEM (..) )

-- | A helper function while base64 < 1.0 is supported.

encodeBase64 :: ByteString -> ByteString
#if MIN_VERSION_base64(1,0,0)
encodeBase64 :: ByteString -> ByteString
encodeBase64 = Base64 'StdPadded ByteString -> ByteString
forall (k :: Alphabet) a. Base64 k a -> a
extractBase64 (Base64 'StdPadded ByteString -> ByteString)
-> (ByteString -> Base64 'StdPadded ByteString)
-> ByteString
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Base64 'StdPadded ByteString
encodeBase64'
#else
encodeBase64 = encodeBase64'
#endif

-- | Write a t'PEM' to a lazy 'Data.ByteString.Lazy.ByteString'.

pemWrite :: PEM -> L.ByteString
pemWrite :: PEM -> ByteString
pemWrite PEM
pem = [ByteString] -> ByteString
L.fromChunks ([ByteString
begin, ByteString
header] [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ [ByteString]
section [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ [ByteString
end])
 where
  begin :: ByteString
begin   = [ByteString] -> ByteString
B.concat [ByteString
"-----BEGIN ", ByteString
sectionName, ByteString
"-----\n"]
  end :: ByteString
end     = [ByteString] -> ByteString
B.concat [ByteString
"-----END ", ByteString
sectionName, ByteString
"-----\n" ]
  section :: [ByteString]
  section :: [ByteString]
section = (ByteString -> ByteString) -> [ByteString] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map ByteString -> ByteString
encodeLine ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
forall a b. (a -> b) -> a -> b
$ ByteString -> [ByteString]
splitChunks (ByteString -> [ByteString]) -> ByteString -> [ByteString]
forall a b. (a -> b) -> a -> b
$ PEM -> ByteString
pemContent PEM
pem
  header :: ByteString
  header :: ByteString
header  = if [(String, ByteString)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([(String, ByteString)] -> Bool) -> [(String, ByteString)] -> Bool
forall a b. (a -> b) -> a -> b
$ PEM -> [(String, ByteString)]
pemHeader PEM
pem
              then ByteString
B.empty
              else [ByteString] -> ByteString
B.concat (((String, ByteString) -> [ByteString])
-> [(String, ByteString)] -> [ByteString]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String, ByteString) -> [ByteString]
toHeader (PEM -> [(String, ByteString)]
pemHeader PEM
pem) [ByteString] -> [ByteString] -> [ByteString]
forall a. [a] -> [a] -> [a]
++ [ByteString
"\n"])
  toHeader :: (String, ByteString) -> [ByteString]
  toHeader :: (String, ByteString) -> [ByteString]
toHeader (String
k,ByteString
v) = [ String -> ByteString
BC.pack String
k, ByteString
":", ByteString
v, ByteString
"\n" ]
  -- expect only ASCII. need to find a type to represent it.

  sectionName :: ByteString
sectionName = String -> ByteString
BC.pack (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ PEM -> String
pemName PEM
pem
  encodeLine :: ByteString -> ByteString
encodeLine ByteString
l = ByteString -> ByteString
encodeBase64 ByteString
l ByteString -> ByteString -> ByteString
`B.append` ByteString
"\n"

  splitChunks :: ByteString -> [ByteString]
splitChunks ByteString
b
    | ByteString -> Int
B.length ByteString
b Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
48 = let (ByteString
x,ByteString
y) = Int -> ByteString -> (ByteString, ByteString)
B.splitAt Int
48 ByteString
b in ByteString
x ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
: ByteString -> [ByteString]
splitChunks ByteString
y
    | Bool
otherwise       = [ByteString
b]

-- | Convert the specified t'PEM' to a strict 'ByteString'.

pemWriteBS :: PEM -> ByteString
pemWriteBS :: PEM -> ByteString
pemWriteBS = [ByteString] -> ByteString
B.concat ([ByteString] -> ByteString)
-> (PEM -> [ByteString]) -> PEM -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
L.toChunks (ByteString -> [ByteString])
-> (PEM -> ByteString) -> PEM -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PEM -> ByteString
pemWrite

-- | Convert the specified t'PEM' to a lazy 'Data.ByteString.Lazy.ByteString'.

pemWriteLBS :: PEM -> L.ByteString
pemWriteLBS :: PEM -> ByteString
pemWriteLBS = PEM -> ByteString
pemWrite