content: {-# LANGUAGE CPP               #-}
  ------------------------------------------------------------------------------
  -- | This module contains functionality common among multiple back-ends.
  --
  
  module Snap.Snaplet.Session.Common
    ( RNG
    , mkRNG
    , withRNG
    , randomToken
    , mkCSRFToken
    ) where
  
  ------------------------------------------------------------------------------
  import           Control.Concurrent
  import           Control.Monad
  import           Data.ByteString (ByteString)
  import qualified Data.ByteString.Char8 as B
  import qualified Data.Text.Encoding as T
  import           Data.Text (Text)
  import           Numeric
  import           System.Random.MWC
  
  #if !MIN_VERSION_base(4,8,0)
  import           Control.Applicative
  #endif
  
  
  ------------------------------------------------------------------------------
  -- | High speed, mutable random number generator state
  newtype RNG = RNG (MVar GenIO)
  
  ------------------------------------------------------------------------------
  -- | Perform given action, mutating the RNG state
  withRNG :: RNG
          -> (GenIO -> IO a)
          -> IO a
  withRNG (RNG rng) m = withMVar rng m
  
  
  ------------------------------------------------------------------------------
  -- | Create a new RNG
  mkRNG :: IO RNG
  mkRNG = withSystemRandom (newMVar >=> return . RNG)
  
  
  ------------------------------------------------------------------------------
  -- | Generates a random salt of given length
  randomToken :: Int -> RNG -> IO ByteString
  randomToken n rng = do
      is <- withRNG rng $ \gen -> sequence . take n . repeat $ mk gen
      return . B.pack . concat . map (flip showHex "") $ is
    where
      mk :: GenIO -> IO Int
      mk = uniformR (0,15)
  
  
  ------------------------------------------------------------------------------
  -- | Generate a randomized CSRF token
  mkCSRFToken :: RNG -> IO Text
  mkCSRFToken rng = T.decodeUtf8 <$> randomToken 40 rng
  