module SaslServer (
SaslState(..), SaslError(..),
SaslErrorType(..), Success(..),
mkSaslServers, Retrieve(..),
runSasl,
) where
import "monads-tf" Control.Monad.State
import "monads-tf" Control.Monad.Error
import Data.Pipe
import Network.Sasl
import qualified Data.ByteString as BS
import qualified Network.Sasl.Plain.Server as Pln
import qualified Network.Sasl.External.Server as Ext
import qualified Network.Sasl.DigestMd5.Server as DM5
import qualified Network.Sasl.ScramSha1.Server as SS1
import XmppType
runSasl :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) =>
[Retrieve m] -> Pipe Xmpp Xmpp m ()
runSasl rt = do
yield $ XCFeatures [FtMechanisms $ map getMechanism rt]
await >>= \a -> case a of
Just (XCAuth m i) -> sasl_ rt m i
_ -> throwError $ fromSaslError
(SaslErrorType "EOF") "unexpected EOF"
sasl_ :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) =>
[Retrieve m] -> BS.ByteString -> Maybe BS.ByteString -> Pipe Xmpp Xmpp m ()
sasl_ rt n i = case lookup n $ mkSaslServers rt of
Just (b, s) -> saslPipe b i s
_ -> throwError $ fromSaslError InvalidMechanism "no such mechanisms"
saslPipe :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) => Bool
-> Maybe BS.ByteString
-> Pipe BS.ByteString (Either Success BS.ByteString) m ()
-> Pipe Xmpp Xmpp m ()
saslPipe True (Just i) s =
(yield i >> convert (\(SRResponse r) -> r)) =$= s =$= saslOutput
saslPipe True _ s = convert (\(SRResponse r) -> r) =$= s
=$= (yield (SRChallenge "") >> saslOutput)
saslPipe False Nothing s = convert (\(SRResponse r) -> r) =$= s =$= saslOutput
saslPipe _ _ _ = throwError $
fromSaslError MalformedRequest "no need of initial data"
saslOutput :: (MonadState m, SaslState (StateType m)) =>
Pipe (Either Success BS.ByteString) Xmpp m ()
saslOutput = await >>= \mch -> case mch of
Just (Right r) -> yield (SRChallenge r) >> saslOutput
Just (Left (Success r)) -> yield $ XCSaslSuccess r
_ -> return ()
data Retrieve m
= RTPlain (BS.ByteString -> BS.ByteString -> BS.ByteString -> m ())
| RTExternal (BS.ByteString -> m ())
| RTDigestMd5 (BS.ByteString -> m BS.ByteString)
| RTScramSha1 (BS.ByteString ->
m (BS.ByteString, BS.ByteString, BS.ByteString, Int))
getMechanism :: Retrieve m -> BS.ByteString
getMechanism (RTPlain _) = "PLAIN"
getMechanism (RTExternal _) = "EXTERNAL"
getMechanism (RTDigestMd5 _) = "DIGEST-MD5"
getMechanism (RTScramSha1 _) = "SCRAM-SHA-1"
mkSaslServers :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m)) => [Retrieve m] -> [(
BS.ByteString,
(Bool, Pipe BS.ByteString (Either Success BS.ByteString) m ()) )]
mkSaslServers = map $ \rts -> case rts of
RTPlain rt -> Pln.sasl rt
RTExternal rt -> Ext.sasl rt
RTDigestMd5 rt -> DM5.sasl rt
RTScramSha1 rt -> SS1.sasl rt