Browse Source

started using xml-conduit instead of xml-enumerator, but because of what seems to be a bug in parseBytes we can't actually read anything

master
Jon Kristensen 14 years ago
parent
commit
9f96c71145
  1. 71
      Network/XMPP/Session.hs
  2. 164
      Network/XMPP/Stream.hs
  3. 75
      Network/XMPP/Types.hs
  4. 6
      pontarius.cabal

71
Network/XMPP/Session.hs

@ -8,9 +8,7 @@
{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
module Network.XMPP.Session ( module Network.XMPP.Session (
@ -23,11 +21,11 @@ module Network.XMPP.Session (
, DisconnectReason , DisconnectReason
) where ) where
import Network.XMPP.Stream
import Network.XMPP.Types import Network.XMPP.Types
import Network.XMPP.Utilities import Network.XMPP.Utilities
import Control.Concurrent (Chan, newChan, readChan, writeChan) import Control.Concurrent (Chan, forkIO, forkOS, newChan, readChan, writeChan)
import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.Certificate.X509 (X509) import Data.Certificate.X509 (X509)
import Data.Dynamic (Dynamic) import Data.Dynamic (Dynamic)
@ -42,19 +40,6 @@ import Codec.Binary.UTF8.String
-- |
-- The XMPP monad transformer. Contains internal state in order to
-- work with Pontarius. Pontarius clients needs to operate in this
-- context.
newtype XMPPT m a = XMPPT { runXMPPT :: StateT (State m) m a } deriving (Monad, MonadIO)
-- Make XMPPT derive the Monad and MonadIO instances.
deriving instance (Monad m, MonadIO m) => MonadState (State m) (XMPPT m)
create :: MonadIO m => XMPPT m () -> m () create :: MonadIO m => XMPPT m () -> m ()
create main = do create main = do
@ -68,31 +53,10 @@ create main = do
stateLoop stateLoop
data HookId = HookId String deriving (Eq)
-- We need a channel because multiple threads needs to append events,
-- and we need to wait for events when there are none.
data State m = State { evtChan :: Chan (InternalEvent m)
, hookIdGenerator :: IdGenerator
, hooks :: [Hook m] }
data HookPayload m = StreamsOpenedHook (Maybe (Maybe OpenStreamsFailureReason -> XMPPT m Bool)) (Maybe OpenStreamsFailureReason -> XMPPT m Bool)
type Hook m = (HookId, HookPayload m)
-- Internal events - events to be processed within Pontarius. -- Internal events - events to be processed within Pontarius.
-- data InternalEvent s m = IEC (ClientEvent s m) | IEE EnumeratorEvent | IET (TimeoutEvent s m) deriving (Show) -- data InternalEvent s m = IEC (ClientEvent s m) | IEE EnumeratorEvent | IET (TimeoutEvent s m) deriving (Show)
data InternalEvent m
= OpenStreamsEvent HostName PortNumber
-- | DisconnectEvent
| RegisterStreamsOpenedHook (Maybe (Maybe OpenStreamsFailureReason -> XMPPT m Bool)) (Maybe OpenStreamsFailureReason -> XMPPT m Bool)
-- | IEEE EnumeratorEvent
instance Show (InternalEvent m) where instance Show (InternalEvent m) where
show _ = "InternalEvent" show _ = "InternalEvent"
@ -121,17 +85,6 @@ data Event = -- ConnectedEvent (Either IntFailureReason Resource)
-- | CAFR AuthenticateFailureReason -- | CAFR AuthenticateFailureReason
-- TODO: Possible ways opening a stream can fail.
data OpenStreamsFailureReason = OpenStreamsFailureReason deriving (Show)
-- data TLSSecureFailureReason = TLSSecureFailureReason
-- data AuthenticateFailureReason = AuthenticateFailureReason
data DisconnectReason = DisconnectReason deriving (Show)
-- The "hook modification" events have a higher priority than other events, and -- The "hook modification" events have a higher priority than other events, and
-- are thus sent through a Chan of their own. The boolean returns value signals -- are thus sent through a Chan of their own. The boolean returns value signals
-- whether or not the hook should be removed. -- whether or not the hook should be removed.
@ -153,30 +106,34 @@ stateLoop = do
rs <- get rs <- get
event <- liftIO $ readChan $ evtChan rs event <- liftIO $ readChan $ evtChan rs
liftIO $ putStrLn $ "Processing " ++ (show event) ++ "..." liftIO $ putStrLn $ "Processing " ++ (show event) ++ "..."
actions <- processEvent event processEvent event
sequence actions -- sequence_ IO actions frmo procesEvent?
stateLoop stateLoop
-- Processes an internal event and generates a list of impure actions. -- Processes an internal event and generates a list of impure actions.
processEvent :: MonadIO m => InternalEvent m -> XMPPT m [XMPPT m ()] processEvent :: MonadIO m => InternalEvent m -> XMPPT m ()
processEvent (OpenStreamsEvent h p) = return [openStreamAction h p] processEvent (OpenStreamsEvent h p) = openStreamAction h p
where where
openStreamAction :: MonadIO m => HostName -> PortNumber -> XMPPT m () openStreamAction :: MonadIO m => HostName -> PortNumber -> XMPPT m ()
openStreamAction h p = let p' = fromIntegral p openStreamAction h p = let p' = fromIntegral p
computation = do computation chan = do -- chan ugly
-- threadID <-
handle <- N.connectTo h (N.PortNumber p') handle <- N.connectTo h (N.PortNumber p')
hSetBuffering handle NoBuffering hSetBuffering handle NoBuffering
hPutStr handle $ encodeString "<?xml version='1.0'?><stream:stream to='" ++ h ++ "' version='1.0' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>" forkIO $ conduit chan (Left handle) -- This must be done after hSetBuffering
hPutStr handle $ encodeString "<stream:stream to='" ++ h ++ "' version='1.0' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>" -- didn't work with <?xml version='1.0'>
hFlush handle hFlush handle
return ()
in do in do
result <- liftIO $ CE.try computation rs <- get
result <- liftIO $ CE.try (computation $ evtChan rs)
case result of case result of
Right () -> do Right () -> do
fireStreamsOpenedEvent Nothing fireStreamsOpenedEvent Nothing
-- -- threadID <- lift $ liftIO $ forkIO $ xmlEnumerator (stateChannel state) (Left handle) return ()
-- -- lift $ liftIO $ putMVar (stateThreadID state) threadID -- -- lift $ liftIO $ putMVar (stateThreadID state) threadID
Left (CE.SomeException e) -> do -- TODO: Safe to do this? Left (CE.SomeException e) -> do -- TODO: Safe to do this?
fireStreamsOpenedEvent $ Just OpenStreamsFailureReason fireStreamsOpenedEvent $ Just OpenStreamsFailureReason

164
Network/XMPP/Stream.hs

@ -6,7 +6,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
module Network.XMPP.Stream ( module Network.XMPP.Stream (
-- xmlEnumerator, conduit,
presenceToXML, presenceToXML,
iqToXML, iqToXML,
messageToXML, messageToXML,
@ -26,8 +26,8 @@ import Control.Concurrent.Chan (Chan, writeChan)
import Control.Exception.Base (SomeException) import Control.Exception.Base (SomeException)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
import Data.ByteString.Lazy (null, toChunks) import Data.ByteString.Lazy (null, toChunks)
import Data.Enumerator ((>>==), ($$), Iteratee (..), Enumeratee, Step (..), Enumerator (..), Stream (Chunks), returnI, joinI, run) import Data.Conduit (($$), ($=), MonadResource, Sink (..), runResourceT)
import Data.Enumerator.Binary (enumHandle) import Data.Conduit.Binary (sourceHandle)
import Data.Maybe (fromJust, isJust) import Data.Maybe (fromJust, isJust)
import Data.Text (pack, unpack) import Data.Text (pack, unpack)
import Data.XML.Types (Content (..), Document (..), Element (..), Event (..), Name (..), Node (..)) import Data.XML.Types (Content (..), Document (..), Element (..), Event (..), Name (..), Node (..))
@ -36,104 +36,122 @@ import Network.TLS (TLSCtx, recvData)
import Text.Parsec (char, count, digit, eof, many, many1, oneOf, parse) import Text.Parsec (char, count, digit, eof, many, many1, oneOf, parse)
import Text.Parsec.ByteString (GenParser) import Text.Parsec.ByteString (GenParser)
-- import Text.XML.Enumerator.Document (fromEvents) -- import Text.XML.Enumerator.Document (fromEvents)
-- import Text.XML.Enumerator.Parse (parseBytes, decodeEntities) import Text.XML.Stream.Parse (def, parseBytes)
import Text.XML.Unresolved (fromEvents)
import qualified Data.ByteString as DB (ByteString)
import qualified Data.ByteString.Char8 as DBC (pack)
import qualified Data.Enumerator.List as DEL (head)
import Control.Monad.IO.Class (MonadIO, liftIO)
-- Reads from the provided handle or TLS context and sends the events to the
-- internal event channel.
-- xmlEnumerator :: Chan InternalEvent -> Either Handle TLSCtx -> IO () -- Was: InternalEvent s m import qualified Data.ByteString as DB (ByteString)
import qualified Data.ByteString.Char8 as DBC (pack)
-- xmlEnumerator c s = do import qualified Data.Conduit.List as DEL (head)
-- enumeratorResult <- case s of import Data.Conduit.List (consume, sourceList) -- use lazy consume instead?
-- Left handle -> run $ enumHandle 1 handle $$ joinI $
-- parseBytes decodeEntities $$ eventConsumer c [] 0
-- Right tlsCtx -> run $ enumTLS tlsCtx $$ joinI $ -- Reads from the provided handle or TLS context and sends the events
-- parseBytes decodeEntities $$ eventConsumer c [] 0 -- to the internal event channel.
-- case enumeratorResult of
-- Right _ -> return () -- writeChan c $ IEE EnumeratorDone conduit :: MonadIO m => Chan (InternalEvent m) -> Either Handle (TLSCtx a) -> IO ()
-- Left e -> return () -- writeChan c $ IEE (EnumeratorException e)
conduit c s = do
enumeratorResult <- case s of
Left handle -> do
print <- runResourceT $ sourceHandle handle $= parseBytes def $$ DEL.head -- $$ DEL.head -- eventConsumer c [] 0
return $ Right 0 -- TODO
Right tlsCtx -> -- run $ enumTLS tlsCtx $$ joinI $
-- parseBytes decodeEntities $$ eventConsumer c [] 0
return $ Left 0 -- TODO
case enumeratorResult of
Right _ -> return () -- writeChan c $ IEE EnumeratorDone
Left e -> return () -- writeChan c $ IEE (EnumeratorException e)
-- where -- where
-- -- Behaves like enumHandle, but reads from the TLS context instead -- -- Behaves like enumHandle, but reads from the TLS context instead
-- -- TODO: Type? -- -- TODO: Type?
-- enumTLS :: TLSCtx -> Enumerator DB.ByteString IO b -- enumTLS :: TLSCtx -> Enumerator DB.ByteString IO b
-- enumTLS c s = loop c s -- enumTLS c s = loop c s
--
-- -- TODO: Type? -- -- TODO: Type?
-- loop :: TLSCtx -> Step DB.ByteString IO b -> Iteratee DB.ByteString IO b -- loop :: TLSCtx -> Step DB.ByteString IO b -> Iteratee DB.ByteString IO b
-- loop c (Continue k) = do -- loop c (Continue k) = do
-- d <- recvData c -- d <- recvData c
-- case null d of -- case null d of
-- True -> loop c (Continue k) -- True -> loop c (Continue k)
-- False -> k (Chunks $ toChunks d) >>== loop c -- False -> k (Chunks $ toChunks d) >>== loop c
-- loop _ step = returnI step -- loop _ step = returnI step
-- Consumes XML events from the input stream, accumulating as necessary, and -- Consumes XML events from the input stream, accumulating as
-- sends the proper events through the channel. The second parameter should be -- necessary, and sends the proper events through the channel. The
-- initialized to [] (no events) and the third to 0 (zeroth XML level). -- second parameter should be initialized to [] (no events) and the
-- third to 0 (zeroth XML level).
-- eventConsumer :: Chan InternalEvent -> [Event] -> Int -> eventConsumer :: (MonadResource r, MonadIO m) =>
-- Iteratee Event IO (Maybe Event) -- Was: InternalEvent s m Chan (InternalEvent m) -> [Event] -> Int -> Sink Event r ()
-- <stream:stream> open event received. -- <stream:stream> open event received.
-- eventConsumer chan [EventBeginElement (Name localName namespace prefixName) attribs] 0 eventConsumer chan [EventBeginElement (Name localName namespace prefixName) attribs] 0
-- | localName == pack "stream" && isJust prefixName && fromJust prefixName == pack "stream" = do | localName == pack "stream" && isJust prefixName && fromJust prefixName == pack "stream" = do
-- liftIO $ return () -- writeChan chan $ IEE $ EnumeratorBeginStream from to id ver lang ns liftIO $ putStrLn "here?"
-- eventConsumer chan [] 1 liftIO $ writeChan chan $ EnumeratorBeginStream from to id ver lang ns
-- where eventConsumer chan [] 1
-- from = case lookup "from" attribs of Nothing -> Nothing; Just fromAttrib -> Just $ show fromAttrib where
-- to = case lookup "to" attribs of Nothing -> Nothing; Just toAttrib -> Just $ show toAttrib from = case lookup "from" attribs of Nothing -> Nothing; Just fromAttrib -> Just $ show fromAttrib
-- id = case lookup "id" attribs of Nothing -> Nothing; Just idAttrib -> Just $ show idAttrib to = case lookup "to" attribs of Nothing -> Nothing; Just toAttrib -> Just $ show toAttrib
-- ver = case lookup "version" attribs of Nothing -> Nothing; Just verAttrib -> Just $ show verAttrib id = case lookup "id" attribs of Nothing -> Nothing; Just idAttrib -> Just $ show idAttrib
-- lang = case lookup "xml:lang" attribs of Nothing -> Nothing; Just langAttrib -> Just $ show langAttrib ver = case lookup "version" attribs of Nothing -> Nothing; Just verAttrib -> Just $ show verAttrib
-- ns = case namespace of Nothing -> Nothing; Just namespaceAttrib -> Just $ unpack namespaceAttrib lang = case lookup "xml:lang" attribs of Nothing -> Nothing; Just langAttrib -> Just $ show langAttrib
ns = case namespace of Nothing -> Nothing; Just namespaceAttrib -> Just $ unpack namespaceAttrib
-- <stream:stream> close event received. -- <stream:stream> close event received.
-- eventConsumer chan [EventEndElement name] 1 eventConsumer chan [EventEndElement name] 1
-- | namePrefix name == Just (pack "stream") && nameLocalName name == pack "stream" = do | namePrefix name == Just (pack "stream") && nameLocalName name == pack "stream" = do
-- liftIO $ return () -- writeChan chan $ IEE $ EnumeratorEndStream liftIO $ putStrLn "here!"
-- return Nothing liftIO $ writeChan chan $ EnumeratorEndStream
return ()
-- Ignore EventDocumentBegin event. -- Ignore EventDocumentBegin event.
-- eventConsumer chan [EventBeginDocument] 0 = eventConsumer chan [] 0 eventConsumer chan [EventBeginDocument] 0 = eventConsumer chan [] 0
-- We have received a complete first-level XML element. Process the accumulated -- We have received a complete first-level XML element. Process the accumulated
-- values into an first-level element event. -- values into an first-level element event.
-- eventConsumer chan ((EventEndElement e):es) 1 = do eventConsumer chan ((EventEndElement e):es) 1 = do
-- liftIO $ return () -- writeChan chan $ IEE $ EnumeratorFirstLevelElement $ eventsToElement $ reverse ((EventEndElement e):es) liftIO $ putStrLn "here..."
-- eventConsumer chan [] 1 element <- liftIO $ eventsToElement $ reverse ((EventEndElement e):es)
liftIO $ writeChan chan $ EnumeratorFirstLevelElement element
eventConsumer chan [] 1
-- Normal condition - accumulate the event. -- Normal condition - accumulate the event.
-- eventConsumer chan events level = do eventConsumer chan events level = do
-- event <- DEL.head liftIO $ putStrLn "listenering for XML event"
-- case event of event <- DEL.head
-- Just event' -> let level' = case event' of liftIO $ putStrLn "got event"
-- EventBeginElement _ _ -> level + 1 case event of
-- EventEndElement _ -> level - 1 Just event' -> let level' = case event' of
-- _ -> level EventBeginElement _ _ -> level + 1
-- in eventConsumer chan (event':events) level' EventEndElement _ -> level - 1
-- Nothing -> eventConsumer chan events level _ -> level
in eventConsumer chan (event':events) level'
Nothing -> eventConsumer chan events level
eventsToElement :: [Event] -> IO Element -- Was: Either SomeException Element
-- eventsToElement :: [Event] -> Either SomeException Element -- TODO: Exceptions.
-- eventsToElement e = do eventsToElement e = do
-- r <- run $ eventsEnum $$ fromEvents putStrLn "eventsToElement"
-- case r of Right doc -> Right $ documentRoot doc; Left ex -> Left ex doc <- runResourceT $ sourceList e $$ fromEvents
return $ documentRoot doc
-- case r of Right doc -> Right $ documentRoot doc; Left ex -> Left ex
-- where -- where
-- -- TODO: Type? -- -- TODO: Type?
-- eventsEnum (Continue k) = k $ Chunks e -- eventsEnum (Continue k) = k $ Chunks e
-- eventsEnum step = returnI step -- eventsEnum step = returnI step
-- Sending stanzas is done through functions, where LangTag is Maybe. -- Sending stanzas is done through functions, where LangTag is Maybe.

75
Network/XMPP/Types.hs

@ -4,6 +4,9 @@
{-# OPTIONS_HADDOCK hide #-} {-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
module Network.XMPP.Types ( module Network.XMPP.Types (
StanzaID (..), StanzaID (..),
@ -24,9 +27,9 @@ StanzaErrorCondition (..),
, PortNumber , PortNumber
, Resource , Resource
, UserName, , UserName,
EnumeratorEvent (..),
Challenge (..), Challenge (..),
Success (..), Success (..),
InternalEvent (..),
-- TLSState (..), -- TLSState (..),
Address (..), Address (..),
Localpart, Localpart,
@ -35,6 +38,9 @@ Resourcepart,
LangTag (..), LangTag (..),
ConnectionState (..), ConnectionState (..),
ClientEvent (..), ClientEvent (..),
XMPPT (..),
OpenStreamsFailureReason (..),
DisconnectReason (..),
StreamState (..), StreamState (..),
AuthenticationState (..), AuthenticationState (..),
ConnectResult (..), ConnectResult (..),
@ -55,6 +61,10 @@ PresenceError (..),
InternalPresence (..), InternalPresence (..),
InternalMessage (..), InternalMessage (..),
MessageError (..), MessageError (..),
HookId (..),
Hook (..),
HookPayload (..),
State (..)
) where ) where
import GHC.IO.Handle (Handle, hPutStr, hFlush, hSetBuffering, hWaitForInput) import GHC.IO.Handle (Handle, hPutStr, hFlush, hSetBuffering, hWaitForInput)
@ -81,6 +91,8 @@ import Data.Char (toLower)
import Control.Exception.Base (SomeException) import Control.Exception.Base (SomeException)
import Control.Concurrent
-- ============================================================================= -- =============================================================================
-- STANZA TYPES -- STANZA TYPES
-- ============================================================================= -- =============================================================================
@ -136,6 +148,56 @@ data IQRequest = IQRequest { iqRequestID :: Maybe StanzaID
deriving (Show) deriving (Show)
data InternalEvent m
= OpenStreamsEvent HostName PortNumber
-- | DisconnectEvent
| RegisterStreamsOpenedHook (Maybe (Maybe OpenStreamsFailureReason -> XMPPT m Bool)) (Maybe OpenStreamsFailureReason -> XMPPT m Bool)
| EnumeratorFirstLevelElement Element
-- | IEEE EnumeratorEvent
| EnumeratorDone
| EnumeratorBeginStream (Maybe String) (Maybe String) (Maybe String) (Maybe String) (Maybe String) (Maybe String)
| EnumeratorEndStream
| EnumeratorException CE.SomeException
-- |
-- The XMPP monad transformer. Contains internal state in order to
-- work with Pontarius. Pontarius clients needs to operate in this
-- context.
newtype XMPPT m a = XMPPT { runXMPPT :: StateT (State m) m a } deriving (Monad, MonadIO)
-- Make XMPPT derive the Monad and MonadIO instances.
deriving instance (Monad m, MonadIO m) => MonadState (State m) (XMPPT m)
-- We need a channel because multiple threads needs to append events,
-- and we need to wait for events when there are none.
data State m = State { evtChan :: Chan (InternalEvent m)
, hookIdGenerator :: IdGenerator
, hooks :: [Hook m] }
data HookId = HookId String deriving (Eq)
data HookPayload m = StreamsOpenedHook (Maybe (Maybe OpenStreamsFailureReason -> XMPPT m Bool)) (Maybe OpenStreamsFailureReason -> XMPPT m Bool)
type Hook m = (HookId, HookPayload m)
-- TODO: Possible ways opening a stream can fail.
data OpenStreamsFailureReason = OpenStreamsFailureReason deriving (Show)
-- data TLSSecureFailureReason = TLSSecureFailureReason
-- data AuthenticateFailureReason = AuthenticateFailureReason
data DisconnectReason = DisconnectReason deriving (Show)
data IQRequestType = Get | Set deriving (Show) data IQRequestType = Get | Set deriving (Show)
@ -428,17 +490,6 @@ type Password = String
type Resource = String type Resource = String
-- An XMLEvent is triggered by an XML stanza or some other XML event, and is
-- sent through the internal event channel, just like client action events.
data EnumeratorEvent = EnumeratorDone |
EnumeratorBeginStream (Maybe String) (Maybe String) (Maybe String) (Maybe String) (Maybe String) (Maybe String) |
EnumeratorEndStream |
EnumeratorFirstLevelElement (Either SomeException Element) |
EnumeratorException CE.SomeException
deriving (Show)
data TimeoutEvent s m = TimeoutEvent StanzaID Timeout (StateT s m ()) data TimeoutEvent s m = TimeoutEvent StanzaID Timeout (StateT s m ())
instance Show (TimeoutEvent s m) where instance Show (TimeoutEvent s m) where

6
pontarius.cabal

@ -23,12 +23,12 @@ Tested-With: GHC ==7.0.4
Library Library
Exposed: True Exposed: True
Build-Depends: base >= 2 && < 5, parsec, enumerator, crypto-api, base64-string, pureMD5, Build-Depends: base >= 2 && < 5, parsec, crypto-api, base64-string, pureMD5,
utf8-string, network, xml-types, text, transformers, utf8-string, network, xml-types, text, transformers,
bytestring, cereal, random, xml-enumerator, bytestring, cereal, random,
tls, tls-extra, containers, mtl, text-icu, tls, tls-extra, containers, mtl, text-icu,
stringprep, asn1-data, cryptohash, time, stringprep, asn1-data, cryptohash, time,
certificate, ranges, uuid certificate, ranges, uuid, conduit, xml-conduit
-- Other-Modules: -- Other-Modules:
-- HS-Source-Dirs: -- HS-Source-Dirs:
-- Extensions: -- Extensions:

Loading…
Cancel
Save