|
|
|
|
@ -9,13 +9,20 @@ import Control.Monad.Error
@@ -9,13 +9,20 @@ import Control.Monad.Error
|
|
|
|
|
|
|
|
|
|
import qualified Data.Attoparsec.ByteString.Char8 as AP |
|
|
|
|
import qualified Data.ByteString as BS |
|
|
|
|
import qualified Data.ByteString.Base64 as B64 |
|
|
|
|
import Data.Maybe (fromMaybe) |
|
|
|
|
import Data.Maybe (maybeToList) |
|
|
|
|
import Data.Text |
|
|
|
|
import qualified Data.Text.Encoding as Text |
|
|
|
|
import Data.XML.Pickle |
|
|
|
|
import Data.XML.Types |
|
|
|
|
|
|
|
|
|
import Network.Xmpp.Monad |
|
|
|
|
import Network.Xmpp.Pickle |
|
|
|
|
import Network.Xmpp.Sasl.Types |
|
|
|
|
|
|
|
|
|
data SaslElement = SaslSuccess |
|
|
|
|
| SaslChallenge (Maybe Text) |
|
|
|
|
|
|
|
|
|
-- The <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/> element, with an |
|
|
|
|
-- optional round-trip value. |
|
|
|
|
@ -26,22 +33,18 @@ saslInitE mechanism rt =
@@ -26,22 +33,18 @@ saslInitE mechanism rt =
|
|
|
|
|
(maybeToList $ NodeContent . ContentText <$> rt) |
|
|
|
|
|
|
|
|
|
-- SASL response with text payload. |
|
|
|
|
saslResponseE :: Text -> Element |
|
|
|
|
saslResponseE :: Maybe Text -> Element |
|
|
|
|
saslResponseE resp = |
|
|
|
|
Element "{urn:ietf:params:xml:ns:xmpp-sasl}response" |
|
|
|
|
[] |
|
|
|
|
[NodeContent $ ContentText resp] |
|
|
|
|
(maybeToList $ NodeContent . ContentText <$> resp) |
|
|
|
|
|
|
|
|
|
-- SASL response without payload. |
|
|
|
|
saslResponse2E :: Element |
|
|
|
|
saslResponse2E = |
|
|
|
|
Element "{urn:ietf:params:xml:ns:xmpp-sasl}response" |
|
|
|
|
[] |
|
|
|
|
[] |
|
|
|
|
xpSuccess :: PU [Node] () |
|
|
|
|
xpSuccess = xpElemBlank "{urn:ietf:params:xml:ns:xmpp-sasl}success" |
|
|
|
|
|
|
|
|
|
-- Parses the incoming SASL data to a mapped list of pairs. |
|
|
|
|
toPairs :: BS.ByteString -> Either String [(BS.ByteString, BS.ByteString)] |
|
|
|
|
toPairs = AP.parseOnly . flip AP.sepBy1 (void $ AP.char ',') $ do |
|
|
|
|
pairs :: BS.ByteString -> Either String Pairs |
|
|
|
|
pairs = AP.parseOnly . flip AP.sepBy1 (void $ AP.char ',') $ do |
|
|
|
|
AP.skipSpace |
|
|
|
|
name <- AP.takeWhile1 (/= '=') |
|
|
|
|
_ <- AP.char '=' |
|
|
|
|
@ -51,8 +54,8 @@ toPairs = AP.parseOnly . flip AP.sepBy1 (void $ AP.char ',') $ do
@@ -51,8 +54,8 @@ toPairs = AP.parseOnly . flip AP.sepBy1 (void $ AP.char ',') $ do
|
|
|
|
|
return (name, content) |
|
|
|
|
|
|
|
|
|
-- Failure element pickler. |
|
|
|
|
failurePickle :: PU [Node] SaslFailure |
|
|
|
|
failurePickle = xpWrap |
|
|
|
|
xpFailure :: PU [Node] SaslFailure |
|
|
|
|
xpFailure = xpWrap |
|
|
|
|
(\(txt, (failure, _, _)) -> SaslFailure failure txt) |
|
|
|
|
(\(SaslFailure failure txt) -> (txt,(failure,(),()))) |
|
|
|
|
(xpElemNodes |
|
|
|
|
@ -68,6 +71,54 @@ failurePickle = xpWrap
@@ -68,6 +71,54 @@ failurePickle = xpWrap
|
|
|
|
|
(xpUnit) |
|
|
|
|
(xpUnit)))) |
|
|
|
|
-- Challenge element pickler. |
|
|
|
|
challengePickle :: PU [Node] Text |
|
|
|
|
challengePickle = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-sasl}challenge" |
|
|
|
|
(xpIsolate $ xpContent xpId) |
|
|
|
|
xpChallenge :: PU [Node] (Maybe Text) |
|
|
|
|
xpChallenge = xpElemNodes "{urn:ietf:params:xml:ns:xmpp-sasl}challenge" |
|
|
|
|
(xpOption $ xpContent xpId) |
|
|
|
|
|
|
|
|
|
xpSaslElement = xpAlt saslSel |
|
|
|
|
[ xpWrap (const SaslSuccess) (const ()) xpSuccess |
|
|
|
|
, xpWrap SaslChallenge (\(SaslChallenge c) -> c) xpChallenge |
|
|
|
|
] |
|
|
|
|
where |
|
|
|
|
saslSel SaslSuccess = 0 |
|
|
|
|
saslSel (SaslChallenge _) = 1 |
|
|
|
|
|
|
|
|
|
quote :: BS.ByteString -> BS.ByteString |
|
|
|
|
quote x = BS.concat ["\"",x,"\""] |
|
|
|
|
|
|
|
|
|
saslInit :: Text -> Maybe Text -> SaslM Bool |
|
|
|
|
saslInit mechanism payload = lift . pushElement $ saslInitE mechanism payload |
|
|
|
|
|
|
|
|
|
pullSaslElement :: SaslM SaslElement |
|
|
|
|
pullSaslElement = do |
|
|
|
|
el <- lift $ pullPickle (xpEither xpFailure xpSaslElement) |
|
|
|
|
case el of |
|
|
|
|
Left e ->throwError $ AuthSaslFailure e |
|
|
|
|
Right r -> return r |
|
|
|
|
|
|
|
|
|
pullChallenge :: SaslM (Maybe Text) |
|
|
|
|
pullChallenge = do |
|
|
|
|
e <- pullSaslElement |
|
|
|
|
case e of |
|
|
|
|
SaslChallenge sc -> return sc |
|
|
|
|
_ -> throwError AuthChallengeError |
|
|
|
|
|
|
|
|
|
saslFromJust :: Maybe a -> SaslM a |
|
|
|
|
saslFromJust Nothing = throwError $ AuthChallengeError |
|
|
|
|
saslFromJust (Just d) = return d |
|
|
|
|
|
|
|
|
|
pullSuccess :: SaslM () |
|
|
|
|
pullSuccess = do |
|
|
|
|
e <- pullSaslElement |
|
|
|
|
case e of |
|
|
|
|
SaslSuccess -> return () |
|
|
|
|
_ -> throwError $ AuthXmlError |
|
|
|
|
|
|
|
|
|
toPairs :: Text -> SaslM Pairs |
|
|
|
|
toPairs ctext = case pairs <=< B64.decode . Text.encodeUtf8 $ ctext of |
|
|
|
|
Left _e -> throwError AuthChallengeError |
|
|
|
|
Right r -> return r |
|
|
|
|
|
|
|
|
|
respond :: Maybe Text -> SaslM Bool |
|
|
|
|
respond = lift . pushElement . saslResponseE |
|
|
|
|
|
|
|
|
|
|