diff options
Diffstat (limited to 'Presence')
-rw-r--r-- | Presence/ControlMaybe.hs | 8 | ||||
-rw-r--r-- | Presence/EventUtil.hs | 17 | ||||
-rw-r--r-- | Presence/Nesting.hs | 4 | ||||
-rw-r--r-- | Presence/XMPPServer.hs | 157 |
4 files changed, 114 insertions, 72 deletions
diff --git a/Presence/ControlMaybe.hs b/Presence/ControlMaybe.hs index 69a38f71..659dab74 100644 --- a/Presence/ControlMaybe.hs +++ b/Presence/ControlMaybe.hs | |||
@@ -4,9 +4,12 @@ module ControlMaybe where | |||
4 | -- import GHC.IO.Exception (IOException(..)) | 4 | -- import GHC.IO.Exception (IOException(..)) |
5 | import Control.Exception as Exception (IOException(..),catch) | 5 | import Control.Exception as Exception (IOException(..),catch) |
6 | 6 | ||
7 | |||
8 | withJust :: Monad m => Maybe x -> (x -> m ()) -> m () | ||
7 | withJust (Just x) f = f x | 9 | withJust (Just x) f = f x |
8 | withJust Nothing f = return () | 10 | withJust Nothing f = return () |
9 | 11 | ||
12 | whenJust :: Monad m => m (Maybe x) -> (x -> m ()) -> m () | ||
10 | whenJust acn f = do | 13 | whenJust acn f = do |
11 | x <- acn | 14 | x <- acn |
12 | withJust x f | 15 | withJust x f |
@@ -18,6 +21,9 @@ catchIO_ a h = Exception.catch a (\(_ :: IOException) -> h) | |||
18 | catchIO :: IO a -> (IOException -> IO a) -> IO a | 21 | catchIO :: IO a -> (IOException -> IO a) -> IO a |
19 | catchIO body handler = Exception.catch body handler | 22 | catchIO body handler = Exception.catch body handler |
20 | 23 | ||
24 | handleIO_ :: IO a -> IO a -> IO a | ||
21 | handleIO_ = flip catchIO_ | 25 | handleIO_ = flip catchIO_ |
22 | handleIO = flip catchIO | ||
23 | 26 | ||
27 | |||
28 | handleIO :: (IOException -> IO a) -> IO a -> IO a | ||
29 | handleIO = flip catchIO | ||
diff --git a/Presence/EventUtil.hs b/Presence/EventUtil.hs index 02a37472..908e09e0 100644 --- a/Presence/EventUtil.hs +++ b/Presence/EventUtil.hs | |||
@@ -4,49 +4,62 @@ module EventUtil where | |||
4 | import Control.Monad | 4 | import Control.Monad |
5 | import Data.XML.Types as XML | 5 | import Data.XML.Types as XML |
6 | import qualified Data.List as List | 6 | import qualified Data.List as List |
7 | import Data.Text (Text) | ||
7 | 8 | ||
8 | -- getStreamName (EventBeginElement name _) = name | 9 | -- getStreamName (EventBeginElement name _) = name |
9 | 10 | ||
11 | isEventBeginElement :: Event -> Bool | ||
10 | isEventBeginElement (EventBeginElement {}) = True | 12 | isEventBeginElement (EventBeginElement {}) = True |
11 | isEventBeginElement _ = False | 13 | isEventBeginElement _ = False |
12 | 14 | ||
15 | isEventEndElement :: Event -> Bool | ||
13 | isEventEndElement (EventEndElement {}) = True | 16 | isEventEndElement (EventEndElement {}) = True |
14 | isEventEndElement _ = False | 17 | isEventEndElement _ = False |
15 | 18 | ||
16 | -- Note: This function ignores name space qualification | 19 | -- Note: This function ignores name space qualification |
20 | elementAttrs :: | ||
21 | MonadPlus m => | ||
22 | Text -> Event -> m [(Name, [Content])] | ||
17 | elementAttrs expected (EventBeginElement name attrs) | 23 | elementAttrs expected (EventBeginElement name attrs) |
18 | | nameLocalName name==expected | 24 | | nameLocalName name==expected |
19 | = return attrs | 25 | = return attrs |
20 | elementAttrs _ _ = mzero | 26 | elementAttrs _ _ = mzero |
21 | 27 | ||
28 | streamP :: Text -> Name | ||
22 | streamP name = Name name (Just "http://etherx.jabber.org/streams") (Just "stream") | 29 | streamP name = Name name (Just "http://etherx.jabber.org/streams") (Just "stream") |
23 | 30 | ||
31 | attr :: Name -> Text -> (Name,[Content]) | ||
24 | attr name value = (name,[ContentText value]) | 32 | attr name value = (name,[ContentText value]) |
25 | 33 | ||
34 | isServerIQOf :: Event -> Text -> Bool | ||
26 | isServerIQOf (EventBeginElement name attrs) testType | 35 | isServerIQOf (EventBeginElement name attrs) testType |
27 | | name=="{jabber:server}iq" | 36 | | name=="{jabber:server}iq" |
28 | && matchAttrib "type" testType attrs | 37 | && matchAttrib "type" testType attrs |
29 | = True | 38 | = True |
30 | isServerIQOf _ _ = False | 39 | isServerIQOf _ _ = False |
31 | 40 | ||
41 | isClientIQOf :: Event -> Text -> Bool | ||
32 | isClientIQOf (EventBeginElement name attrs) testType | 42 | isClientIQOf (EventBeginElement name attrs) testType |
33 | | name=="{jabber:client}iq" | 43 | | name=="{jabber:client}iq" |
34 | && matchAttrib "type" testType attrs | 44 | && matchAttrib "type" testType attrs |
35 | = True | 45 | = True |
36 | isClientIQOf _ _ = False | 46 | isClientIQOf _ _ = False |
37 | 47 | ||
48 | matchAttrib :: Name -> Text -> [(Name, [Content])] -> Bool | ||
38 | matchAttrib name value attrs = | 49 | matchAttrib name value attrs = |
39 | case List.find ( (==name) . fst) attrs of | 50 | case List.find ( (==name) . fst) attrs of |
40 | Just (_,[ContentText x]) | x==value -> True | 51 | Just (_,[ContentText x]) | x==value -> True |
41 | Just (_,[ContentEntity x]) | x==value -> True | 52 | Just (_,[ContentEntity x]) | x==value -> True |
42 | _ -> False | 53 | _ -> False |
43 | 54 | ||
55 | lookupAttrib :: Name -> [(Name, [Content])] -> Maybe Text | ||
44 | lookupAttrib name attrs = | 56 | lookupAttrib name attrs = |
45 | case List.find ( (==name) . fst) attrs of | 57 | case List.find ( (==name) . fst) attrs of |
46 | Just (_,[ContentText x]) -> Just x | 58 | Just (_,[ContentText x]) -> Just x |
47 | Just (_,[ContentEntity x]) -> Just x | 59 | Just (_,[ContentEntity x]) -> Just x |
48 | _ -> Nothing | 60 | _ -> Nothing |
49 | 61 | ||
62 | tagAttrs :: Event -> [(Name, [Content])] | ||
50 | tagAttrs (EventBeginElement _ xs) = xs | 63 | tagAttrs (EventBeginElement _ xs) = xs |
51 | tagAttrs _ = [] | 64 | tagAttrs _ = [] |
52 | 65 | ||
@@ -59,8 +72,12 @@ iqTypeError = "error" | |||
59 | -} | 72 | -} |
60 | 73 | ||
61 | 74 | ||
75 | tagName :: Event -> Name | ||
62 | tagName (EventBeginElement n _) = n | 76 | tagName (EventBeginElement n _) = n |
63 | tagName _ = "" | 77 | tagName _ = "" |
64 | 78 | ||
79 | closerFor :: Event -> Event | ||
65 | closerFor (EventBeginElement n _) = EventEndElement n | 80 | closerFor (EventBeginElement n _) = EventEndElement n |
66 | closerFor _ = error "closerFor: unsupported event" | 81 | closerFor _ = error "closerFor: unsupported event" |
82 | |||
83 | |||
diff --git a/Presence/Nesting.hs b/Presence/Nesting.hs index 850cb8c0..dd0e4113 100644 --- a/Presence/Nesting.hs +++ b/Presence/Nesting.hs | |||
@@ -52,6 +52,7 @@ trackNesting = awaitForever doit | |||
52 | yield xml | 52 | yield xml |
53 | 53 | ||
54 | 54 | ||
55 | lookupLang :: [(Name, [Content])] -> Maybe S.Text | ||
55 | lookupLang attrs = | 56 | lookupLang attrs = |
56 | case List.find ( (=="xml:lang") . fst) attrs of | 57 | case List.find ( (=="xml:lang") . fst) attrs of |
57 | Just (_,[ContentText x]) -> Just x | 58 | Just (_,[ContentText x]) -> Just x |
@@ -67,6 +68,9 @@ awaitCloser lvl = do | |||
67 | xml <- await | 68 | xml <- await |
68 | maybe (return ()) (const loop) xml | 69 | maybe (return ()) (const loop) xml |
69 | 70 | ||
71 | withXML :: | ||
72 | Monad m => | ||
73 | (i -> ConduitM i o m ()) -> ConduitM i o m () | ||
70 | withXML f = await >>= maybe (return ()) f | 74 | withXML f = await >>= maybe (return ()) f |
71 | 75 | ||
72 | nextElement :: Monad m => NestingXML o m (Maybe Event) | 76 | nextElement :: Monad m => NestingXML o m (Maybe Event) |
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index 7eb0fbc5..d41e06cb 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -1,5 +1,6 @@ | |||
1 | {-# LANGUAGE CPP #-} | 1 | {-# LANGUAGE CPP #-} |
2 | {-# LANGUAGE OverloadedStrings #-} | 2 | {-# LANGUAGE OverloadedStrings #-} |
3 | {-# LANGUAGE RankNTypes #-} | ||
3 | module XMPPServer | 4 | module XMPPServer |
4 | ( xmppServer | 5 | ( xmppServer |
5 | , ConnectionKey(..) | 6 | , ConnectionKey(..) |
@@ -18,6 +19,7 @@ module XMPPServer | |||
18 | , sendModifiedStanzaToPeer | 19 | , sendModifiedStanzaToPeer |
19 | , sendModifiedStanzaToClient | 20 | , sendModifiedStanzaToClient |
20 | ) where | 21 | ) where |
22 | |||
21 | import Debug.Trace | 23 | import Debug.Trace |
22 | import Control.Monad.Trans.Resource (runResourceT) | 24 | import Control.Monad.Trans.Resource (runResourceT) |
23 | import Control.Monad.Trans (lift) | 25 | import Control.Monad.Trans (lift) |
@@ -28,7 +30,6 @@ import Control.Concurrent (forkIO) | |||
28 | import Control.Concurrent.STM | 30 | import Control.Concurrent.STM |
29 | -- import Control.Concurrent.STM.TChan | 31 | -- import Control.Concurrent.STM.TChan |
30 | import Network.Socket | 32 | import Network.Socket |
31 | import XMPPTypes (withPort) | ||
32 | import Text.Printf | 33 | import Text.Printf |
33 | import System.Posix.Signals | 34 | import System.Posix.Signals |
34 | import Data.ByteString (ByteString) | 35 | import Data.ByteString (ByteString) |
@@ -55,17 +56,26 @@ import Data.Set (Set, (\\) ) | |||
55 | import qualified Data.Set as Set | 56 | import qualified Data.Set as Set |
56 | import qualified System.Random | 57 | import qualified System.Random |
57 | import qualified Network.BSD as BSD | 58 | import qualified Network.BSD as BSD |
59 | import Data.Void (Void) | ||
58 | 60 | ||
59 | import GetHostByAddr (getHostByAddr) | 61 | import GetHostByAddr (getHostByAddr) |
60 | import qualified Control.Concurrent.STM.UpdateStream as Slotted | 62 | import qualified Control.Concurrent.STM.UpdateStream as Slotted |
61 | import ControlMaybe | ||
62 | import Nesting | 63 | import Nesting |
63 | import EventUtil | ||
64 | import Server | 64 | import Server |
65 | import EventUtil | ||
66 | import ControlMaybe | ||
65 | 67 | ||
68 | withPort :: SockAddr -> Int -> SockAddr | ||
69 | withPort (SockAddrInet _ a) port = SockAddrInet (toEnum port) a | ||
70 | withPort (SockAddrInet6 _ a b c) port = SockAddrInet6 (toEnum port) a b c | ||
71 | |||
72 | |||
73 | peerport :: PortNumber | ||
66 | peerport = 5269 | 74 | peerport = 5269 |
75 | clientport :: PortNumber | ||
67 | clientport = 5222 | 76 | clientport = 5222 |
68 | 77 | ||
78 | my_uuid :: Text | ||
69 | my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" | 79 | my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" |
70 | 80 | ||
71 | data ConnectionKey | 81 | data ConnectionKey |
@@ -164,6 +174,7 @@ data XMPPServerParameters = | |||
164 | , xmppInformClientPresence :: ConnectionKey -> Stanza -> IO () | 174 | , xmppInformClientPresence :: ConnectionKey -> Stanza -> IO () |
165 | } | 175 | } |
166 | 176 | ||
177 | |||
167 | -- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error | 178 | -- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error |
168 | -- client connection | 179 | -- client connection |
169 | -- socat script to send stanza fragment | 180 | -- socat script to send stanza fragment |
@@ -252,6 +263,7 @@ copyToChannel f chan closer_stack = awaitForever copy | |||
252 | yield x | 263 | yield x |
253 | 264 | ||
254 | 265 | ||
266 | prettyPrint :: ByteString -> ConduitM Event Void IO () | ||
255 | prettyPrint prefix = | 267 | prettyPrint prefix = |
256 | XML.renderBytes (XML.def { XML.rsPretty=True }) | 268 | XML.renderBytes (XML.def { XML.rsPretty=True }) |
257 | =$= CB.lines | 269 | =$= CB.lines |
@@ -279,6 +291,44 @@ fixHeaders Stanza { stanzaTo=mto, stanzaFrom=mfrom } = do | |||
279 | as' = maybe as (\to->attr "to" to:as) mto | 291 | as' = maybe as (\to->attr "to" to:as) mto |
280 | as'' = maybe as' (\from->attr "from" from:as') mfrom | 292 | as'' = maybe as' (\from->attr "from" from:as') mfrom |
281 | 293 | ||
294 | conduitToChan | ||
295 | :: Conduit () IO Event | ||
296 | -> IO (TChan Event, TVar (Maybe [Event]), TMVar a) | ||
297 | conduitToChan c = do | ||
298 | chan <- atomically newTChan | ||
299 | clsrs <- atomically $ newTVar (Just []) | ||
300 | quitvar <- atomically $ newEmptyTMVar | ||
301 | forkIO $ do | ||
302 | c =$= copyToChannel id chan clsrs $$ awaitForever (const $ return ()) | ||
303 | atomically $ writeTVar clsrs Nothing | ||
304 | return (chan,clsrs,quitvar) | ||
305 | |||
306 | ioWriteChan :: MonadIO m => TChan a -> a -> m () | ||
307 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | ||
308 | |||
309 | stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m () | ||
310 | stanzaToConduit stanza = do | ||
311 | let xchan = stanzaChan stanza | ||
312 | xfin = stanzaClosers stanza | ||
313 | rdone = stanzaInterrupt stanza | ||
314 | loop = return () | ||
315 | fix $ \inner -> do | ||
316 | what <- liftIO . atomically $ foldr1 orElse | ||
317 | [readTChan xchan >>= \xml -> return $ do | ||
318 | yield xml -- atomically $ Slotted.push slots Nothing xml | ||
319 | inner | ||
320 | ,do mb <- readTVar xfin | ||
321 | cempty <- isEmptyTChan xchan | ||
322 | if isNothing mb | ||
323 | then if cempty then return loop else retry | ||
324 | else do done <- tryReadTMVar rdone | ||
325 | check (isJust done) | ||
326 | trace "todo: send closers" retry | ||
327 | ,do isEmptyTChan xchan >>= check | ||
328 | readTMVar rdone | ||
329 | return (return ())] | ||
330 | what | ||
331 | |||
282 | 332 | ||
283 | sendModifiedStanzaToPeer :: Stanza -> TChan Stanza -> IO () | 333 | sendModifiedStanzaToPeer :: Stanza -> TChan Stanza -> IO () |
284 | sendModifiedStanzaToPeer stanza chan = do | 334 | sendModifiedStanzaToPeer stanza chan = do |
@@ -374,9 +424,19 @@ C->Unrecognized xmlns="jabber:client"> | |||
374 | C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> | 424 | C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> |
375 | C->Unrecognized </iq> | 425 | C->Unrecognized </iq> |
376 | -} | 426 | -} |
427 | chanContents :: TChan x -> IO [x] | ||
428 | chanContents ch = do | ||
429 | x <- atomically $ do | ||
430 | bempty <- isEmptyTChan ch | ||
431 | if bempty | ||
432 | then return Nothing | ||
433 | else fmap Just $ readTChan ch | ||
434 | maybe (return []) | ||
435 | (\x -> do | ||
436 | xs <- chanContents ch | ||
437 | return (x:xs)) | ||
438 | x | ||
377 | 439 | ||
378 | ioWriteChan :: MonadIO m => TChan a -> a -> m () | ||
379 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | ||
380 | 440 | ||
381 | parsePresenceStatus | 441 | parsePresenceStatus |
382 | :: ( MonadThrow m | 442 | :: ( MonadThrow m |
@@ -499,7 +559,7 @@ grokMessage ns stanzaTag = do | |||
499 | 559 | ||
500 | grokStanza | 560 | grokStanza |
501 | :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType) | 561 | :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType) |
502 | ggrokStanza "jabber:server" stanzaTag = | 562 | grokStanza "jabber:server" stanzaTag = |
503 | case () of | 563 | case () of |
504 | _ | stanzaTag `isServerIQOf` "get" -> grokStanzaIQGet stanzaTag | 564 | _ | stanzaTag `isServerIQOf` "get" -> grokStanzaIQGet stanzaTag |
505 | _ | stanzaTag `isServerIQOf` "result" -> grokStanzaIQResult stanzaTag | 565 | _ | stanzaTag `isServerIQOf` "result" -> grokStanzaIQResult stanzaTag |
@@ -516,6 +576,24 @@ grokStanza "jabber:client" stanzaTag = | |||
516 | _ | tagName stanzaTag == "{jabber:client}message" -> grokMessage "jabber:client" stanzaTag | 576 | _ | tagName stanzaTag == "{jabber:client}message" -> grokMessage "jabber:client" stanzaTag |
517 | _ -> return $ Just Unrecognized | 577 | _ -> return $ Just Unrecognized |
518 | 578 | ||
579 | mkname :: Text -> Text -> XML.Name | ||
580 | mkname namespace name = (Name name (Just namespace) Nothing) | ||
581 | |||
582 | makePong :: Text -> Maybe Text -> Text -> Text -> [XML.Event] | ||
583 | makePong namespace mid to from = | ||
584 | -- Note: similar to session reply | ||
585 | [ EventBeginElement (mkname namespace "iq") | ||
586 | $(case mid of | ||
587 | Just c -> (("id",[ContentText c]):) | ||
588 | _ -> id) | ||
589 | [ attr "type" "result" | ||
590 | , attr "to" to | ||
591 | , attr "from" from | ||
592 | ] | ||
593 | , EventEndElement (mkname namespace "iq") | ||
594 | ] | ||
595 | |||
596 | |||
519 | xmppInbound :: Server ConnectionKey SockAddr | 597 | xmppInbound :: Server ConnectionKey SockAddr |
520 | -> XMPPServerParameters | 598 | -> XMPPServerParameters |
521 | -> ConnectionKey | 599 | -> ConnectionKey |
@@ -609,19 +687,6 @@ xmppInbound sv xmpp k laddr pingflag src stanzas output donevar = doNestingXML $ | |||
609 | loop | 687 | loop |
610 | 688 | ||
611 | 689 | ||
612 | chanContents :: TChan x -> IO [x] | ||
613 | chanContents ch = do | ||
614 | x <- atomically $ do | ||
615 | bempty <- isEmptyTChan ch | ||
616 | if bempty | ||
617 | then return Nothing | ||
618 | else fmap Just $ readTChan ch | ||
619 | maybe (return []) | ||
620 | (\x -> do | ||
621 | xs <- chanContents ch | ||
622 | return (x:xs)) | ||
623 | x | ||
624 | |||
625 | while :: IO Bool -> IO a -> IO [a] | 690 | while :: IO Bool -> IO a -> IO [a] |
626 | while cond body = do | 691 | while cond body = do |
627 | b <- cond | 692 | b <- cond |
@@ -639,6 +704,7 @@ readUntilNothing ch = do | |||
639 | return (x:xs)) | 704 | return (x:xs)) |
640 | x | 705 | x |
641 | 706 | ||
707 | |||
642 | streamFeatures :: Text -> [XML.Event] | 708 | streamFeatures :: Text -> [XML.Event] |
643 | streamFeatures "jabber:client" = | 709 | streamFeatures "jabber:client" = |
644 | [ EventBeginElement (streamP "features") [] | 710 | [ EventBeginElement (streamP "features") [] |
@@ -680,9 +746,6 @@ data XMPPState | |||
680 | = PingSlot | 746 | = PingSlot |
681 | deriving (Eq,Ord) | 747 | deriving (Eq,Ord) |
682 | 748 | ||
683 | mkname :: Text -> Text -> XML.Name | ||
684 | mkname namespace name = (Name name (Just namespace) Nothing) | ||
685 | |||
686 | makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event] | 749 | makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event] |
687 | makePing namespace mid to from = | 750 | makePing namespace mid to from = |
688 | [ EventBeginElement (mkname namespace "iq") | 751 | [ EventBeginElement (mkname namespace "iq") |
@@ -697,20 +760,6 @@ makePing namespace mid to from = | |||
697 | , EventEndElement "{urn:xmpp:ping}ping" | 760 | , EventEndElement "{urn:xmpp:ping}ping" |
698 | , EventEndElement $ mkname namespace "iq"] | 761 | , EventEndElement $ mkname namespace "iq"] |
699 | 762 | ||
700 | makePong :: Text -> Maybe Text -> Text -> Text -> [XML.Event] | ||
701 | makePong namespace mid to from = | ||
702 | -- Note: similar to session reply | ||
703 | [ EventBeginElement (mkname namespace "iq") | ||
704 | $(case mid of | ||
705 | Just c -> (("id",[ContentText c]):) | ||
706 | _ -> id) | ||
707 | [ attr "type" "result" | ||
708 | , attr "to" to | ||
709 | , attr "from" from | ||
710 | ] | ||
711 | , EventEndElement (mkname namespace "iq") | ||
712 | ] | ||
713 | |||
714 | iq_bind_reply :: Maybe Text -> Text -> [XML.Event] | 763 | iq_bind_reply :: Maybe Text -> Text -> [XML.Event] |
715 | iq_bind_reply mid jid = | 764 | iq_bind_reply mid jid = |
716 | [ EventBeginElement "{jabber:client}iq" (consid mid [("type",[ContentText "result"])]) | 765 | [ EventBeginElement "{jabber:client}iq" (consid mid [("type",[ContentText "result"])]) |
@@ -944,29 +993,6 @@ clientKey (sock,addr) = do | |||
944 | paddr <- getPeerName sock | 993 | paddr <- getPeerName sock |
945 | return $ (ClientKey addr,paddr) | 994 | return $ (ClientKey addr,paddr) |
946 | 995 | ||
947 | stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m () | ||
948 | stanzaToConduit stanza = do | ||
949 | let xchan = stanzaChan stanza | ||
950 | xfin = stanzaClosers stanza | ||
951 | rdone = stanzaInterrupt stanza | ||
952 | loop = return () | ||
953 | fix $ \inner -> do | ||
954 | what <- liftIO . atomically $ foldr1 orElse | ||
955 | [readTChan xchan >>= \xml -> return $ do | ||
956 | yield xml -- atomically $ Slotted.push slots Nothing xml | ||
957 | inner | ||
958 | ,do mb <- readTVar xfin | ||
959 | cempty <- isEmptyTChan xchan | ||
960 | if isNothing mb | ||
961 | then if cempty then return loop else retry | ||
962 | else do done <- tryReadTMVar rdone | ||
963 | check (isJust done) | ||
964 | trace "todo: send closers" retry | ||
965 | ,do isEmptyTChan xchan >>= check | ||
966 | readTMVar rdone | ||
967 | return (return ())] | ||
968 | what | ||
969 | |||
970 | xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m () | 996 | xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m () |
971 | xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set) | 997 | xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set) |
972 | where | 998 | where |
@@ -978,18 +1004,6 @@ xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set) | |||
978 | else [] ) | 1004 | else [] ) |
979 | yield $ EventEndElement "{jabber:iq:roster}item" | 1005 | yield $ EventEndElement "{jabber:iq:roster}item" |
980 | 1006 | ||
981 | conduitToChan | ||
982 | :: Conduit () IO Event | ||
983 | -> IO (TChan Event, TVar (Maybe [Event]), TMVar a) | ||
984 | conduitToChan c = do | ||
985 | chan <- atomically newTChan | ||
986 | clsrs <- atomically $ newTVar (Just []) | ||
987 | quitvar <- atomically $ newEmptyTMVar | ||
988 | forkIO $ do | ||
989 | c =$= copyToChannel id chan clsrs $$ awaitForever (const $ return ()) | ||
990 | atomically $ writeTVar clsrs Nothing | ||
991 | return (chan,clsrs,quitvar) | ||
992 | |||
993 | sendRoster :: | 1007 | sendRoster :: |
994 | StanzaWrap a | 1008 | StanzaWrap a |
995 | -> XMPPServerParameters | 1009 | -> XMPPServerParameters |
@@ -1190,3 +1204,4 @@ cloneTChan chan = do | |||
1190 | xs <- chanContents' chan | 1204 | xs <- chanContents' chan |
1191 | return (x:xs) | 1205 | return (x:xs) |
1192 | #endif | 1206 | #endif |
1207 | |||