You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
141 lines
5.2 KiB
141 lines
5.2 KiB
{-# OPTIONS_HADDOCK hide #-} |
|
{-# LANGUAGE OverloadedStrings #-} |
|
module Network.Xmpp.Session where |
|
|
|
import qualified Control.Exception as Ex |
|
import Control.Monad.Error |
|
import Data.Text as Text |
|
import Data.XML.Pickle |
|
import Data.XML.Types(Element) |
|
import Network |
|
import qualified Network.TLS as TLS |
|
import Network.Xmpp.Bind |
|
import Network.Xmpp.Concurrent.Types |
|
import Network.Xmpp.Concurrent.Channels |
|
import Network.Xmpp.Connection |
|
import Network.Xmpp.Marshal |
|
import Network.Xmpp.Pickle |
|
import Network.Xmpp.Sasl |
|
import Network.Xmpp.Sasl.Mechanisms |
|
import Network.Xmpp.Sasl.Types |
|
import Network.Xmpp.Stream |
|
import Network.Xmpp.TLS |
|
import Network.Xmpp.Types |
|
|
|
-- | The quick and easy way to set up a connection to an XMPP server |
|
-- |
|
-- This will |
|
-- |
|
-- * connect to the host |
|
-- |
|
-- * secure the connection with TLS |
|
-- |
|
-- * authenticate to the server using either SCRAM-SHA1 (preferred) or |
|
-- Digest-MD5 |
|
-- |
|
-- * bind a resource |
|
-- |
|
-- * return the full JID you have been assigned |
|
-- |
|
-- Note that the server might assign a different resource even when we send |
|
-- a preference. |
|
simpleConnect :: HostName -- ^ Host to connect to |
|
-> PortID -- ^ Port to connec to |
|
-> Text -- ^ Hostname of the server (to distinguish the XMPP |
|
-- service) |
|
-> Text -- ^ User name (authcid) |
|
-> Text -- ^ Password |
|
-> Maybe Text -- ^ Desired resource (or Nothing to let the server |
|
-- decide) |
|
-> IO Session |
|
simpleConnect host port hostname username password resource = do |
|
con' <- connectTcp host port hostname |
|
con <- case con' of |
|
Left e -> Ex.throwIO e |
|
Right r -> return r |
|
startTLS exampleParams con |
|
saslResponse <- simpleAuth username password resource con |
|
case saslResponse of |
|
Right jid -> newSession con |
|
Left e -> error $ show e |
|
|
|
|
|
-- | Connect to host with given address. |
|
connectTcp :: HostName -> PortID -> Text -> IO (Either StreamError Connection) |
|
connectTcp address port hostname = do |
|
con <- connectTcpRaw address port hostname |
|
result <- withConnection startStream con |
|
case result of |
|
Left e -> do |
|
withConnection (pushElement . pickleElem xpStreamError $ toError e) |
|
con |
|
closeStreams con |
|
return $ Left e |
|
Right () -> return $ Right con |
|
where |
|
-- TODO: Descriptive texts in stream errors? |
|
toError (StreamNotStreamElement _name) = |
|
XmppStreamError StreamInvalidXml Nothing Nothing |
|
toError (StreamInvalidStreamNamespace _ns) = |
|
XmppStreamError StreamInvalidNamespace Nothing Nothing |
|
toError (StreamInvalidStreamPrefix _prefix) = |
|
XmppStreamError StreamBadNamespacePrefix Nothing Nothing |
|
-- TODO: Catch remaining xmppStartStream errors. |
|
toError (StreamWrongVersion _ver) = |
|
XmppStreamError StreamUnsupportedVersion Nothing Nothing |
|
toError (StreamWrongLangTag _) = |
|
XmppStreamError StreamInvalidXml Nothing Nothing |
|
toError StreamUnknownError = |
|
XmppStreamError StreamBadFormat Nothing Nothing |
|
|
|
|
|
sessionXML :: Element |
|
sessionXML = pickleElem |
|
(xpElemBlank "{urn:ietf:params:xml:ns:xmpp-session}session") |
|
() |
|
|
|
sessionIQ :: Stanza |
|
sessionIQ = IQRequestS $ IQRequest { iqRequestID = "sess" |
|
, iqRequestFrom = Nothing |
|
, iqRequestTo = Nothing |
|
, iqRequestLangTag = Nothing |
|
, iqRequestType = Set |
|
, iqRequestPayload = sessionXML |
|
} |
|
|
|
-- Sends the session IQ set element and waits for an answer. Throws an error if |
|
-- if an IQ error stanza is returned from the server. |
|
startSession :: Connection -> IO () |
|
startSession con = do |
|
answer <- pushIQ' "session" Nothing Set Nothing sessionXML con |
|
case answer of |
|
Left e -> error $ show e |
|
Right _ -> return () |
|
|
|
-- | Authenticate to the server using the first matching method and bind a |
|
-- resource. |
|
auth :: [SaslHandler] |
|
-> Maybe Text |
|
-> Connection |
|
-> IO (Either AuthError Jid) |
|
auth mechanisms resource con = runErrorT $ do |
|
ErrorT $ xmppSasl mechanisms con |
|
jid <- lift $ xmppBind resource con |
|
lift $ startSession con |
|
return jid |
|
|
|
-- | Authenticate to the server with the given username and password |
|
-- and bind a resource. |
|
-- |
|
-- Prefers SCRAM-SHA1 over DIGEST-MD5. |
|
simpleAuth :: Text.Text -- ^ The username |
|
-> Text.Text -- ^ The password |
|
-> Maybe Text -- ^ The desired resource or 'Nothing' to let the |
|
-- server assign one |
|
-> Connection |
|
-> IO (Either AuthError Jid) |
|
simpleAuth username passwd resource = flip auth resource $ |
|
[ -- TODO: scramSha1Plus |
|
scramSha1 username Nothing passwd |
|
, digestMd5 username Nothing passwd |
|
]
|
|
|