diff --git a/pontarius-xmpp.cabal b/pontarius-xmpp.cabal
index ce1cfa4..0f9b3c1 100644
--- a/pontarius-xmpp.cabal
+++ b/pontarius-xmpp.cabal
@@ -58,7 +58,6 @@ Library
, Network.Xmpp.Internal
, Network.Xmpp.IM
Other-modules: Data.Conduit.Tls
- , Network.Xmpp.Bind
, Network.Xmpp.Concurrent
, Network.Xmpp.Concurrent.Types
, Network.Xmpp.Concurrent.Basic
@@ -67,7 +66,6 @@ Library
, Network.Xmpp.Concurrent.Presence
, Network.Xmpp.Concurrent.Threads
, Network.Xmpp.Concurrent.Monad
- , Network.Xmpp.Connection
, Network.Xmpp.IM.Message
, Network.Xmpp.IM.Presence
, Network.Xmpp.Marshal
diff --git a/source/Network/Xmpp.hs b/source/Network/Xmpp.hs
index 6f9da93..f545cba 100644
--- a/source/Network/Xmpp.hs
+++ b/source/Network/Xmpp.hs
@@ -145,7 +145,7 @@ module Network.Xmpp
, AuthFailure( AuthXmlFailure -- Does not export AuthStreamFailure
, AuthNoAcceptableMechanism
, AuthChallengeFailure
- , AuthNoConnection
+ , AuthNoStream
, AuthFailure
, AuthSaslFailure
, AuthStringPrepFailure )
diff --git a/source/Network/Xmpp/Concurrent.hs b/source/Network/Xmpp/Concurrent.hs
index 8e7c02b..5c6de2b 100644
--- a/source/Network/Xmpp/Concurrent.hs
+++ b/source/Network/Xmpp/Concurrent.hs
@@ -83,14 +83,14 @@ toChans stanzaC iqHands sta = atomically $ do
-- | Creates and initializes a new Xmpp context.
-newSession :: TMVar Connection -> IO (Either XmppFailure Session)
-newSession con = runErrorT $ do
+newSession :: TMVar Stream -> IO (Either XmppFailure Session)
+newSession stream = runErrorT $ do
outC <- lift newTChanIO
stanzaChan <- lift newTChanIO
iqHandlers <- lift $ newTVarIO (Map.empty, Map.empty)
eh <- lift $ newTVarIO $ EventHandlers { connectionClosedHandler = \_ -> return () }
let stanzaHandler = toChans stanzaChan iqHandlers
- (kill, wLock, conState, readerThread) <- ErrorT $ startThreadsWith stanzaHandler eh con
+ (kill, wLock, streamState, readerThread) <- ErrorT $ startThreadsWith stanzaHandler eh stream
writer <- lift $ forkIO $ writeWorker outC wLock
idRef <- lift $ newTVarIO 1
let getId = atomically $ do
@@ -103,7 +103,7 @@ newSession con = runErrorT $ do
, writeRef = wLock
, readerThread = readerThread
, idGenerator = getId
- , conRef = conState
+ , streamRef = streamState
, eventHandlers = eh
, stopThreads = kill >> killThread writer
}
@@ -139,7 +139,7 @@ session :: HostName -- ^ Host to connect to
-- the server decide)
-> IO (Either XmppFailure (Session, Maybe AuthFailure))
session hostname realm port tls sasl = runErrorT $ do
- con <- ErrorT $ connect hostname port realm
+ con <- ErrorT $ openStream hostname port realm
if isJust tls
then ErrorT $ startTls (fromJust tls) con
else return ()
diff --git a/source/Network/Xmpp/Concurrent/Monad.hs b/source/Network/Xmpp/Concurrent/Monad.hs
index 588c9e3..5a1d627 100644
--- a/source/Network/Xmpp/Concurrent/Monad.hs
+++ b/source/Network/Xmpp/Concurrent/Monad.hs
@@ -9,7 +9,7 @@ import qualified Control.Exception.Lifted as Ex
import Control.Monad.Reader
import Network.Xmpp.Concurrent.Types
-import Network.Xmpp.Connection
+import Network.Xmpp.Stream
@@ -94,6 +94,6 @@ closeConnection :: Session -> IO ()
closeConnection session = Ex.mask_ $ do
(_send, connection) <- atomically $ liftM2 (,)
(takeTMVar $ writeRef session)
- (takeTMVar $ conRef session)
+ (takeTMVar $ streamRef session)
_ <- closeStreams connection
return ()
diff --git a/source/Network/Xmpp/Concurrent/Threads.hs b/source/Network/Xmpp/Concurrent/Threads.hs
index 7c78682..c98a8ff 100644
--- a/source/Network/Xmpp/Concurrent/Threads.hs
+++ b/source/Network/Xmpp/Concurrent/Threads.hs
@@ -16,7 +16,7 @@ import Control.Monad.State.Strict
import qualified Data.ByteString as BS
import Network.Xmpp.Concurrent.Types
-import Network.Xmpp.Connection
+import Network.Xmpp.Stream
import Control.Concurrent.STM.TMVar
@@ -28,7 +28,7 @@ import Control.Monad.Error
-- all listener threads.
readWorker :: (Stanza -> IO ())
-> (XmppFailure -> IO ())
- -> TMVar (TMVar Connection)
+ -> TMVar (TMVar Stream)
-> IO a
readWorker onStanza onConnectionClosed stateRef =
Ex.mask_ . forever $ do
@@ -38,7 +38,7 @@ readWorker onStanza onConnectionClosed stateRef =
s <- atomically $ do
con <- readTMVar stateRef
state <- cState <$> readTMVar con
- when (state == ConnectionClosed)
+ when (state == Closed)
retry
return con
allowInterrupt
@@ -77,13 +77,13 @@ readWorker onStanza onConnectionClosed stateRef =
-- connection.
startThreadsWith :: (Stanza -> IO ())
-> TVar EventHandlers
- -> TMVar Connection
+ -> TMVar Stream
-> IO (Either XmppFailure (IO (),
TMVar (BS.ByteString -> IO Bool),
- TMVar (TMVar Connection),
+ TMVar (TMVar Stream),
ThreadId))
startThreadsWith stanzaHandler eh con = do
- read <- withConnection' (gets $ cSend . cHandle >>= \d -> return $ Right d) con
+ read <- withStream' (gets $ cSend . cHandle >>= \d -> return $ Right d) con
case read of
Left e -> return $ Left e
Right read' -> do
diff --git a/source/Network/Xmpp/Concurrent/Types.hs b/source/Network/Xmpp/Concurrent/Types.hs
index decce8a..7473d44 100644
--- a/source/Network/Xmpp/Concurrent/Types.hs
+++ b/source/Network/Xmpp/Concurrent/Types.hs
@@ -42,9 +42,9 @@ data Session = Session
, writeRef :: TMVar (BS.ByteString -> IO Bool)
, readerThread :: ThreadId
, idGenerator :: IO StanzaId
- -- | Lock (used by withConnection) to make sure that a maximum of one
- -- XmppConMonad action is executed at any given time.
- , conRef :: TMVar (TMVar Connection)
+ -- | Lock (used by withStream) to make sure that a maximum of one
+ -- Stream action is executed at any given time.
+ , streamRef :: TMVar (TMVar Stream)
, eventHandlers :: TVar EventHandlers
, stopThreads :: IO ()
}
diff --git a/source/Network/Xmpp/Connection.hs b/source/Network/Xmpp/Connection.hs
deleted file mode 100644
index 0006073..0000000
--- a/source/Network/Xmpp/Connection.hs
+++ /dev/null
@@ -1,284 +0,0 @@
-{-# OPTIONS_HADDOCK hide #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE OverloadedStrings #-}
-
-module Network.Xmpp.Connection where
-
-import Control.Applicative((<$>))
-import Control.Concurrent (forkIO, threadDelay)
-import System.IO.Error (tryIOError)
-import Control.Monad
-import Control.Monad.IO.Class
-import Control.Monad.Trans.Class
---import Control.Monad.Trans.Resource
-import qualified Control.Exception.Lifted as Ex
-import qualified GHC.IO.Exception as GIE
-import Control.Monad.State.Strict
-
-import Data.ByteString as BS
-import Data.ByteString.Char8 as BSC8
-import Data.Conduit
-import Data.Conduit.Binary as CB
-import Data.Conduit.Internal as DCI
-import qualified Data.Conduit.List as CL
-import Data.IORef
-import Data.Text(Text)
-import qualified Data.Text as T
-import Data.XML.Pickle
-import Data.XML.Types
-
-import Network
-import Network.Xmpp.Types
-import Network.Xmpp.Marshal
-
-import System.IO
-
-import Text.Xml.Stream.Elements
-import Text.XML.Stream.Parse as XP
-import Text.XML.Unresolved(InvalidEventStream(..))
-
-import System.Log.Logger
-import Data.ByteString.Base64
-
-import Control.Concurrent.STM.TMVar
-import Control.Monad.Error
-
--- Enable/disable debug output
--- This will dump all incoming and outgoing network taffic to the console,
--- prefixed with "in: " and "out: " respectively
-debug :: Bool
-debug = False
-
--- TODO: Can the TLS send/recv functions throw something other than an IO error?
-
-wrapIOException :: IO a -> StateT Connection IO (Either XmppFailure a)
-wrapIOException action = do
- r <- liftIO $ tryIOError action
- case r of
- Right b -> return $ Right b
- Left e -> return $ Left $ XmppIOException e
-
-pushElement :: Element -> StateT Connection IO (Either XmppFailure Bool)
-pushElement x = do
- send <- gets (cSend . cHandle)
- wrapIOException $ send $ renderElement x
-
--- | Encode and send stanza
-pushStanza :: Stanza -> TMVar Connection -> IO (Either XmppFailure Bool)
-pushStanza s = withConnection' . pushElement $ pickleElem xpStanza s
-
--- XML documents and XMPP streams SHOULD be preceeded by an XML declaration.
--- UTF-8 is the only supported XMPP encoding. The standalone document
--- 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.
-pushXmlDecl :: StateT Connection IO (Either XmppFailure Bool)
-pushXmlDecl = do
- con <- gets cHandle
- wrapIOException $ (cSend con) ""
-
-pushOpenElement :: Element -> StateT Connection IO (Either XmppFailure Bool)
-pushOpenElement e = do
- sink <- gets (cSend . cHandle)
- wrapIOException $ sink $ renderOpenElement e
-
--- `Connect-and-resumes' the given sink to the connection source, and pulls a
--- `b' value.
-runEventsSink :: Sink Event IO b -> StateT Connection IO (Either XmppFailure b)
-runEventsSink snk = do -- TODO: Wrap exceptions?
- source <- gets cEventSource
- (src', r) <- lift $ source $$++ snk
- modify (\s -> s{cEventSource = src'})
- return $ Right r
-
-pullElement :: StateT Connection IO (Either XmppFailure Element)
-pullElement = do
- Ex.catches (do
- e <- runEventsSink (elements =$ await)
- case e of
- Left f -> return $ Left f
- Right Nothing -> return $ Left XmppOtherFailure -- TODO
- Right (Just r) -> return $ Right r
- )
- [ Ex.Handler (\StreamEnd -> return $ Left StreamEndFailure)
- , Ex.Handler (\(InvalidXmppXml s) -- Invalid XML `Event' encountered, or missing element close tag
- -> return $ Left XmppOtherFailure) -- TODO: Log: s
- , Ex.Handler $ \(e :: InvalidEventStream) -- xml-conduit exception
- -> return $ Left XmppOtherFailure -- TODO: Log: (show e)
- ]
-
--- Pulls an element and unpickles it.
-pullUnpickle :: PU [Node] a -> StateT Connection IO (Either XmppFailure a)
-pullUnpickle p = do
- elem <- pullElement
- case elem of
- Left e -> return $ Left e
- Right elem' -> do
- let res = unpickleElem p elem'
- case res of
- Left e -> return $ Left XmppOtherFailure -- TODO: Log
- Right r -> return $ Right r
-
--- | Pulls a stanza (or stream error) from the stream.
-pullStanza :: TMVar Connection -> IO (Either XmppFailure Stanza)
-pullStanza = withConnection' $ do
- res <- pullUnpickle xpStreamStanza
- case res of
- Left e -> return $ Left e
- Right (Left e) -> return $ Left $ StreamErrorFailure e
- Right (Right r) -> return $ Right r
-
--- Performs the given IO operation, catches any errors and re-throws everything
--- except 'ResourceVanished' and IllegalOperation, in which case it will return False instead
-catchPush :: IO () -> IO Bool
-catchPush p = Ex.catch
- (p >> return True)
- (\e -> case GIE.ioe_type e of
- GIE.ResourceVanished -> return False
- GIE.IllegalOperation -> return False
- _ -> Ex.throwIO e
- )
-
--- Connection state used when there is no connection.
-xmppNoConnection :: Connection
-xmppNoConnection = Connection
- { cHandle = ConnectionHandle { cSend = \_ -> return False
- , cRecv = \_ -> Ex.throwIO
- XmppOtherFailure
- , cFlush = return ()
- , cClose = return ()
- }
- , cEventSource = DCI.ResumableSource zeroSource (return ())
- , cFeatures = SF Nothing [] []
- , cState = ConnectionClosed
- , cHostName = Nothing
- , cJid = Nothing
- , cStreamLang = Nothing
- , cStreamId = Nothing
- , cPreferredLang = Nothing
- , cToJid = Nothing
- , cJidWhenPlain = False
- , cFrom = Nothing
- }
- where
- zeroSource :: Source IO output
- zeroSource = liftIO . Ex.throwIO $ XmppOtherFailure
-
-connectTcp :: HostName -> PortID -> Text -> IO (Either XmppFailure (TMVar Connection))
-connectTcp host port hostname = do
- let PortNumber portNumber = port
- debugM "Pontarius.Xmpp" $ "Connecting to " ++ host ++ " on port " ++
- (show portNumber) ++ " through the realm " ++ (T.unpack hostname) ++ "."
- h <- connectTo host port
- debugM "Pontarius.Xmpp" "Setting NoBuffering mode on handle."
- hSetBuffering h NoBuffering
- let eSource = DCI.ResumableSource
- ((sourceHandle h $= logConduit) $= XP.parseBytes def)
- (return ())
- let hand = ConnectionHandle { cSend = \d -> do
- let d64 = encode d
- debugM "Pontarius.Xmpp" $
- "Sending TCP data: " ++ (BSC8.unpack d64)
- ++ "."
- catchPush $ BS.hPut h d
- , cRecv = \n -> do
- d <- BS.hGetSome h n
- let d64 = encode d
- debugM "Pontarius.Xmpp" $
- "Received TCP data: " ++
- (BSC8.unpack d64) ++ "."
- return d
- , cFlush = hFlush h
- , cClose = hClose h
- }
- let con = Connection
- { cHandle = hand
- , cEventSource = eSource
- , cFeatures = (SF Nothing [] [])
- , cState = ConnectionPlain
- , cHostName = (Just hostname)
- , cJid = Nothing
- , cPreferredLang = Nothing -- TODO: Allow user to set
- , cStreamLang = Nothing
- , cStreamId = Nothing
- , cToJid = Nothing -- TODO: Allow user to set
- , cJidWhenPlain = False -- TODO: Allow user to set
- , cFrom = Nothing
- }
- con' <- mkConnection con
- return $ Right con'
- where
- logConduit :: Conduit ByteString IO ByteString
- logConduit = CL.mapM $ \d -> do
- let d64 = encode d
- debugM "Pontarius.Xmpp" $ "Received TCP data: " ++ (BSC8.unpack d64) ++
- "."
- return d
-
-
--- Closes the connection and updates the XmppConMonad Connection state.
--- killConnection :: TMVar Connection -> IO (Either Ex.SomeException ())
-killConnection :: TMVar Connection -> IO (Either XmppFailure ())
-killConnection = withConnection $ do
- cc <- gets (cClose . cHandle)
- err <- wrapIOException cc
- -- (Ex.try cc :: IO (Either Ex.SomeException ()))
- put xmppNoConnection
- return err
-
--- Sends an IQ request and waits for the response. If the response ID does not
--- match the outgoing ID, an error is thrown.
-pushIQ' :: StanzaId
- -> Maybe Jid
- -> IQRequestType
- -> Maybe LangTag
- -> Element
- -> TMVar Connection
- -> IO (Either XmppFailure (Either IQError IQResult))
-pushIQ' iqID to tp lang body con = do
- pushStanza (IQRequestS $ IQRequest iqID Nothing to lang tp body) con
- res <- pullStanza con
- case res of
- Left e -> return $ Left e
- Right (IQErrorS e) -> return $ Right $ Left e
- Right (IQResultS r) -> do
- unless
- (iqID == iqResultID r) . liftIO . Ex.throwIO $
- XmppOtherFailure
- -- TODO: Log: ("In sendIQ' IDs don't match: " ++ show iqID ++
- -- " /= " ++ show (iqResultID r) ++ " .")
- return $ Right $ Right r
- _ -> return $ Left XmppOtherFailure
- -- TODO: Log: "sendIQ': unexpected stanza type "
-
--- | Send "" and wait for the server to finish processing and to
--- close the connection. Any remaining elements from the server are returned.
--- Surpresses StreamEndFailure exceptions, but may throw a StreamCloseError.
-closeStreams :: TMVar Connection -> IO (Either XmppFailure [Element])
-closeStreams = withConnection $ do
- send <- gets (cSend . cHandle)
- cc <- gets (cClose . cHandle)
- liftIO $ send ""
- void $ liftIO $ forkIO $ do
- threadDelay 3000000 -- TODO: Configurable value
- (Ex.try cc) :: IO (Either Ex.SomeException ())
- return ()
- collectElems []
- where
- -- Pulls elements from the stream until the stream ends, or an error is
- -- raised.
- collectElems :: [Element] -> StateT Connection IO (Either XmppFailure [Element])
- collectElems es = do
- result <- pullElement
- case result of
- Left StreamEndFailure -> return $ Right es
- Left e -> return $ Left $ StreamCloseError (es, e)
- Right e -> collectElems (e:es)
-
-debugConduit :: Pipe l ByteString ByteString u IO b
-debugConduit = forever $ do
- s' <- await
- case s' of
- Just s -> do
- liftIO $ BS.putStrLn (BS.append "in: " s)
- yield s
- Nothing -> return ()
diff --git a/source/Network/Xmpp/Internal.hs b/source/Network/Xmpp/Internal.hs
index d921bc4..47da87d 100644
--- a/source/Network/Xmpp/Internal.hs
+++ b/source/Network/Xmpp/Internal.hs
@@ -8,21 +8,21 @@
-- This module allows for low-level access to Pontarius XMPP. Generally, the
-- "Network.Xmpp" module should be used instead.
--
--- The 'Connection' object provides the most low-level access to the XMPP
+-- The 'Stream' object provides the most low-level access to the XMPP
-- stream: a simple and single-threaded interface which exposes the conduit
-- 'Event' source, as well as the input and output byte streams. Custom stateful
--- 'Connection' functions can be executed using 'withConnection'.
+-- 'Stream' functions can be executed using 'withStream'.
--
-- The TLS, SASL, and 'Session' functionalities of Pontarius XMPP are built on
-- top of this API.
module Network.Xmpp.Internal
- ( Connection(..)
- , ConnectionState(..)
- , ConnectionHandle(..)
+ ( Stream(..)
+ , StreamState(..)
+ , StreamHandle(..)
, ServerFeatures(..)
- , connect
- , withConnection
+ , openStream
+ , withStream
, startTls
, simpleAuth
, auth
@@ -32,7 +32,7 @@ module Network.Xmpp.Internal
where
-import Network.Xmpp.Connection
+import Network.Xmpp.Stream
import Network.Xmpp.Sasl
import Network.Xmpp.Tls
import Network.Xmpp.Types
diff --git a/source/Network/Xmpp/Sasl.hs b/source/Network/Xmpp/Sasl.hs
index e272557..e6511a3 100644
--- a/source/Network/Xmpp/Sasl.hs
+++ b/source/Network/Xmpp/Sasl.hs
@@ -36,7 +36,6 @@ import qualified Data.Text as Text
import Data.Text (Text)
import qualified Data.Text.Encoding as Text
-import Network.Xmpp.Connection
import Network.Xmpp.Stream
import Network.Xmpp.Types
@@ -67,9 +66,9 @@ import Control.Monad.Error
-- authentication fails, or an `XmppFailure' if anything else fails.
xmppSasl :: [SaslHandler] -- ^ Acceptable authentication mechanisms and their
-- corresponding handlers
- -> TMVar Connection
+ -> TMVar Stream
-> IO (Either XmppFailure (Maybe AuthFailure))
-xmppSasl handlers = withConnection $ do
+xmppSasl handlers = withStream $ do
-- Chooses the first mechanism that is acceptable by both the client and the
-- server.
mechanisms <- gets $ saslMechanisms . cFeatures
@@ -78,7 +77,7 @@ xmppSasl handlers = withConnection $ do
(_name, handler):_ -> do
cs <- gets cState
case cs of
- ConnectionClosed -> return . Right $ Just AuthNoConnection
+ Closed -> return . Right $ Just AuthNoStream
_ -> do
r <- runErrorT handler
case r of
@@ -91,7 +90,7 @@ xmppSasl handlers = withConnection $ do
-- resource.
auth :: [SaslHandler]
-> Maybe Text
- -> TMVar Connection
+ -> TMVar Stream
-> IO (Either XmppFailure (Maybe AuthFailure))
auth mechanisms resource con = runErrorT $ do
ErrorT $ xmppSasl mechanisms con
@@ -107,7 +106,7 @@ simpleAuth :: Text.Text -- ^ The username
-> Text.Text -- ^ The password
-> Maybe Text -- ^ The desired resource or 'Nothing' to let the
-- server assign one
- -> TMVar Connection
+ -> TMVar Stream
-> IO (Either XmppFailure (Maybe AuthFailure))
simpleAuth username passwd resource = flip auth resource $
[ -- TODO: scramSha1Plus
@@ -126,7 +125,7 @@ bindBody = pickleElem $
-- Sends a (synchronous) IQ set request for a (`Just') given or server-generated
-- resource and extract the JID from the non-error response.
-xmppBind :: Maybe Text -> TMVar Connection -> IO (Either XmppFailure Jid)
+xmppBind :: Maybe Text -> TMVar Stream -> IO (Either XmppFailure Jid)
xmppBind rsrc c = runErrorT $ do
answer <- ErrorT $ pushIQ' "bind" Nothing Set Nothing (bindBody rsrc) c
case answer of
@@ -134,7 +133,7 @@ xmppBind rsrc c = runErrorT $ do
let jid = unpickleElem xpJid b
case jid of
Right jid' -> do
- ErrorT $ withConnection (do
+ ErrorT $ withStream (do
modify $ \s -> s{cJid = Just jid'}
return $ Right jid') c -- not pretty
return jid'
@@ -167,7 +166,7 @@ sessionIQ = IQRequestS $ IQRequest { iqRequestID = "sess"
-- 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 :: TMVar Connection -> IO ()
+startSession :: TMVar Stream -> IO ()
startSession con = do
answer <- pushIQ' "session" Nothing Set Nothing sessionXml con
case answer of
diff --git a/source/Network/Xmpp/Sasl/Common.hs b/source/Network/Xmpp/Sasl/Common.hs
index c38c843..c449c71 100644
--- a/source/Network/Xmpp/Sasl/Common.hs
+++ b/source/Network/Xmpp/Sasl/Common.hs
@@ -22,7 +22,7 @@ import Data.Word (Word8)
import Data.XML.Pickle
import Data.XML.Types
-import Network.Xmpp.Connection
+import Network.Xmpp.Stream
import Network.Xmpp.Sasl.StringPrep
import Network.Xmpp.Sasl.Types
import Network.Xmpp.Marshal
diff --git a/source/Network/Xmpp/Sasl/Mechanisms/DigestMd5.hs b/source/Network/Xmpp/Sasl/Mechanisms/DigestMd5.hs
index 3cab7e4..015086b 100644
--- a/source/Network/Xmpp/Sasl/Mechanisms/DigestMd5.hs
+++ b/source/Network/Xmpp/Sasl/Mechanisms/DigestMd5.hs
@@ -31,7 +31,6 @@ import qualified Data.ByteString as BS
import Data.XML.Types
-import Network.Xmpp.Connection
import Network.Xmpp.Stream
import Network.Xmpp.Types
import Network.Xmpp.Sasl.Common
diff --git a/source/Network/Xmpp/Sasl/Mechanisms/Plain.hs b/source/Network/Xmpp/Sasl/Mechanisms/Plain.hs
index 1ca91fe..caea0ec 100644
--- a/source/Network/Xmpp/Sasl/Mechanisms/Plain.hs
+++ b/source/Network/Xmpp/Sasl/Mechanisms/Plain.hs
@@ -35,7 +35,6 @@ import qualified Data.ByteString as BS
import Data.XML.Types
-import Network.Xmpp.Connection
import Network.Xmpp.Stream
import Network.Xmpp.Types
diff --git a/source/Network/Xmpp/Sasl/Types.hs b/source/Network/Xmpp/Sasl/Types.hs
index 90f20da..43879c7 100644
--- a/source/Network/Xmpp/Sasl/Types.hs
+++ b/source/Network/Xmpp/Sasl/Types.hs
@@ -15,7 +15,7 @@ data AuthFailure = AuthXmlFailure
-- itself
| AuthStreamFailure XmppFailure -- ^ Stream error on stream restart
-- TODO: Rename AuthConnectionFailure?
- | AuthNoConnection
+ | AuthNoStream
| AuthFailure -- General instance used for the Error instance
| AuthSaslFailure SaslFailure -- ^ Defined SASL error condition
| AuthStringPrepFailure -- ^ StringPrep failed
@@ -27,9 +27,9 @@ instance Error AuthFailure where
data SaslElement = SaslSuccess (Maybe Text.Text)
| SaslChallenge (Maybe Text.Text)
--- | SASL mechanism XmppConnection computation, with the possibility of throwing
+-- | SASL mechanism Stream computation, with the possibility of throwing
-- an authentication error.
-type SaslM a = ErrorT AuthFailure (StateT Connection IO) a
+type SaslM a = ErrorT AuthFailure (StateT Stream IO) a
type Pairs = [(ByteString, ByteString)]
diff --git a/source/Network/Xmpp/Stream.hs b/source/Network/Xmpp/Stream.hs
index 1b3d10c..614e522 100644
--- a/source/Network/Xmpp/Stream.hs
+++ b/source/Network/Xmpp/Stream.hs
@@ -1,6 +1,7 @@
{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE NoMonomorphismRestriction, OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE ScopedTypeVariables #-}
module Network.Xmpp.Stream where
@@ -20,16 +21,34 @@ import Data.Void (Void)
import Data.XML.Pickle
import Data.XML.Types
-import Network.Xmpp.Connection
import Network.Xmpp.Types
import Network.Xmpp.Marshal
import Text.Xml.Stream.Elements
import Text.XML.Stream.Parse as XP
+import Control.Concurrent (forkIO, threadDelay)
import Network
import Control.Concurrent.STM
+import Data.ByteString as BS
+import Data.ByteString.Base64
+import System.Log.Logger
+import qualified GHC.IO.Exception as GIE
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Class
+import System.IO.Error (tryIOError)
+import System.IO
+import Data.Conduit
+import Data.Conduit.Binary as CB
+import Data.Conduit.Internal as DCI
+import qualified Data.Conduit.List as CL
+import qualified Data.Text as T
+import Data.ByteString.Char8 as BSC8
+import Text.XML.Unresolved(InvalidEventStream(..))
+import qualified Control.Exception.Lifted as ExL
+
-- import Text.XML.Stream.Elements
-- Unpickles and returns a stream element.
@@ -67,16 +86,16 @@ openElementFromEvents = do
-- 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 XmppFailure
-- will be produced.
-startStream :: StateT Connection IO (Either XmppFailure ())
+startStream :: StateT Stream IO (Either XmppFailure ())
startStream = runErrorT $ do
state <- lift $ get
- con <- liftIO $ mkConnection state
+ stream <- liftIO $ mkStream state
-- Set the `from' (which is also the expected to) attribute depending on the
- -- state of the connection.
+ -- state of the stream.
let expectedTo = case cState state of
- ConnectionPlain -> if cJidWhenPlain state
+ Plain -> if cJidWhenPlain state
then cJid state else Nothing
- ConnectionSecured -> cJid state
+ Secured -> cJid state
case cHostName state of
Nothing -> throwError XmppOtherFailure -- TODO: When does this happen?
Just hostname -> lift $ do
@@ -93,15 +112,15 @@ startStream = runErrorT $ do
Left e -> throwError e
-- Successful unpickling of stream element.
Right (Right (ver, from, to, id, lt, features))
- | (unpack ver) /= "1.0" ->
- closeStreamWithError con StreamUnsupportedVersion Nothing
+ | (T.unpack ver) /= "1.0" ->
+ closeStreamWithError stream StreamUnsupportedVersion Nothing
| lt == Nothing ->
- closeStreamWithError con StreamInvalidXml Nothing
+ closeStreamWithError stream StreamInvalidXml Nothing
-- If `from' is set, we verify that it's the correct one. TODO: Should we check against the realm instead?
| isJust from && (from /= Just (Jid Nothing (fromJust $ cHostName state) Nothing)) ->
- closeStreamWithError con StreamInvalidFrom Nothing
+ closeStreamWithError stream StreamInvalidFrom Nothing
| to /= expectedTo ->
- closeStreamWithError con StreamUndefinedCondition (Just $ Element "invalid-to" [] []) -- TODO: Suitable?
+ closeStreamWithError stream StreamUndefinedCondition (Just $ Element "invalid-to" [] []) -- TODO: Suitable?
| otherwise -> do
modify (\s -> s{ cFeatures = features
, cStreamLang = lt
@@ -112,36 +131,36 @@ startStream = runErrorT $ do
-- Unpickling failed - we investigate the element.
Right (Left (Element name attrs children))
| (nameLocalName name /= "stream") ->
- closeStreamWithError con StreamInvalidXml Nothing
+ closeStreamWithError stream StreamInvalidXml Nothing
| (nameNamespace name /= Just "http://etherx.jabber.org/streams") ->
- closeStreamWithError con StreamInvalidNamespace Nothing
+ closeStreamWithError stream StreamInvalidNamespace Nothing
| (isJust $ namePrefix name) && (fromJust (namePrefix name) /= "stream") ->
- closeStreamWithError con StreamBadNamespacePrefix Nothing
- | otherwise -> ErrorT $ checkchildren con (flattenAttrs attrs)
+ closeStreamWithError stream StreamBadNamespacePrefix Nothing
+ | otherwise -> ErrorT $ checkchildren stream (flattenAttrs attrs)
where
- -- closeStreamWithError :: MonadIO m => TMVar Connection -> StreamErrorCondition ->
+ -- closeStreamWithError :: MonadIO m => TMVar Stream -> StreamErrorCondition ->
-- Maybe Element -> ErrorT XmppFailure m ()
- closeStreamWithError con sec el = do
+ closeStreamWithError stream sec el = do
liftIO $ do
- withConnection (pushElement . pickleElem xpStreamError $
- StreamErrorInfo sec Nothing el) con
- closeStreams con
+ withStream (pushElement . pickleElem xpStreamError $
+ StreamErrorInfo sec Nothing el) stream
+ closeStreams stream
throwError XmppOtherFailure
- checkchildren con children =
+ checkchildren stream children =
let to' = lookup "to" children
ver' = lookup "version" children
xl = lookup xmlLang children
in case () of () | Just (Nothing :: Maybe Jid) == (safeRead <$> to') ->
- runErrorT $ closeStreamWithError con
+ runErrorT $ closeStreamWithError stream
StreamBadNamespacePrefix Nothing
| Nothing == ver' ->
- runErrorT $ closeStreamWithError con
+ runErrorT $ closeStreamWithError stream
StreamUnsupportedVersion Nothing
| Just (Nothing :: Maybe LangTag) == (safeRead <$> xl) ->
- runErrorT $ closeStreamWithError con
+ runErrorT $ closeStreamWithError stream
StreamInvalidXml Nothing
| otherwise ->
- runErrorT $ closeStreamWithError con
+ runErrorT $ closeStreamWithError stream
StreamBadFormat Nothing
safeRead x = case reads $ Text.unpack x of
[] -> Nothing
@@ -159,7 +178,7 @@ flattenAttrs attrs = Prelude.map (\(name, content) ->
-- Sets a new Event source using the raw source (of bytes)
-- and calls xmppStartStream.
-restartStream :: StateT Connection IO (Either XmppFailure ())
+restartStream :: StateT Stream IO (Either XmppFailure ())
restartStream = do
raw <- gets (cRecv . cHandle)
let newSource = DCI.ResumableSource (loopRead raw $= XP.parseBytes def)
@@ -251,12 +270,252 @@ xpStreamFeatures = xpWrap
-- | Connects to the XMPP server and opens the XMPP stream against the given
-- host name, port, and realm.
-connect :: HostName -> PortID -> Text -> IO (Either XmppFailure (TMVar Connection))
-connect address port hostname = do
- con <- connectTcp address port hostname
- case con of
- Right con' -> do
- result <- withConnection startStream con'
- return $ Right con'
+openStream :: HostName -> PortID -> Text -> IO (Either XmppFailure (TMVar Stream))
+openStream address port hostname = do
+ stream <- connectTcp address port hostname
+ case stream of
+ Right stream' -> do
+ result <- withStream startStream stream'
+ return $ Right stream'
Left e -> do
return $ Left e
+
+-- | Send "" and wait for the server to finish processing and to
+-- close the connection. Any remaining elements from the server are returned.
+-- Surpresses StreamEndFailure exceptions, but may throw a StreamCloseError.
+closeStreams :: TMVar Stream -> IO (Either XmppFailure [Element])
+closeStreams = withStream $ do
+ send <- gets (cSend . cHandle)
+ cc <- gets (cClose . cHandle)
+ liftIO $ send ""
+ void $ liftIO $ forkIO $ do
+ threadDelay 3000000 -- TODO: Configurable value
+ (Ex.try cc) :: IO (Either Ex.SomeException ())
+ return ()
+ collectElems []
+ where
+ -- Pulls elements from the stream until the stream ends, or an error is
+ -- raised.
+ collectElems :: [Element] -> StateT Stream IO (Either XmppFailure [Element])
+ collectElems es = do
+ result <- pullElement
+ case result of
+ Left StreamEndFailure -> return $ Right es
+ Left e -> return $ Left $ StreamCloseError (es, e)
+ Right e -> collectElems (e:es)
+
+-- Enable/disable debug output
+-- This will dump all incoming and outgoing network taffic to the console,
+-- prefixed with "in: " and "out: " respectively
+debug :: Bool
+debug = False
+
+-- TODO: Can the TLS send/recv functions throw something other than an IO error?
+
+wrapIOException :: IO a -> StateT Stream IO (Either XmppFailure a)
+wrapIOException action = do
+ r <- liftIO $ tryIOError action
+ case r of
+ Right b -> return $ Right b
+ Left e -> return $ Left $ XmppIOException e
+
+pushElement :: Element -> StateT Stream IO (Either XmppFailure Bool)
+pushElement x = do
+ send <- gets (cSend . cHandle)
+ wrapIOException $ send $ renderElement x
+
+-- | Encode and send stanza
+pushStanza :: Stanza -> TMVar Stream -> IO (Either XmppFailure Bool)
+pushStanza s = withStream' . pushElement $ pickleElem xpStanza s
+
+-- XML documents and XMPP streams SHOULD be preceeded by an XML declaration.
+-- UTF-8 is the only supported XMPP encoding. The standalone document
+-- 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.
+pushXmlDecl :: StateT Stream IO (Either XmppFailure Bool)
+pushXmlDecl = do
+ con <- gets cHandle
+ wrapIOException $ (cSend con) ""
+
+pushOpenElement :: Element -> StateT Stream IO (Either XmppFailure Bool)
+pushOpenElement e = do
+ sink <- gets (cSend . cHandle)
+ wrapIOException $ sink $ renderOpenElement e
+
+-- `Connect-and-resumes' the given sink to the stream source, and pulls a
+-- `b' value.
+runEventsSink :: Sink Event IO b -> StateT Stream IO (Either XmppFailure b)
+runEventsSink snk = do -- TODO: Wrap exceptions?
+ source <- gets cEventSource
+ (src', r) <- lift $ source $$++ snk
+ modify (\s -> s{cEventSource = src'})
+ return $ Right r
+
+pullElement :: StateT Stream IO (Either XmppFailure Element)
+pullElement = do
+ ExL.catches (do
+ e <- runEventsSink (elements =$ await)
+ case e of
+ Left f -> return $ Left f
+ Right Nothing -> return $ Left XmppOtherFailure -- TODO
+ Right (Just r) -> return $ Right r
+ )
+ [ ExL.Handler (\StreamEnd -> return $ Left StreamEndFailure)
+ , ExL.Handler (\(InvalidXmppXml s) -- Invalid XML `Event' encountered, or missing element close tag
+ -> return $ Left XmppOtherFailure) -- TODO: Log: s
+ , ExL.Handler $ \(e :: InvalidEventStream) -- xml-conduit exception
+ -> return $ Left XmppOtherFailure -- TODO: Log: (show e)
+ ]
+
+-- Pulls an element and unpickles it.
+pullUnpickle :: PU [Node] a -> StateT Stream IO (Either XmppFailure a)
+pullUnpickle p = do
+ elem <- pullElement
+ case elem of
+ Left e -> return $ Left e
+ Right elem' -> do
+ let res = unpickleElem p elem'
+ case res of
+ Left e -> return $ Left XmppOtherFailure -- TODO: Log
+ Right r -> return $ Right r
+
+-- | Pulls a stanza (or stream error) from the stream.
+pullStanza :: TMVar Stream -> IO (Either XmppFailure Stanza)
+pullStanza = withStream' $ do
+ res <- pullUnpickle xpStreamStanza
+ case res of
+ Left e -> return $ Left e
+ Right (Left e) -> return $ Left $ StreamErrorFailure e
+ Right (Right r) -> return $ Right r
+
+-- Performs the given IO operation, catches any errors and re-throws everything
+-- except 'ResourceVanished' and IllegalOperation, in which case it will return False instead
+catchPush :: IO () -> IO Bool
+catchPush p = ExL.catch
+ (p >> return True)
+ (\e -> case GIE.ioe_type e of
+ GIE.ResourceVanished -> return False
+ GIE.IllegalOperation -> return False
+ _ -> ExL.throwIO e
+ )
+
+-- Stream state used when there is no connection.
+xmppNoStream :: Stream
+xmppNoStream = Stream
+ { cHandle = StreamHandle { cSend = \_ -> return False
+ , cRecv = \_ -> ExL.throwIO
+ XmppOtherFailure
+ , cFlush = return ()
+ , cClose = return ()
+ }
+ , cEventSource = DCI.ResumableSource zeroSource (return ())
+ , cFeatures = SF Nothing [] []
+ , cState = Closed
+ , cHostName = Nothing
+ , cJid = Nothing
+ , cStreamLang = Nothing
+ , cStreamId = Nothing
+ , cPreferredLang = Nothing
+ , cToJid = Nothing
+ , cJidWhenPlain = False
+ , cFrom = Nothing
+ }
+ where
+ zeroSource :: Source IO output
+ zeroSource = liftIO . ExL.throwIO $ XmppOtherFailure
+
+connectTcp :: HostName -> PortID -> Text -> IO (Either XmppFailure (TMVar Stream))
+connectTcp host port hostname = do
+ let PortNumber portNumber = port
+ debugM "Pontarius.Xmpp" $ "Connecting to " ++ host ++ " on port " ++
+ (show portNumber) ++ " through the realm " ++ (T.unpack hostname) ++ "."
+ h <- connectTo host port
+ debugM "Pontarius.Xmpp" "Setting NoBuffering mode on handle."
+ hSetBuffering h NoBuffering
+ let eSource = DCI.ResumableSource
+ ((sourceHandle h $= logConduit) $= XP.parseBytes def)
+ (return ())
+ let hand = StreamHandle { cSend = \d -> do
+ let d64 = encode d
+ debugM "Pontarius.Xmpp" $
+ "Sending TCP data: " ++ (BSC8.unpack d64)
+ ++ "."
+ catchPush $ BS.hPut h d
+ , cRecv = \n -> do
+ d <- BS.hGetSome h n
+ let d64 = encode d
+ debugM "Pontarius.Xmpp" $
+ "Received TCP data: " ++
+ (BSC8.unpack d64) ++ "."
+ return d
+ , cFlush = hFlush h
+ , cClose = hClose h
+ }
+ let stream = Stream
+ { cHandle = hand
+ , cEventSource = eSource
+ , cFeatures = (SF Nothing [] [])
+ , cState = Plain
+ , cHostName = (Just hostname)
+ , cJid = Nothing
+ , cPreferredLang = Nothing -- TODO: Allow user to set
+ , cStreamLang = Nothing
+ , cStreamId = Nothing
+ , cToJid = Nothing -- TODO: Allow user to set
+ , cJidWhenPlain = False -- TODO: Allow user to set
+ , cFrom = Nothing
+ }
+ stream' <- mkStream stream
+ return $ Right stream'
+ where
+ logConduit :: Conduit ByteString IO ByteString
+ logConduit = CL.mapM $ \d -> do
+ let d64 = encode d
+ debugM "Pontarius.Xmpp" $ "Received TCP data: " ++ (BSC8.unpack d64) ++
+ "."
+ return d
+
+
+-- Closes the connection and updates the XmppConMonad Stream state.
+-- killStream :: TMVar Stream -> IO (Either ExL.SomeException ())
+killStream :: TMVar Stream -> IO (Either XmppFailure ())
+killStream = withStream $ do
+ cc <- gets (cClose . cHandle)
+ err <- wrapIOException cc
+ -- (ExL.try cc :: IO (Either ExL.SomeException ()))
+ put xmppNoStream
+ return err
+
+-- Sends an IQ request and waits for the response. If the response ID does not
+-- match the outgoing ID, an error is thrown.
+pushIQ' :: StanzaId
+ -> Maybe Jid
+ -> IQRequestType
+ -> Maybe LangTag
+ -> Element
+ -> TMVar Stream
+ -> IO (Either XmppFailure (Either IQError IQResult))
+pushIQ' iqID to tp lang body stream = do
+ pushStanza (IQRequestS $ IQRequest iqID Nothing to lang tp body) stream
+ res <- pullStanza stream
+ case res of
+ Left e -> return $ Left e
+ Right (IQErrorS e) -> return $ Right $ Left e
+ Right (IQResultS r) -> do
+ unless
+ (iqID == iqResultID r) . liftIO . ExL.throwIO $
+ XmppOtherFailure
+ -- TODO: Log: ("In sendIQ' IDs don't match: " ++ show iqID ++
+ -- " /= " ++ show (iqResultID r) ++ " .")
+ return $ Right $ Right r
+ _ -> return $ Left XmppOtherFailure
+ -- TODO: Log: "sendIQ': unexpected stanza type "
+
+debugConduit :: Pipe l ByteString ByteString u IO b
+debugConduit = forever $ do
+ s' <- await
+ case s' of
+ Just s -> do
+ liftIO $ BS.putStrLn (BS.append "in: " s)
+ yield s
+ Nothing -> return ()
diff --git a/source/Network/Xmpp/Tls.hs b/source/Network/Xmpp/Tls.hs
index e8c3c87..80ab2f7 100644
--- a/source/Network/Xmpp/Tls.hs
+++ b/source/Network/Xmpp/Tls.hs
@@ -17,7 +17,6 @@ import Data.Conduit.Tls as TLS
import Data.Typeable
import Data.XML.Types
-import Network.Xmpp.Connection
import Network.Xmpp.Stream
import Network.Xmpp.Types
@@ -75,16 +74,16 @@ exampleParams = TLS.defaultParamsClient
-- Pushes ", waits for "", performs the TLS handshake, and
-- restarts the stream.
-startTls :: TLS.TLSParams -> TMVar Connection -> IO (Either XmppFailure ())
+startTls :: TLS.TLSParams -> TMVar Stream -> IO (Either XmppFailure ())
startTls params con = Ex.handle (return . Left . TlsError)
- . flip withConnection con
+ . flip withStream con
. runErrorT $ do
features <- lift $ gets cFeatures
state <- gets cState
case state of
- ConnectionPlain -> return ()
- ConnectionClosed -> throwError XmppNoConnection
- ConnectionSecured -> throwError TlsConnectionSecured
+ Plain -> return ()
+ Closed -> throwError XmppNoStream
+ Secured -> throwError TlsStreamSecured
con <- lift $ gets cHandle
when (stls features == Nothing) $ throwError TlsNoServerSupport
lift $ pushElement starttlsE
@@ -94,12 +93,12 @@ startTls params con = Ex.handle (return . Left . TlsError)
Right (Element "{urn:ietf:params:xml:ns:xmpp-tls}proceed" [] []) -> return $ Right ()
Right (Element "{urn:ietf:params:xml:ns:xmpp-tls}failure" _ _) -> return $ Left XmppOtherFailure
(raw, _snk, psh, read, ctx) <- lift $ TLS.tlsinit debug params (mkBackend con)
- let newHand = ConnectionHandle { cSend = catchPush . psh
+ let newHand = StreamHandle { cSend = catchPush . psh
, cRecv = read
, cFlush = contextFlush ctx
, cClose = bye ctx >> cClose con
}
lift $ modify ( \x -> x {cHandle = newHand})
either (lift . Ex.throwIO) return =<< lift restartStream
- modify (\s -> s{cState = ConnectionSecured})
+ modify (\s -> s{cState = Secured})
return ()
diff --git a/source/Network/Xmpp/Types.hs b/source/Network/Xmpp/Types.hs
index 13c6d5e..0a686cd 100644
--- a/source/Network/Xmpp/Types.hs
+++ b/source/Network/Xmpp/Types.hs
@@ -31,12 +31,12 @@ module Network.Xmpp.Types
, XmppFailure(..)
, StreamErrorCondition(..)
, Version(..)
- , ConnectionHandle(..)
- , Connection(..)
- , withConnection
- , withConnection'
- , mkConnection
- , ConnectionState(..)
+ , StreamHandle(..)
+ , Stream(..)
+ , withStream
+ , withStream'
+ , mkStream
+ , StreamState(..)
, StreamErrorInfo(..)
, langTag
, Jid(..)
@@ -652,8 +652,8 @@ data XmppFailure = StreamErrorFailure StreamErrorInfo -- ^ An error XML stream
-- far.
| TlsError TLS.TLSError
| TlsNoServerSupport
- | XmppNoConnection
- | TlsConnectionSecured -- ^ Connection already secured
+ | XmppNoStream
+ | TlsStreamSecured -- ^ Connection already secured
| XmppOtherFailure -- ^ Undefined condition. More
-- information should be available
-- in the log.
@@ -762,27 +762,27 @@ data ServerFeatures = SF
} deriving Show
-- | Signals the state of the connection.
-data ConnectionState
- = ConnectionClosed -- ^ No connection at this point.
- | ConnectionPlain -- ^ Connection established, but not secured.
- | ConnectionSecured -- ^ Connection established and secured via TLS.
+data StreamState
+ = Closed -- ^ No stream at this point.
+ | Plain -- ^ Stream established, but not secured.
+ | Secured -- ^ Stream established and secured via TLS.
deriving (Show, Eq, Typeable)
-- | Defines operations for sending, receiving, flushing, and closing on a
-- connection.
-data ConnectionHandle =
- ConnectionHandle { cSend :: BS.ByteString -> IO Bool
- , cRecv :: Int -> IO BS.ByteString
- -- This is to hold the state of the XML parser (otherwise
- -- we will receive EventBeginDocument events and forget
- -- about name prefixes).
- , cFlush :: IO ()
- , cClose :: IO ()
- }
-
-data Connection = Connection
- { cState :: !ConnectionState -- ^ State of connection
- , cHandle :: ConnectionHandle -- ^ Handle to send, receive, flush, and close
+data StreamHandle =
+ StreamHandle { cSend :: BS.ByteString -> IO Bool
+ , cRecv :: Int -> IO BS.ByteString
+ -- This is to hold the state of the XML parser (otherwise
+ -- we will receive EventBeginDocument events and forget
+ -- about name prefixes).
+ , cFlush :: IO ()
+ , cClose :: IO ()
+ }
+
+data Stream = Stream
+ { cState :: !StreamState -- ^ State of connection
+ , cHandle :: StreamHandle -- ^ Handle to send, receive, flush, and close
-- on the connection.
, cEventSource :: ResumableSource IO Event -- ^ Event conduit source, and
-- its associated finalizer
@@ -803,26 +803,26 @@ data Connection = Connection
-- element's `from' attribute.
}
-withConnection :: StateT Connection IO (Either XmppFailure c) -> TMVar Connection -> IO (Either XmppFailure c)
-withConnection action con = bracketOnError
- (atomically $ takeTMVar con)
- (atomically . putTMVar con )
- (\c -> do
- (r, c') <- runStateT action c
- atomically $ putTMVar con c'
+withStream :: StateT Stream IO (Either XmppFailure c) -> TMVar Stream -> IO (Either XmppFailure c)
+withStream action stream = bracketOnError
+ (atomically $ takeTMVar stream)
+ (atomically . putTMVar stream)
+ (\s -> do
+ (r, s') <- runStateT action s
+ atomically $ putTMVar stream s'
return r
)
-- nonblocking version. Changes to the connection are ignored!
-withConnection' :: StateT Connection IO (Either XmppFailure b) -> TMVar Connection -> IO (Either XmppFailure b)
-withConnection' action con = do
- con_ <- atomically $ readTMVar con
- (r, _) <- runStateT action con_
+withStream' :: StateT Stream IO (Either XmppFailure b) -> TMVar Stream -> IO (Either XmppFailure b)
+withStream' action stream = do
+ stream_ <- atomically $ readTMVar stream
+ (r, _) <- runStateT action stream_
return r
-mkConnection :: Connection -> IO (TMVar Connection)
-mkConnection con = {- Connection `fmap` -} (atomically $ newTMVar con)
+mkStream :: Stream -> IO (TMVar Stream)
+mkStream con = {- Stream `fmap` -} (atomically $ newTMVar con)
---------------
-- JID
diff --git a/source/Network/Xmpp/Xep/InbandRegistration.hs b/source/Network/Xmpp/Xep/InbandRegistration.hs
index 6e14447..c8ceeb6 100644
--- a/source/Network/Xmpp/Xep/InbandRegistration.hs
+++ b/source/Network/Xmpp/Xep/InbandRegistration.hs
@@ -19,7 +19,7 @@ import qualified Data.Text as Text
import Data.XML.Pickle
import qualified Data.XML.Types as XML
-import Network.Xmpp.Connection
+import Network.Xmpp.Stream
import Network.Xmpp.Pickle
import Network.Xmpp.Types
import Network.Xmpp.Xep.ServiceDiscovery
@@ -32,7 +32,7 @@ ibrns = "jabber:iq:register"
ibrName x = (XML.Name x (Just ibrns) Nothing)
data IbrError = IbrNotSupported
- | IbrNoConnection
+ | IbrNoStream
| IbrIQError IQError
deriving (Show)
@@ -61,7 +61,7 @@ emptyQuery = Query Nothing False False []
-- hn' <- gets sHostname
-- hn <- case hn' of
-- Just h -> return (Jid Nothing h Nothing)
--- Nothing -> throwError IbrNoConnection
+-- Nothing -> throwError IbrNoStream
-- qi <- lift $ xmppQueryInfo Nothing Nothing
-- case qi of
-- Left e -> return False
@@ -71,7 +71,7 @@ emptyQuery = Query Nothing False False []
-- if r then return True else g
-query :: IQRequestType -> Query -> TMVar Connection -> IO (Either IbrError Query)
+query :: IQRequestType -> Query -> TMVar Stream -> IO (Either IbrError Query)
query queryType x con = do
answer <- pushIQ' "ibr" Nothing queryType Nothing (pickleElem xpQuery x) con
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
-- the server
registerWith :: [(Field, Text.Text)]
- -> TMVar Connection
+ -> TMVar Stream
-> IO (Either RegisterError Query)
registerWith givenFields con = runErrorT $ do
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
-- work. You connection will most likely be terminated after unregistering.
-unregister :: TMVar Connection -> IO (Either IbrError Query)
+unregister :: TMVar Stream -> IO (Either IbrError Query)
unregister = query Set $ emptyQuery {remove = True}
requestFields con = runErrorT $ do
diff --git a/source/Network/Xmpp/Xep/ServiceDiscovery.hs b/source/Network/Xmpp/Xep/ServiceDiscovery.hs
index 2b695c4..01be720 100644
--- a/source/Network/Xmpp/Xep/ServiceDiscovery.hs
+++ b/source/Network/Xmpp/Xep/ServiceDiscovery.hs
@@ -27,7 +27,7 @@ import Data.XML.Types
import Network.Xmpp
import Network.Xmpp.Concurrent
import Network.Xmpp.Concurrent.Types
-import Network.Xmpp.Connection
+import Network.Xmpp.Stream
import Network.Xmpp.Marshal
import Network.Xmpp.Types
import Control.Concurrent.STM.TMVar
@@ -105,7 +105,7 @@ queryInfo to node context = do
xmppQueryInfo :: Maybe Jid
-> Maybe Text.Text
- -> TMVar Connection
+ -> TMVar Stream
-> IO (Either DiscoError QueryInfoResult)
xmppQueryInfo to node con = do
res <- pushIQ' "info" to Get Nothing queryBody con