xmpipe-0.0.0.0: examples/simpleServer.hs
{-# LANGUAGE OverloadedStrings, FlexibleContexts, PackageImports #-}
import Control.Applicative
import Control.Arrow
import Control.Monad
import "monads-tf" Control.Monad.State
import "monads-tf" Control.Monad.Error
import Control.Concurrent hiding (yield)
import Control.Concurrent.STM
import Data.Pipe
import Data.Pipe.ByteString
import Data.Pipe.TChan
import Network
import Network.Sasl
import Network.XMPiPe.Core.C2S.Server
import qualified Data.ByteString as BS
import qualified Network.Sasl.DigestMd5.Server as DM5
import qualified Network.Sasl.ScramSha1.Server as SS1
main :: IO ()
main = do
userlist <- atomically $ newTVar []
soc <- listenOn $ PortNumber 5222
forever $ accept soc >>= \(h, _, _) -> forkIO $ do
c <- atomically newTChan
(Just ns, st) <- (`runStateT` initXSt) . runPipe $ do
fromHandle h =$= sasl "localhost" retrieves =$= toHandle h
fromHandle h =$= bind "localhost" [] =@= toHandle h
let u = user st; sl = selector userlist
atomically $ modifyTVar userlist ((u, c) :)
void . forkIO . runPipe_ $ fromTChan c =$= output =$= toHandle h
runPipe_ $ fromHandle h =$= input ns =$= select u =$= toTChansM sl
selector :: TVar [(Jid, TChan Mpi)] -> IO [(Jid -> Bool, TChan Mpi)]
selector ul = map (first eq) <$> atomically (readTVar ul)
where
eq (Jid u d _) (Jid v e Nothing) = u == v && d == e
eq j k = j == k
select :: Monad m => Jid -> Pipe Mpi (Jid, Mpi) m ()
select f = (await >>=) . maybe (return ()) $ \mpi -> case mpi of
End -> yield (f, End)
Message tgs@(Tags { tagTo = Just to }) b ->
yield (to, Message tgs { tagFrom = Just f } b) >> select f
_ -> select f
initXSt :: XSt
initXSt = XSt {
user = Jid "" "localhost" Nothing, rands = repeat "00DEADBEEF00",
sSt = [ ("realm", "localhost"), ("qop", "auth"), ("charset", "utf-8"),
("algorithm", "md5-sess") ] }
retrieves :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) => [Retrieve m]
retrieves = [RTPlain retrievePln, RTDigestMd5 retrieveDM5, RTScramSha1 retrieveSS1]
retrievePln :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) =>
BS.ByteString -> BS.ByteString -> BS.ByteString -> m ()
retrievePln "" "yoshikuni" "password" = return ()
retrievePln "" "yoshio" "password" = return ()
retrievePln _ _ _ = throwError $ fromSaslError NotAuthorized "auth failure"
retrieveDM5 :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) => BS.ByteString -> m BS.ByteString
retrieveDM5 "yoshikuni" = return $ DM5.mkStored "yoshikuni" "localhost" "password"
retrieveDM5 "yoshio" = return $ DM5.mkStored "yoshio" "localhost" "password"
retrieveDM5 _ = throwError $ fromSaslError NotAuthorized "auth failure"
retrieveSS1 :: (
MonadState m, SaslState (StateType m),
MonadError m, SaslError (ErrorType m) ) => BS.ByteString ->
m (BS.ByteString, BS.ByteString, BS.ByteString, Int)
retrieveSS1 "yoshikuni" = return (slt, stk, svk, i)
where slt = "pepper"; i = 4492; (stk, svk) = SS1.salt "password" slt i
retrieveSS1 "yoshio" = return (slt, stk, svk, i)
where slt = "sugar"; i = 4492; (stk, svk) = SS1.salt "password" slt i
retrieveSS1 _ = throwError $ fromSaslError NotAuthorized "auth failure"
type Pairs a = [(a, a)]
data XSt = XSt { user :: Jid, rands :: [BS.ByteString], sSt :: Pairs BS.ByteString }
instance XmppState XSt where
getXmppState xs = (user xs, rands xs)
putXmppState (usr, rl) xs = xs { user = usr, rands = rl }
instance SaslState XSt where
getSaslState XSt { user = Jid n _ _, rands = nnc : _, sSt = ss } =
("username", n) : ("nonce", nnc) : ("snonce", nnc) : ss
getSaslState _ = error "XSt.getSaslState: null random list"
putSaslState ss xs@XSt { user = Jid _ d r, rands = _ : rs } =
xs { user = Jid n d r, rands = rs, sSt = ss }
where Just n = lookup "username" ss
putSaslState _ _ = error "XSt.getSaslState: null random list"