Browse Source

Simplify API

Drops the Connection newtype, and shows TMVar Connection instead.

Hides Connection from Network.Xmpp, as the vast majority of users will
not need to work with Connection directly. The related functions are
now instead available from Network.Xmpp.Basic.

Renames `simpleConnect' to `session', and makes it flexible in terms
of authentication and whether or not to use TLS.

Adds some minor documentation changes.

We will need to export some session related information (such as the
acquired resource, stream properties, etc.). We will also need to
expose any failures encountered, probably by making `session' an
ErrorT calculation.

Also removed the Errors module from the Cabal file.
master
Jon Kristensen 13 years ago
parent
commit
2fa9b0decb
  1. 1
      pontarius-xmpp.cabal
  2. 14
      source/Network/Xmpp.hs
  3. 10
      source/Network/Xmpp/Basic.hs
  4. 4
      source/Network/Xmpp/Bind.hs
  5. 2
      source/Network/Xmpp/Concurrent/Channels.hs
  6. 3
      source/Network/Xmpp/Concurrent/Channels/Types.hs
  7. 12
      source/Network/Xmpp/Concurrent/Threads.hs
  8. 2
      source/Network/Xmpp/Concurrent/Types.hs
  9. 41
      source/Network/Xmpp/Connection.hs
  10. 4
      source/Network/Xmpp/Sasl.hs
  11. 2
      source/Network/Xmpp/Sasl/Types.hs
  12. 70
      source/Network/Xmpp/Session.hs
  13. 7
      source/Network/Xmpp/Stream.hs
  14. 4
      source/Network/Xmpp/Tls.hs
  15. 17
      source/Network/Xmpp/Types.hs
  16. 6
      source/Network/Xmpp/Xep/InbandRegistration.hs
  17. 3
      source/Network/Xmpp/Xep/ServiceDiscovery.hs

1
pontarius-xmpp.cabal

@ -69,7 +69,6 @@ Library
, Network.Xmpp.Concurrent.Threads , Network.Xmpp.Concurrent.Threads
, Network.Xmpp.Concurrent.Monad , Network.Xmpp.Concurrent.Monad
, Network.Xmpp.Connection , Network.Xmpp.Connection
, Network.Xmpp.Errors
, Network.Xmpp.IM.Message , Network.Xmpp.IM.Message
, Network.Xmpp.IM.Presence , Network.Xmpp.IM.Presence
, Network.Xmpp.Jid , Network.Xmpp.Jid

14
source/Network/Xmpp.hs

@ -25,19 +25,7 @@
module Network.Xmpp module Network.Xmpp
( -- * Session management ( -- * Session management
Session Session
, simpleConnect , session
, connectTcp
, newSession
, withConnection
, startTls
, simpleAuth
, auth
, scramSha1
, digestMd5
, plain
, closeConnection
, endContext
, setConnectionClosedHandler
-- * JID -- * JID
-- | A JID (historically: Jabber ID) is XMPPs native format -- | A JID (historically: Jabber ID) is XMPPs native format
-- for addressing entities in the network. It is somewhat similar to an e-mail -- for addressing entities in the network. It is somewhat similar to an e-mail

10
source/Network/Xmpp/Basic.hs

@ -2,16 +2,21 @@ module Network.Xmpp.Basic
( Connection(..) ( Connection(..)
, ConnectionState(..) , ConnectionState(..)
, connectTcp , connectTcp
, simpleConnect , newSession
, withConnection
, startTls , startTls
, simpleAuth , simpleAuth
, auth , auth
, scramSha1 , scramSha1
, digestMd5 , digestMd5
, plain , plain
, closeConnection
, pushStanza , pushStanza
, pullStanza , pullStanza
) , closeConnection
, endContext
, setConnectionClosedHandler
)
where where
@ -21,3 +26,4 @@ import Network.Xmpp.Session
import Network.Xmpp.Stream import Network.Xmpp.Stream
import Network.Xmpp.Tls import Network.Xmpp.Tls
import Network.Xmpp.Types import Network.Xmpp.Types
import Network.Xmpp.Concurrent

4
source/Network/Xmpp/Bind.hs

@ -17,6 +17,8 @@ import Network.Xmpp.Types
import Control.Monad.State(modify) import Control.Monad.State(modify)
import Control.Concurrent.STM.TMVar
-- Produces a `bind' element, optionally wrapping a resource. -- Produces a `bind' element, optionally wrapping a resource.
bindBody :: Maybe Text -> Element bindBody :: Maybe Text -> Element
bindBody = pickleElem $ bindBody = pickleElem $
@ -28,7 +30,7 @@ bindBody = pickleElem $
-- Sends a (synchronous) IQ set request for a (`Just') given or server-generated -- Sends a (synchronous) IQ set request for a (`Just') given or server-generated
-- resource and extract the JID from the non-error response. -- resource and extract the JID from the non-error response.
xmppBind :: Maybe Text -> Connection -> IO Jid xmppBind :: Maybe Text -> TMVar Connection -> IO Jid
xmppBind rsrc c = do xmppBind rsrc c = do
answer <- pushIQ' "bind" Nothing Set Nothing (bindBody rsrc) c answer <- pushIQ' "bind" Nothing Set Nothing (bindBody rsrc) c
jid <- case () of () | Right IQResult{iqResultPayload = Just b} <- answer jid <- case () of () | Right IQResult{iqResultPayload = Just b} <- answer

2
source/Network/Xmpp/Concurrent/Channels.hs

@ -71,7 +71,7 @@ toChans stanzaC iqHands sta = atomically $ do
-- | Creates and initializes a new Xmpp context. -- | Creates and initializes a new Xmpp context.
newSession :: Connection -> IO Session newSession :: TMVar Connection -> IO Session
newSession con = do newSession con = do
outC <- newTChanIO outC <- newTChanIO
stanzaChan <- newTChanIO stanzaChan <- newTChanIO

3
source/Network/Xmpp/Concurrent/Channels/Types.hs

@ -8,7 +8,8 @@ import Data.Text (Text)
import Network.Xmpp.Concurrent.Types import Network.Xmpp.Concurrent.Types
import Network.Xmpp.Types import Network.Xmpp.Types
-- | An XMPP session context -- | The @Session@ object holds the current state of the XMPP connection, and is
-- thus necessary for any interaction with it.
data Session = Session data Session = Session
{ context :: Context { context :: Context
, stanzaCh :: TChan Stanza -- All stanzas , stanzaCh :: TChan Stanza -- All stanzas

12
source/Network/Xmpp/Concurrent/Threads.hs

@ -18,13 +18,15 @@ import qualified Data.ByteString as BS
import Network.Xmpp.Concurrent.Types import Network.Xmpp.Concurrent.Types
import Network.Xmpp.Connection import Network.Xmpp.Connection
import Control.Concurrent.STM.TMVar
import GHC.IO (unsafeUnmask) import GHC.IO (unsafeUnmask)
-- Worker to read stanzas from the stream and concurrently distribute them to -- Worker to read stanzas from the stream and concurrently distribute them to
-- all listener threads. -- all listener threads.
readWorker :: (Stanza -> IO ()) readWorker :: (Stanza -> IO ())
-> (StreamFailure -> IO ()) -> (StreamFailure -> IO ())
-> TMVar Connection -> TMVar (TMVar Connection)
-> IO a -> IO a
readWorker onStanza onConnectionClosed stateRef = readWorker onStanza onConnectionClosed stateRef =
Ex.mask_ . forever $ do Ex.mask_ . forever $ do
@ -32,8 +34,8 @@ readWorker onStanza onConnectionClosed stateRef =
-- we don't know whether pull will -- we don't know whether pull will
-- necessarily be interruptible -- necessarily be interruptible
s <- atomically $ do s <- atomically $ do
con@(Connection con_) <- readTMVar stateRef con <- readTMVar stateRef
state <- sConnectionState <$> readTMVar con_ state <- sConnectionState <$> readTMVar con
when (state == ConnectionClosed) when (state == ConnectionClosed)
retry retry
return con return con
@ -72,11 +74,11 @@ readWorker onStanza onConnectionClosed stateRef =
-- connection. -- connection.
startThreadsWith :: (Stanza -> IO ()) startThreadsWith :: (Stanza -> IO ())
-> TVar EventHandlers -> TVar EventHandlers
-> Connection -> TMVar Connection
-> IO -> IO
(IO (), (IO (),
TMVar (BS.ByteString -> IO Bool), TMVar (BS.ByteString -> IO Bool),
TMVar Connection, TMVar (TMVar Connection),
ThreadId) ThreadId)
startThreadsWith stanzaHandler eh con = do startThreadsWith stanzaHandler eh con = do
read <- withConnection' (gets $ cSend. cHand) con read <- withConnection' (gets $ cSend. cHand) con

2
source/Network/Xmpp/Concurrent/Types.hs

@ -25,7 +25,7 @@ data Context = Context
, idGenerator :: IO StanzaId , idGenerator :: IO StanzaId
-- | Lock (used by withConnection) to make sure that a maximum of one -- | Lock (used by withConnection) to make sure that a maximum of one
-- XmppConMonad action is executed at any given time. -- XmppConMonad action is executed at any given time.
, conRef :: TMVar Connection , conRef :: TMVar (TMVar Connection)
, eventHandlers :: TVar EventHandlers , eventHandlers :: TVar EventHandlers
, stopThreads :: IO () , stopThreads :: IO ()
} }

41
source/Network/Xmpp/Connection.hs

@ -40,45 +40,48 @@ import Text.XML.Unresolved(InvalidEventStream(..))
import System.Log.Logger import System.Log.Logger
import Data.ByteString.Base64 import Data.ByteString.Base64
import Control.Concurrent.STM.TMVar
-- Enable/disable debug output -- Enable/disable debug output
-- This will dump all incoming and outgoing network taffic to the console, -- This will dump all incoming and outgoing network taffic to the console,
-- prefixed with "in: " and "out: " respectively -- prefixed with "in: " and "out: " respectively
debug :: Bool debug :: Bool
debug = False debug = False
pushElement :: Element -> StateT Connection_ IO Bool pushElement :: Element -> StateT Connection IO Bool
pushElement x = do pushElement x = do
send <- gets (cSend . cHand) send <- gets (cSend . cHand)
liftIO . send $ renderElement x liftIO . send $ renderElement x
-- | Encode and send stanza -- | Encode and send stanza
pushStanza :: Stanza -> Connection -> IO Bool pushStanza :: Stanza -> TMVar Connection -> IO Bool
pushStanza s = withConnection' . pushElement $ pickleElem xpStanza s pushStanza s = withConnection' . pushElement $ pickleElem xpStanza s
-- XML documents and XMPP streams SHOULD be preceeded by an XML declaration. -- XML documents and XMPP streams SHOULD be preceeded by an XML declaration.
-- UTF-8 is the only supported XMPP encoding. The standalone document -- UTF-8 is the only supported XMPP encoding. The standalone document
-- declaration (matching "SDDecl" in the XML standard) MUST NOT be included in -- declaration (matching "SDDecl" in the XML standard) MUST NOT be included in
-- XMPP streams. RFC 6120 defines XMPP only in terms of XML 1.0. -- XMPP streams. RFC 6120 defines XMPP only in terms of XML 1.0.
pushXmlDecl :: StateT Connection_ IO Bool pushXmlDecl :: StateT Connection IO Bool
pushXmlDecl = do pushXmlDecl = do
con <- gets cHand con <- gets cHand
liftIO $ (cSend con) "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" liftIO $ (cSend con) "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
pushOpenElement :: Element -> StateT Connection_ IO Bool pushOpenElement :: Element -> StateT Connection IO Bool
pushOpenElement e = do pushOpenElement e = do
sink <- gets (cSend . cHand ) sink <- gets (cSend . cHand )
liftIO . sink $ renderOpenElement e liftIO . sink $ renderOpenElement e
-- `Connect-and-resumes' the given sink to the connection source, and pulls a -- `Connect-and-resumes' the given sink to the connection source, and pulls a
-- `b' value. -- `b' value.
runEventsSink :: Sink Event IO b -> StateT Connection_ IO b runEventsSink :: Sink Event IO b -> StateT Connection IO b
runEventsSink snk = do runEventsSink snk = do
source <- gets cEventSource source <- gets cEventSource
(src', r) <- lift $ source $$++ snk (src', r) <- lift $ source $$++ snk
modify (\s -> s{cEventSource = src'}) modify (\s -> s{cEventSource = src'})
return r return r
pullElement :: StateT Connection_ IO Element pullElement :: StateT Connection IO Element
pullElement = do pullElement = do
Ex.catches (do Ex.catches (do
e <- runEventsSink (elements =$ await) e <- runEventsSink (elements =$ await)
@ -94,7 +97,7 @@ pullElement = do
] ]
-- Pulls an element and unpickles it. -- Pulls an element and unpickles it.
pullUnpickle :: PU [Node] a -> StateT Connection_ IO a pullUnpickle :: PU [Node] a -> StateT Connection IO a
pullUnpickle p = do pullUnpickle p = do
res <- unpickleElem p <$> pullElement res <- unpickleElem p <$> pullElement
case res of case res of
@ -103,7 +106,7 @@ pullUnpickle p = do
-- | Pulls a stanza (or stream error) from the stream. Throws an error on a stream -- | Pulls a stanza (or stream error) from the stream. Throws an error on a stream
-- error. -- error.
pullStanza :: Connection -> IO Stanza pullStanza :: TMVar Connection -> IO Stanza
pullStanza = withConnection' $ do pullStanza = withConnection' $ do
res <- pullUnpickle xpStreamStanza res <- pullUnpickle xpStreamStanza
case res of case res of
@ -121,9 +124,9 @@ catchPush p = Ex.catch
_ -> Ex.throwIO e _ -> Ex.throwIO e
) )
-- -- Connection_ state used when there is no connection. -- -- Connection state used when there is no connection.
xmppNoConnection :: Connection_ xmppNoConnection :: Connection
xmppNoConnection = Connection_ xmppNoConnection = Connection
{ cHand = Hand { cSend = \_ -> return False { cHand = Hand { cSend = \_ -> return False
, cRecv = \_ -> Ex.throwIO , cRecv = \_ -> Ex.throwIO
$ StreamOtherFailure $ StreamOtherFailure
@ -147,8 +150,8 @@ xmppNoConnection = Connection_
zeroSource = liftIO . Ex.throwIO $ StreamOtherFailure zeroSource = liftIO . Ex.throwIO $ StreamOtherFailure
-- Connects to the given hostname on port 5222 (TODO: Make this dynamic) and -- Connects to the given hostname on port 5222 (TODO: Make this dynamic) and
-- updates the XmppConMonad Connection_ state. -- updates the XmppConMonad Connection state.
connectTcpRaw :: HostName -> PortID -> Text -> IO Connection connectTcpRaw :: HostName -> PortID -> Text -> IO (TMVar Connection)
connectTcpRaw host port hostname = do connectTcpRaw host port hostname = do
let PortNumber portNumber = port let PortNumber portNumber = port
debugM "Pontarius.Xmpp" $ "Connecting to " ++ host ++ " on port " ++ debugM "Pontarius.Xmpp" $ "Connecting to " ++ host ++ " on port " ++
@ -172,7 +175,7 @@ connectTcpRaw host port hostname = do
, cFlush = hFlush h , cFlush = hFlush h
, cClose = hClose h , cClose = hClose h
} }
let con = Connection_ let con = Connection
{ cHand = hand { cHand = hand
, cEventSource = eSource , cEventSource = eSource
, sFeatures = (SF Nothing [] []) , sFeatures = (SF Nothing [] [])
@ -196,8 +199,8 @@ connectTcpRaw host port hostname = do
return d return d
-- Closes the connection and updates the XmppConMonad Connection_ state. -- Closes the connection and updates the XmppConMonad Connection state.
killConnection :: Connection -> IO (Either Ex.SomeException ()) killConnection :: TMVar Connection -> IO (Either Ex.SomeException ())
killConnection = withConnection $ do killConnection = withConnection $ do
cc <- gets (cClose . cHand) cc <- gets (cClose . cHand)
err <- liftIO $ (Ex.try cc :: IO (Either Ex.SomeException ())) err <- liftIO $ (Ex.try cc :: IO (Either Ex.SomeException ()))
@ -211,7 +214,7 @@ pushIQ' :: StanzaId
-> IQRequestType -> IQRequestType
-> Maybe LangTag -> Maybe LangTag
-> Element -> Element
-> Connection -> TMVar Connection
-> IO (Either IQError IQResult) -> IO (Either IQError IQResult)
pushIQ' iqID to tp lang body con = do pushIQ' iqID to tp lang body con = do
pushStanza (IQRequestS $ IQRequest iqID Nothing to lang tp body) con pushStanza (IQRequestS $ IQRequest iqID Nothing to lang tp body) con
@ -231,7 +234,7 @@ pushIQ' iqID to tp lang body con = do
-- | Send "</stream:stream>" and wait for the server to finish processing and to -- | Send "</stream:stream>" and wait for the server to finish processing and to
-- close the connection. Any remaining elements from the server and whether or -- close the connection. Any remaining elements from the server and whether or
-- not we received a </stream:stream> element from the server is returned. -- not we received a </stream:stream> element from the server is returned.
closeStreams :: Connection -> IO ([Element], Bool) closeStreams :: TMVar Connection -> IO ([Element], Bool)
closeStreams = withConnection $ do closeStreams = withConnection $ do
send <- gets (cSend . cHand) send <- gets (cSend . cHand)
cc <- gets (cClose . cHand) cc <- gets (cClose . cHand)
@ -244,7 +247,7 @@ closeStreams = withConnection $ do
where where
-- Pulls elements from the stream until the stream ends, or an error is -- Pulls elements from the stream until the stream ends, or an error is
-- raised. -- raised.
collectElems :: [Element] -> StateT Connection_ IO ([Element], Bool) collectElems :: [Element] -> StateT Connection IO ([Element], Bool)
collectElems es = do collectElems es = do
result <- Ex.try pullElement result <- Ex.try pullElement
case result of case result of

4
source/Network/Xmpp/Sasl.hs

@ -38,12 +38,14 @@ import qualified System.Random as Random
import Network.Xmpp.Sasl.Types import Network.Xmpp.Sasl.Types
import Network.Xmpp.Sasl.Mechanisms import Network.Xmpp.Sasl.Mechanisms
import Control.Concurrent.STM.TMVar
-- | Uses the first supported mechanism to authenticate, if any. Updates the -- | Uses the first supported mechanism to authenticate, if any. Updates the
-- state with non-password credentials and restarts the stream upon -- state with non-password credentials and restarts the stream upon
-- success. -- success.
xmppSasl :: [SaslHandler] -- ^ Acceptable authentication mechanisms and their xmppSasl :: [SaslHandler] -- ^ Acceptable authentication mechanisms and their
-- corresponding handlers -- corresponding handlers
-> Connection -> TMVar Connection
-> IO (Either AuthError ()) -> IO (Either AuthError ())
xmppSasl handlers = withConnection $ do xmppSasl handlers = withConnection $ do
-- Chooses the first mechanism that is acceptable by both the client and the -- Chooses the first mechanism that is acceptable by both the client and the

2
source/Network/Xmpp/Sasl/Types.hs

@ -29,7 +29,7 @@ data SaslElement = SaslSuccess (Maybe Text.Text)
-- | SASL mechanism XmppConnection computation, with the possibility of throwing -- | SASL mechanism XmppConnection computation, with the possibility of throwing
-- an authentication error. -- an authentication error.
type SaslM a = ErrorT AuthError (StateT Connection_ IO) a type SaslM a = ErrorT AuthError (StateT Connection IO) a
type Pairs = [(ByteString, ByteString)] type Pairs = [(ByteString, ByteString)]

70
source/Network/Xmpp/Session.hs

@ -21,47 +21,35 @@ import Network.Xmpp.Sasl.Types
import Network.Xmpp.Stream import Network.Xmpp.Stream
import Network.Xmpp.Tls import Network.Xmpp.Tls
import Network.Xmpp.Types import Network.Xmpp.Types
import Control.Concurrent.STM.TMVar
import Data.Maybe
-- | The quick and easy way to set up a connection to an XMPP server -- | Creates a 'Session' object by setting up a connection with an XMPP server.
-- --
-- This will -- Will connect to the specified host, optionally secure the connection with
-- -- TLS, as well as optionally authenticate and acquire an XMPP resource.
-- * connect to the host session :: HostName -- ^ Host to connect to
-- -> Text -- ^ The realm host name (to
-- * secure the connection with TLS -- distinguish the XMPP service)
-- -> PortID -- ^ Port to connect to
-- * authenticate to the server using either SCRAM-SHA1 (preferred) or -> Maybe TLS.TLSParams -- ^ TLS settings, if securing the
-- Digest-MD5 -- connection to the server is
-- -- desired
-- * bind a resource -> Maybe ([SaslHandler], Maybe Text) -- ^ SASL handlers and the desired
-- -- JID resource (or Nothing to let
-- * return the full JID you have been assigned -- the server decide)
-- -> IO Session -- TODO: ErrorT
-- Note that the server might assign a different resource even when we send session hostname realm port tls sasl = do
-- a preference. con' <- connectTcp hostname port realm
simpleConnect :: HostName -- ^ Host to connect to con <- case con' of
-> PortID -- ^ Port to connec to Left e -> Ex.throwIO e
-> Text -- ^ Hostname of the server (to distinguish the XMPP Right c -> return c
-- service) if isJust tls then startTls (fromJust tls) con >> return () else return () -- TODO: Eats TlsFailure
-> Text -- ^ User name (authcid) saslResponse <- if isJust sasl then auth (fst $ fromJust sasl) (snd $ fromJust sasl) con >> return () else return () -- TODO: Eats AuthError
-> Text -- ^ Password newSession con
-> 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. -- | Connect to host with given address.
connectTcp :: HostName -> PortID -> Text -> IO (Either StreamFailure Connection) connectTcp :: HostName -> PortID -> Text -> IO (Either StreamFailure (TMVar Connection))
connectTcp address port hostname = do connectTcp address port hostname = do
con <- connectTcpRaw address port hostname con <- connectTcpRaw address port hostname
result <- withConnection startStream con result <- withConnection startStream con
@ -104,7 +92,7 @@ sessionIQ = IQRequestS $ IQRequest { iqRequestID = "sess"
-- Sends the session IQ set element and waits for an answer. Throws an error if -- 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. -- if an IQ error stanza is returned from the server.
startSession :: Connection -> IO () startSession :: TMVar Connection -> IO ()
startSession con = do startSession con = do
answer <- pushIQ' "session" Nothing Set Nothing sessionXml con answer <- pushIQ' "session" Nothing Set Nothing sessionXml con
case answer of case answer of
@ -115,7 +103,7 @@ startSession con = do
-- resource. -- resource.
auth :: [SaslHandler] auth :: [SaslHandler]
-> Maybe Text -> Maybe Text
-> Connection -> TMVar Connection
-> IO (Either AuthError Jid) -> IO (Either AuthError Jid)
auth mechanisms resource con = runErrorT $ do auth mechanisms resource con = runErrorT $ do
ErrorT $ xmppSasl mechanisms con ErrorT $ xmppSasl mechanisms con
@ -131,7 +119,7 @@ simpleAuth :: Text.Text -- ^ The username
-> Text.Text -- ^ The password -> Text.Text -- ^ The password
-> Maybe Text -- ^ The desired resource or 'Nothing' to let the -> Maybe Text -- ^ The desired resource or 'Nothing' to let the
-- server assign one -- server assign one
-> Connection -> TMVar Connection
-> IO (Either AuthError Jid) -> IO (Either AuthError Jid)
simpleAuth username passwd resource = flip auth resource $ simpleAuth username passwd resource = flip auth resource $
[ -- TODO: scramSha1Plus [ -- TODO: scramSha1Plus

7
source/Network/Xmpp/Stream.hs

@ -21,7 +21,6 @@ import Data.XML.Pickle
import Data.XML.Types import Data.XML.Types
import Network.Xmpp.Connection import Network.Xmpp.Connection
import Network.Xmpp.Errors
import Network.Xmpp.Pickle import Network.Xmpp.Pickle
import Network.Xmpp.Types import Network.Xmpp.Types
import Network.Xmpp.Marshal import Network.Xmpp.Marshal
@ -66,7 +65,7 @@ openElementFromEvents = do
-- server responds in a way that is invalid, an appropriate stream error will be -- server responds in a way that is invalid, an appropriate stream error will be
-- generated, the connection to the server will be closed, and a StreamFilure -- generated, the connection to the server will be closed, and a StreamFilure
-- will be produced. -- will be produced.
startStream :: StateT Connection_ IO (Either StreamFailure ()) startStream :: StateT Connection IO (Either StreamFailure ())
startStream = runErrorT $ do startStream = runErrorT $ do
state <- lift $ get state <- lift $ get
con <- liftIO $ mkConnection state con <- liftIO $ mkConnection state
@ -117,7 +116,7 @@ startStream = runErrorT $ do
closeStreamWithError con StreamBadNamespacePrefix Nothing closeStreamWithError con StreamBadNamespacePrefix Nothing
| otherwise -> ErrorT $ checkchildren con (flattenAttrs attrs) | otherwise -> ErrorT $ checkchildren con (flattenAttrs attrs)
where where
-- closeStreamWithError :: MonadIO m => Connection -> StreamErrorCondition -> -- closeStreamWithError :: MonadIO m => TMVar Connection -> StreamErrorCondition ->
-- Maybe Element -> ErrorT StreamFailure m () -- Maybe Element -> ErrorT StreamFailure m ()
closeStreamWithError con sec el = do closeStreamWithError con sec el = do
liftIO $ do liftIO $ do
@ -157,7 +156,7 @@ flattenAttrs attrs = Prelude.map (\(name, content) ->
-- Sets a new Event source using the raw source (of bytes) -- Sets a new Event source using the raw source (of bytes)
-- and calls xmppStartStream. -- and calls xmppStartStream.
restartStream :: StateT Connection_ IO (Either StreamFailure ()) restartStream :: StateT Connection IO (Either StreamFailure ())
restartStream = do restartStream = do
raw <- gets (cRecv . cHand) raw <- gets (cRecv . cHand)
let newSource = DCI.ResumableSource (loopRead raw $= XP.parseBytes def) let newSource = DCI.ResumableSource (loopRead raw $= XP.parseBytes def)

4
source/Network/Xmpp/Tls.hs

@ -22,6 +22,8 @@ import Network.Xmpp.Pickle(ppElement)
import Network.Xmpp.Stream import Network.Xmpp.Stream
import Network.Xmpp.Types import Network.Xmpp.Types
import Control.Concurrent.STM.TMVar
mkBackend con = Backend { backendSend = \bs -> void (cSend con bs) mkBackend con = Backend { backendSend = \bs -> void (cSend con bs)
, backendRecv = cRecv con , backendRecv = cRecv con
, backendFlush = cFlush con , backendFlush = cFlush con
@ -74,7 +76,7 @@ exampleParams = TLS.defaultParamsClient
-- Pushes "<starttls/>, waits for "<proceed/>", performs the TLS handshake, and -- Pushes "<starttls/>, waits for "<proceed/>", performs the TLS handshake, and
-- restarts the stream. -- restarts the stream.
startTls :: TLS.TLSParams -> Connection -> IO (Either TlsFailure ()) startTls :: TLS.TLSParams -> TMVar Connection -> IO (Either TlsFailure ())
startTls params con = Ex.handle (return . Left . TlsError) startTls params con = Ex.handle (return . Left . TlsError)
. flip withConnection con . flip withConnection con
. runErrorT $ do . runErrorT $ do

17
source/Network/Xmpp/Types.hs

@ -33,7 +33,6 @@ module Network.Xmpp.Types
, Version(..) , Version(..)
, HandleLike(..) , HandleLike(..)
, Connection(..) , Connection(..)
, Connection_(..)
, withConnection , withConnection
, withConnection' , withConnection'
, mkConnection , mkConnection
@ -755,7 +754,7 @@ data HandleLike = Hand { cSend :: BS.ByteString -> IO Bool
, cClose :: IO () , cClose :: IO ()
} }
data Connection_ = Connection_ data Connection = Connection
{ sConnectionState :: !ConnectionState -- ^ State of { sConnectionState :: !ConnectionState -- ^ State of
-- connection -- connection
, cHand :: HandleLike , cHand :: HandleLike
@ -789,10 +788,8 @@ data Connection_ = Connection_
} }
newtype Connection = Connection {unConnection :: TMVar Connection_} withConnection :: StateT Connection IO c -> TMVar Connection -> IO c
withConnection action con = bracketOnError
withConnection :: StateT Connection_ IO c -> Connection -> IO c
withConnection action (Connection con) = bracketOnError
(atomically $ takeTMVar con) (atomically $ takeTMVar con)
(atomically . putTMVar con ) (atomically . putTMVar con )
(\c -> do (\c -> do
@ -802,15 +799,15 @@ withConnection action (Connection con) = bracketOnError
) )
-- nonblocking version. Changes to the connection are ignored! -- nonblocking version. Changes to the connection are ignored!
withConnection' :: StateT Connection_ IO b -> Connection -> IO b withConnection' :: StateT Connection IO b -> TMVar Connection -> IO b
withConnection' action (Connection con) = do withConnection' action con = do
con_ <- atomically $ readTMVar con con_ <- atomically $ readTMVar con
(r, _) <- runStateT action con_ (r, _) <- runStateT action con_
return r return r
mkConnection :: Connection_ -> IO Connection mkConnection :: Connection -> IO (TMVar Connection)
mkConnection con = Connection `fmap` (atomically $ newTMVar con) mkConnection con = {- Connection `fmap` -} (atomically $ newTMVar con)
-- | Failure conditions that may arise during TLS negotiation. -- | Failure conditions that may arise during TLS negotiation.

6
source/Network/Xmpp/Xep/InbandRegistration.hs

@ -71,7 +71,7 @@ emptyQuery = Query Nothing False False []
-- if r then return True else g -- if r then return True else g
query :: IQRequestType -> Query -> Connection -> IO (Either IbrError Query) query :: IQRequestType -> Query -> TMVar Connection -> IO (Either IbrError Query)
query queryType x con = do query queryType x con = do
answer <- pushIQ' "ibr" Nothing queryType Nothing (pickleElem xpQuery x) con answer <- pushIQ' "ibr" Nothing queryType Nothing (pickleElem xpQuery x) con
case answer of case answer of
@ -97,7 +97,7 @@ mapError f = mapErrorT (liftM $ left f)
-- | Retrieve the necessary fields and fill them in to register an account with -- | Retrieve the necessary fields and fill them in to register an account with
-- the server -- the server
registerWith :: [(Field, Text.Text)] registerWith :: [(Field, Text.Text)]
-> Connection -> TMVar Connection
-> IO (Either RegisterError Query) -> IO (Either RegisterError Query)
registerWith givenFields con = runErrorT $ do registerWith givenFields con = runErrorT $ do
fs <- mapError IbrError . ErrorT $ requestFields con fs <- mapError IbrError . ErrorT $ requestFields con
@ -114,7 +114,7 @@ registerWith givenFields con = runErrorT $ do
-- | Terminate your account on the server. You have to be logged in for this to -- | Terminate your account on the server. You have to be logged in for this to
-- work. You connection will most likely be terminated after unregistering. -- work. You connection will most likely be terminated after unregistering.
unregister :: Connection -> IO (Either IbrError Query) unregister :: TMVar Connection -> IO (Either IbrError Query)
unregister = query Set $ emptyQuery {remove = True} unregister = query Set $ emptyQuery {remove = True}
requestFields con = runErrorT $ do requestFields con = runErrorT $ do

3
source/Network/Xmpp/Xep/ServiceDiscovery.hs

@ -31,6 +31,7 @@ import Network.Xmpp.Concurrent.Types
import Network.Xmpp.Connection import Network.Xmpp.Connection
import Network.Xmpp.Pickle import Network.Xmpp.Pickle
import Network.Xmpp.Types import Network.Xmpp.Types
import Control.Concurrent.STM.TMVar
data DiscoError = DiscoNoQueryElement data DiscoError = DiscoNoQueryElement
| DiscoIQError IQError | DiscoIQError IQError
@ -105,7 +106,7 @@ queryInfo to node context = do
xmppQueryInfo :: Maybe Jid xmppQueryInfo :: Maybe Jid
-> Maybe Text.Text -> Maybe Text.Text
-> Connection -> TMVar Connection
-> IO (Either DiscoError QueryInfoResult) -> IO (Either DiscoError QueryInfoResult)
xmppQueryInfo to node con = do xmppQueryInfo to node con = do
res <- pushIQ' "info" to Get Nothing queryBody con res <- pushIQ' "info" to Get Nothing queryBody con

Loading…
Cancel
Save