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