summaryrefslogtreecommitdiff
path: root/Presence
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2018-11-13 16:53:24 -0500
committerJoe Crayne <joe@jerkface.net>2018-11-14 01:29:41 -0500
commit971b23b40e2b519107923dcb6976145e2b83b9cf (patch)
tree733086bcca436b4726e0507738638f4d06a77b86 /Presence
parentc2cce27bc86c5aefccc5e2afa9b2063e8c915336 (diff)
MUC: Two-step nominate/comit chat transactions.
Diffstat (limited to 'Presence')
-rw-r--r--Presence/Chat.hs120
-rw-r--r--Presence/MUC.hs61
-rw-r--r--Presence/Presence.hs10
-rw-r--r--Presence/Stanza/Build.hs7
-rw-r--r--Presence/Util.hs3
-rw-r--r--Presence/XMPPServer.hs62
6 files changed, 231 insertions, 32 deletions
diff --git a/Presence/Chat.hs b/Presence/Chat.hs
index 47512bf1..172c5242 100644
--- a/Presence/Chat.hs
+++ b/Presence/Chat.hs
@@ -1,12 +1,16 @@
1{-# LANGUAGE LambdaCase #-}
1module Chat where 2module Chat where
2 3
4import Debug.Trace
3import Control.Concurrent.STM 5import Control.Concurrent.STM
4import Control.Monad 6import Control.Monad
5import qualified Data.Map as Map 7import qualified Data.Map as Map
6 ;import Data.Map (Map) 8 ;import Data.Map (Map)
7import Data.Text 9import Data.Text (Text)
8import Data.Word 10import Data.Word
9 11
12import Util (stripResource)
13
10-- To join a chat room, add a 'ClientRoomLink' to 'roomDesiredLink' with 14-- To join a chat room, add a 'ClientRoomLink' to 'roomDesiredLink' with
11-- 'desireStreamEvent' set to 'Nothing'. 15-- 'desireStreamEvent' set to 'Nothing'.
12-- 16--
@@ -16,9 +20,20 @@ data Room k = Room
16 { roomDesiredTransaction :: TVar (Map k (TVar (Maybe ChatTransaction))) 20 { roomDesiredTransaction :: TVar (Map k (TVar (Maybe ChatTransaction)))
17 , roomChan :: TChan ChatTransaction 21 , roomChan :: TChan ChatTransaction
18 , roomFutureSeqNo :: TVar Word64 22 , roomFutureSeqNo :: TVar Word64
23 , roomMembers :: TVar (Map Text{-nick-} (PerMember k))
24 , roomAffiliations :: TVar (Map Text{-jid-} Affiliation)
25 , roomReservations :: TVar (Map Text{-nick-} Text{-jid-})
26 }
27
28newtype PerMember k = PerMember
29 { memberKey :: k
30 }
31
32newtype Affiliation = Affiliation
33 { reservedNick :: Text
19 } 34 }
20 35
21data ChatEvent = Join | Part | Action Text | Talk Text | NickChange Text 36data ChatEvent = Join | Part | Action Text | Talk Text -- | NickChange Text
22 deriving (Eq,Ord,Show) 37 deriving (Eq,Ord,Show)
23 38
24data Membership = Outside | Inside 39data Membership = Outside | Inside
@@ -62,6 +77,7 @@ newtype RoomHandle = RH (TVar (Maybe ChatTransaction))
62data JoinedRoom k = JoinedRoom 77data JoinedRoom k = JoinedRoom
63 { joinedRoom :: Room k 78 { joinedRoom :: Room k
64 , roomHandle :: RoomHandle 79 , roomHandle :: RoomHandle
80 , roomTransactions :: TChan ChatTransaction
65 } 81 }
66 82
67newRoom :: STM (Room k) 83newRoom :: STM (Room k)
@@ -69,10 +85,16 @@ newRoom = do
69 m <- newTVar Map.empty 85 m <- newTVar Map.empty
70 c <- newTChan -- newBroadcastTChan 86 c <- newTChan -- newBroadcastTChan
71 n <- newTVar 0 87 n <- newTVar 0
88 cs <- newTVar Map.empty
89 as <- newTVar Map.empty
90 rs <- newTVar Map.empty
72 return Room 91 return Room
73 { roomDesiredTransaction = m 92 { roomDesiredTransaction = m
74 , roomChan = c 93 , roomChan = c
75 , roomFutureSeqNo = n 94 , roomFutureSeqNo = n
95 , roomMembers = cs
96 , roomAffiliations = as
97 , roomReservations = rs
76 } 98 }
77 99
78 100
@@ -86,18 +108,18 @@ joinRoom :: Ord k => k
86joinRoom k room jid nick = do 108joinRoom k room jid nick = do
87 no <- readTVar $ roomFutureSeqNo room 109 no <- readTVar $ roomFutureSeqNo room
88 v <- newTVar (Just $ ChatTransaction no jid nick [Join]) 110 v <- newTVar (Just $ ChatTransaction no jid nick [Join])
89 modifyTVar' (roomDesiredTransaction room) 111 modifyTVar' (roomDesiredTransaction room) $ Map.insert k v
90 $ Map.insert k v 112 c <- dupTChan (roomChan room)
91 return $ JoinedRoom room (RH v) 113 return $ JoinedRoom room (RH v) c
92 114
93partRoom :: JoinedRoom k -> Maybe Text -> Text -> STM () 115partRoom :: JoinedRoom k -> Maybe Text -> Text -> STM ()
94partRoom (JoinedRoom room (RH v)) jid nick = do 116partRoom (JoinedRoom room (RH v) c) jid nick = do
95 writeTVar v Nothing -- Cancel pending chat. 117 writeTVar v Nothing -- Cancel pending chat.
96 sendChat (JoinedRoom room (RH v)) jid nick [Part] 118 sendChat (JoinedRoom room (RH v) c) jid nick [Part]
97 return () 119 return ()
98 120
99sendChat :: JoinedRoom k -> Maybe Text -> Text -> [ChatEvent] -> STM Bool 121sendChat :: JoinedRoom k -> Maybe Text -> Text -> [ChatEvent] -> STM Bool
100sendChat (JoinedRoom room (RH v)) jid nick chat = do 122sendChat (JoinedRoom room (RH v) _) jid nick chat = do
101 mpending <- readTVar v 123 mpending <- readTVar v
102 case mpending of 124 case mpending of
103 Nothing -> do 125 Nothing -> do
@@ -110,9 +132,9 @@ sendChat (JoinedRoom room (RH v)) jid nick chat = do
110-- | Blocks until a transaction occurs. Optionally, a failed transaction will 132-- | Blocks until a transaction occurs. Optionally, a failed transaction will
111-- be automatically renewed. 133-- be automatically renewed.
112readRoom :: Ord k => k -> JoinedRoom k -> STM (Bool, ChatTransaction) 134readRoom :: Ord k => k -> JoinedRoom k -> STM (Bool, ChatTransaction)
113readRoom k (JoinedRoom room (RH v)) = do 135readRoom k (JoinedRoom room (RH v) c) = do
114 mpending <- readTVar v 136 mpending <- readTVar v
115 final <- readTChan $ roomChan room 137 final <- readTChan c
116 case mpending of 138 case mpending of
117 Just pending -> do 139 Just pending -> do
118 if pending == final 140 if pending == final
@@ -128,9 +150,75 @@ readRoom k (JoinedRoom room (RH v)) = do
128 return (False,final) 150 return (False,final)
129 Nothing -> return (False,final) 151 Nothing -> return (False,final)
130 152
153roomOccupants :: Room k-> STM [(Text{-nick-},Maybe Text{-friendly name-})]
154roomOccupants room = do
155 ns <- Map.keys <$> readTVar (roomMembers room)
156 return $ map (\n -> (n,Just n)) ns
157
158roomReservedNick :: Room k -> Text{-JID-} -> STM (Maybe Text{-nick-})
159roomReservedNick room jid = do
160 a <- Map.lookup jid <$> readTVar (roomAffiliations room)
161 return $ reservedNick <$> a
162
163roomFriendlyName :: Room k -> STM (Maybe Text)
164roomFriendlyName _ = return Nothing
165
131-- Room implementation interface 166-- Room implementation interface
132 167
133roomCommit :: Room k -> ChatTransaction -> STM () 168data Validation = Malformed | Requires Membership | Denied | Valid Membership Membership
134roomCommit room t = do 169 deriving (Eq,Ord,Show,Read)
135 modifyTVar' (roomFutureSeqNo room) succ 170
136 writeTChan (roomChan room) t 171validateTransaction :: Ord k => Room k -> k -> ChatTransaction -> STM Validation
172validateTransaction room k t@(ChatTransaction no mjid nick xs)
173 | null xs = return Malformed
174 | otherwise = case membershipEffect xs of
175 MembershipEffect Inside what ->
176 Map.lookup nick <$> readTVar (roomMembers room) >>= \case
177 Nothing -> return (Requires Inside)
178 Just p | memberKey p /= k -> return Denied
179 _ -> return (Valid Inside what)
180 MembershipEffect Outside what -> do
181 Map.lookup k <$> return Map.empty {- readTVar (roomDesiredTransaction room) -} >>= \case
182 Nothing -> Map.lookup nick <$> readTVar (roomMembers room) >>= \case
183 Nothing -> Map.lookup nick <$> readTVar (roomReservations room) >>= \case
184 Just rjid | Just jid <- mjid
185 , stripResource jid == rjid
186 -> return (Valid Outside what)
187 Just _ -> return Denied
188 Nothing -> return (Valid Outside what)
189 Just _ -> return Denied -- Nick already taken.
190 Just _ -> return (Requires Outside)
191 _ -> return Malformed
192
193
194roomCommit :: Ord k => Room k -> k -> ChatTransaction -> STM ()
195roomCommit room k t = do
196 let fin = do
197 trace "increment seqno!" $ return ()
198 modifyTVar' (roomFutureSeqNo room) succ
199 writeTChan (roomChan room) t
200 v <- validateTransaction room k t
201 trace ("roomCommit " ++ show v) $ return ()
202 case v of
203 Valid Outside Inside -> do
204 modifyTVar' (roomMembers room) $ Map.insert (chatSender t) PerMember
205 { memberKey = k
206 }
207 fin
208 Valid Inside Outside -> do
209 modifyTVar' (roomMembers room) $ Map.delete (chatSender t)
210 fin
211 Valid _ _ -> fin
212 _ -> return ()
213
214roomPending :: Ord k => Room k -> STM (Map k ChatTransaction)
215roomPending room = do
216 no <- readTVar $ roomFutureSeqNo room
217 m <- Map.mapMaybe (>>= \t -> do guard (chatSeqNo t == no)
218 return t)
219 <$> do readTVar (roomDesiredTransaction room)
220 >>= mapM readTVar
221 fmap (Map.mapMaybe id)
222 $ sequence $ Map.mapWithKey (\k t -> validateTransaction room k t >>= \case
223 Valid _ _ -> return (Just t)
224 _ -> return Nothing) m
diff --git a/Presence/MUC.hs b/Presence/MUC.hs
new file mode 100644
index 00000000..76c53391
--- /dev/null
+++ b/Presence/MUC.hs
@@ -0,0 +1,61 @@
1module MUC where
2
3import Control.Monad
4import Control.Concurrent.STM
5
6import qualified Data.Map.Strict as Map
7 ;import Data.Map.Strict (Map)
8
9import Chat
10import ConnectionKey
11import Data.Text (Text)
12
13data MUC = MUC
14 { mucRooms :: TVar (Map Text (Room ClientAddress))
15 , mucChan :: TChan MUCEvent
16 }
17
18data MUCEvent = MUCCreate Text{-room-} Text{-JID-} Text{-nick-} (Room ClientAddress)
19
20
21newMUC :: STM MUC
22newMUC = MUC <$> newTVar Map.empty <*> newBroadcastTChan
23
24mucRoomList :: MUC -> IO [(Text{-room-},Maybe Text{-friendly room name-})]
25mucRoomList muc = atomically $ do
26 rs <- Map.toList <$> readTVar (mucRooms muc)
27 forM rs $ \(rkey,r) -> do
28 fn <- roomFriendlyName r
29 return (rkey,fn)
30
31mucRoomOccupants :: MUC -> Text{-room-} -> IO [(Text{-nick-},Maybe Text{-friendly name-})]
32mucRoomOccupants muc rkey = atomically $ do
33 mr <- Map.lookup rkey <$> readTVar (mucRooms muc)
34 case mr of
35 Nothing -> return []
36 Just r -> roomOccupants r
37
38mucReservedNick :: MUC -> Text{-room-} -> IO (Maybe (Text{-JID-} -> IO (Maybe Text)))
39mucReservedNick muc rkey = atomically $ do
40 mr <- Map.lookup rkey <$> readTVar (mucRooms muc)
41 case mr of
42 Nothing -> return Nothing
43 Just r -> return $ Just $ \jid -> atomically $ roomReservedNick r jid
44
45mucJoinRoom :: MUC -> Text{-JID-} -> Text{-nick-} -> Text{-room-} -> ClientAddress -> IO (JoinedRoom ClientAddress)
46mucJoinRoom muc jid nick rkey k = atomically $ do
47 mr <- Map.lookup rkey <$> readTVar (mucRooms muc)
48 case mr of
49 Nothing -> do
50 -- create room.
51 r <- newRoom
52 v <- joinRoom k r (Just jid) nick
53 modifyTVar' (mucRooms muc) $ Map.insert rkey r
54 writeTChan (mucChan muc) $ MUCCreate rkey jid nick r
55 return v
56 Just r -> do
57 -- join room.
58 v <- joinRoom k r (Just jid) nick
59 return v
60
61
diff --git a/Presence/Presence.hs b/Presence/Presence.hs
index 0a73aced..f8a18388 100644
--- a/Presence/Presence.hs
+++ b/Presence/Presence.hs
@@ -144,10 +144,12 @@ nameForClient state k = do
144 "." -> textHostName 144 "." -> textHostName
145 profile -> return profile 145 profile -> return profile
146 146
147presenceHooks :: PresenceState stat -> Int -> Maybe SockAddr -- ^ client-to-server bind address 147presenceHooks :: PresenceState stat -> Map Text MUC
148 -> Int
149 -> Maybe SockAddr -- ^ client-to-server bind address
148 -> Maybe SockAddr -- ^ server-to-server bind address 150 -> Maybe SockAddr -- ^ server-to-server bind address
149 -> XMPPServerParameters 151 -> XMPPServerParameters
150presenceHooks state verbosity mclient mpeer = XMPPServerParameters 152presenceHooks state chats verbosity mclient mpeer = XMPPServerParameters
151 { xmppChooseResourceName = chooseResourceName state 153 { xmppChooseResourceName = chooseResourceName state
152 , xmppTellClientHisName = tellClientHisName state 154 , xmppTellClientHisName = tellClientHisName state
153 , xmppTellMyNameToClient = nameForClient state 155 , xmppTellMyNameToClient = nameForClient state
@@ -169,7 +171,7 @@ presenceHooks state verbosity mclient mpeer = XMPPServerParameters
169 , xmppClientInformSubscription = clientInformSubscription state 171 , xmppClientInformSubscription = clientInformSubscription state
170 , xmppPeerInformSubscription = peerInformSubscription state 172 , xmppPeerInformSubscription = peerInformSubscription state
171 , xmppVerbosity = return verbosity 173 , xmppVerbosity = return verbosity
172 , xmppGroupChat = Map.singleton "chat" MUC 174 , xmppGroupChat = chats {- Map.singleton "chat" chat
173 { mucRoomList = return [("testroom",Just "testroom")] 175 { mucRoomList = return [("testroom",Just "testroom")]
174 , mucRoomOccupants = \case 176 , mucRoomOccupants = \case
175 "testroom" -> return [("fakeperson",Nothing)] 177 "testroom" -> return [("fakeperson",Nothing)]
@@ -183,7 +185,7 @@ presenceHooks state verbosity mclient mpeer = XMPPServerParameters
183 ++ " with nick: " ++ Text.unpack nick 185 ++ " with nick: " ++ Text.unpack nick
184 -- TODO: broadcast presence to all participants. 186 -- TODO: broadcast presence to all participants.
185 -- See 7.2.3 of XEP-0045 187 -- See 7.2.3 of XEP-0045
186 } 188 -}
187 , xmppClientBind = mclient 189 , xmppClientBind = mclient
188 , xmppPeerBind = mpeer 190 , xmppPeerBind = mpeer
189 } 191 }
diff --git a/Presence/Stanza/Build.hs b/Presence/Stanza/Build.hs
index 5c4d371a..e02684f5 100644
--- a/Presence/Stanza/Build.hs
+++ b/Presence/Stanza/Build.hs
@@ -48,7 +48,10 @@ makeInformSubscription namespace from to approved =
48 , EventEndElement (mkname namespace "presence")] 48 , EventEndElement (mkname namespace "presence")]
49 49
50makePresenceStanza :: Text -> Maybe Text -> JabberShow -> IO Stanza 50makePresenceStanza :: Text -> Maybe Text -> JabberShow -> IO Stanza
51makePresenceStanza namespace mjid pstat = do 51makePresenceStanza ns mjid pstat = makePresenceStanzaEx ns mjid pstat []
52
53makePresenceStanzaEx :: Text -> Maybe Text -> JabberShow -> [XML.Event]-> IO Stanza
54makePresenceStanzaEx namespace mjid pstat es = do
52 stanzaFromList PresenceStatus { presenceShow = pstat 55 stanzaFromList PresenceStatus { presenceShow = pstat
53 , presencePriority = Nothing 56 , presencePriority = Nothing
54 , presenceStatus = [] 57 , presenceStatus = []
@@ -56,7 +59,7 @@ makePresenceStanza namespace mjid pstat = do
56 } 59 }
57 $ [ EventBeginElement (mkname namespace "presence") 60 $ [ EventBeginElement (mkname namespace "presence")
58 (setFrom $ typ pstat) ] 61 (setFrom $ typ pstat) ]
59 ++ (shw pstat >>= jabberShow) ++ 62 ++ (shw pstat >>= jabberShow) ++ es ++
60 [ EventEndElement (mkname namespace "presence")] 63 [ EventEndElement (mkname namespace "presence")]
61 where 64 where
62 setFrom = maybe id 65 setFrom = maybe id
diff --git a/Presence/Util.hs b/Presence/Util.hs
index ef98d415..e19b35fd 100644
--- a/Presence/Util.hs
+++ b/Presence/Util.hs
@@ -14,6 +14,9 @@ import Network.Address (setPort)
14type UserName = Text 14type UserName = Text
15type ResourceName = Text 15type ResourceName = Text
16 16
17stripResource :: Text -> Text
18stripResource jid = let (n,h,_) = splitJID jid
19 in unsplitJID (n,h,Nothing)
17 20
18unsplitJID :: (Maybe UserName,Text,Maybe ResourceName) -> Text 21unsplitJID :: (Maybe UserName,Text,Maybe ResourceName) -> Text
19unsplitJID (n,h,r) = username <> h <> resource 22unsplitJID (n,h,r) = username <> h <> resource
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs
index 3a255cdd..912bbf0b 100644
--- a/Presence/XMPPServer.hs
+++ b/Presence/XMPPServer.hs
@@ -2,6 +2,7 @@
2{-# LANGUAGE DoAndIfThenElse #-} 2{-# LANGUAGE DoAndIfThenElse #-}
3{-# LANGUAGE ExistentialQuantification #-} 3{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE FlexibleInstances #-} 4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE MultiWayIf #-} 6{-# LANGUAGE MultiWayIf #-}
6{-# LANGUAGE OverloadedStrings #-} 7{-# LANGUAGE OverloadedStrings #-}
7{-# LANGUAGE RankNTypes #-} 8{-# LANGUAGE RankNTypes #-}
@@ -71,6 +72,7 @@ import Control.Concurrent.Lifted (forkIO,myThreadId,ThreadId)
71import GHC.Conc (labelThread) 72import GHC.Conc (labelThread)
72#endif 73#endif
73import Control.Concurrent.STM 74import Control.Concurrent.STM
75import Data.List hiding ((\\))
74-- import Control.Concurrent.STM.TChan 76-- import Control.Concurrent.STM.TChan
75import Network.SocketLike 77import Network.SocketLike
76import Text.Printf 78import Text.Printf
@@ -108,6 +110,8 @@ import DebugTag
108import Stanza.Build 110import Stanza.Build
109import Stanza.Parse 111import Stanza.Parse
110import Stanza.Types 112import Stanza.Types
113import MUC
114import Chat
111 115
112-- peerport :: PortNumber 116-- peerport :: PortNumber
113-- peerport = 5269 117-- peerport = 5269
@@ -121,13 +125,6 @@ my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574"
121newtype Local a = Local a deriving (Eq,Ord,Show) 125newtype Local a = Local a deriving (Eq,Ord,Show)
122newtype Remote a = Remote a deriving (Eq,Ord,Show) 126newtype Remote a = Remote a deriving (Eq,Ord,Show)
123 127
124data MUC = MUC
125 { mucRoomList :: IO [(Text{-room-},Maybe Text{-friendly room name-})]
126 , mucRoomOccupants :: Text{-room-} -> IO [(Text{-nick-},Maybe Text{-friendly name-})]
127 , mucReservedNick :: Text{-room-} -> IO (Maybe (Text{-JID-} -> IO (Maybe Text)))
128 , mucJoinRoom :: Text{-room-} -> Text{-nick-} -> ClientAddress -> Stanza -> IO ()
129 }
130
131data XMPPServerParameters = 128data XMPPServerParameters =
132 XMPPServerParameters 129 XMPPServerParameters
133 { -- | Called when a client requests a resource id. The first Maybe indicates 130 { -- | Called when a client requests a resource id. The first Maybe indicates
@@ -1286,6 +1283,8 @@ monitor sv params xmpp = do
1286 stanzas <- atomically newTChan 1283 stanzas <- atomically newTChan
1287 quitVar <- atomically newEmptyTMVar 1284 quitVar <- atomically newEmptyTMVar
1288 pp_mvar <- newMVar () -- Lock for synchronous pretty-printing of stanzas in log. 1285 pp_mvar <- newMVar () -- Lock for synchronous pretty-printing of stanzas in log.
1286 joined_rooms <- atomically
1287 $ newTVar (Map.empty :: Map.Map ClientAddress (Map.Map (Text,Text) (TChan Stanza,JoinedRoom ClientAddress)))
1289 fix $ \loop -> do 1288 fix $ \loop -> do
1290 action <- atomically $ foldr1 orElse 1289 action <- atomically $ foldr1 orElse
1291 [ readTChan chan >>= \((addr,u),e) -> return $ do 1290 [ readTChan chan >>= \((addr,u),e) -> return $ do
@@ -1325,7 +1324,7 @@ monitor sv params xmpp = do
1325 -} 1324 -}
1326 dup <- cloneStanza stanza 1325 dup <- cloneStanza stanza
1327 1326
1328 t <- forkIO $ do applyStanza sv quitVar xmpp stanza 1327 t <- forkIO $ do applyStanza sv joined_rooms quitVar xmpp stanza
1329 forwardStanza quitVar xmpp stanza 1328 forwardStanza quitVar xmpp stanza
1330 labelThread t $ "process." ++ stanzaTypeString stanza 1329 labelThread t $ "process." ++ stanzaTypeString stanza
1331 1330
@@ -1347,6 +1346,19 @@ monitor sv params xmpp = do
1347 liftIO $ takeMVar pp_mvar 1346 liftIO $ takeMVar pp_mvar
1348 runConduit $ stanzaToConduit dup .| prettyPrint typ 1347 runConduit $ stanzaToConduit dup .| prettyPrint typ
1349 liftIO $ putMVar pp_mvar () 1348 liftIO $ putMVar pp_mvar ()
1349 , do
1350 m <- readTVar joined_rooms
1351 foldr orElse retry $ (`map` (do (k,rs) <- Map.toList m
1352 i <- Map.toList rs
1353 return (k,i)))
1354 $ \(k,((rkey,muckey),(replyto,r))) -> do
1355 (mine,ChatTransaction no cjid cnick es) <- readRoom k r
1356 return $ do
1357 me <- xmppTellMyNameToClient xmpp k
1358 dput XJabber $ "CHAT " ++ Text.unpack rkey ++ ": <" ++ Text.unpack cnick ++ "> " ++ show es
1359 forM_ es $ \case
1360 Join | mine -> sendRoomOccupants muckey me cnick rkey r replyto
1361 _ -> return ()
1350 ] 1362 ]
1351 action 1363 action
1352 loop 1364 loop
@@ -1355,6 +1367,30 @@ monitor sv params xmpp = do
1355 where 1367 where
1356 _ = str :: String 1368 _ = str :: String
1357 1369
1370sendRoomOccupants :: Text -> Text -> Text -> Text -> JoinedRoom k -> TChan Stanza -> IO ()
1371sendRoomOccupants a me them room r replyto = do
1372 let roomjid n = room <> "@" <> a <> "." <> me <> "/" <> n
1373 xs <- map (\(n,m) -> (roomjid n, m))
1374 <$> atomically (roomOccupants $ joinedRoom r)
1375 let (ys,xs') = partition (\(jid,_) -> jid == roomjid them) xs
1376 forM_ xs $ \(jid,_) -> do
1377 stanza <- makePresenceStanzaEx "jabber:client" (Just jid) Available
1378 [ EventBeginElement "{http://jabber.org/protocol/muc#user}x" []
1379 , EventEndElement "{http://jabber.org/protocol/muc#user}x"
1380 ]
1381 ioWriteChan replyto stanza
1382 forM_ ys $ \(jid,_) -> do
1383 stanza <- makePresenceStanzaEx "jabber:client" (Just jid) Available
1384 [ EventBeginElement "{http://jabber.org/protocol/muc#user}x" []
1385 , EventBeginElement "{http://jabber.org/protocol/muc#user}status"
1386 [ ("code",[ContentText "110"]) -- self-presence code.
1387 ]
1388 , EventEndElement "{http://jabber.org/protocol/muc#user}status"
1389 , EventEndElement "{http://jabber.org/protocol/muc#user}x"
1390 ]
1391 ioWriteChan replyto stanza
1392
1393
1358stanzaTypeString :: StanzaWrap a -> String 1394stanzaTypeString :: StanzaWrap a -> String
1359stanzaTypeString stanza = concat . take 1 . words $ show (stanzaType stanza) 1395stanzaTypeString stanza = concat . take 1 . words $ show (stanzaType stanza)
1360 1396
@@ -1382,12 +1418,13 @@ lookupService me mucs to = case Text.toLower to of
1382 _ -> NotMe 1418 _ -> NotMe
1383 1419
1384applyStanza :: Server PeerAddress ConnectionData releaseKey Event 1420applyStanza :: Server PeerAddress ConnectionData releaseKey Event
1421 -> TVar (Map.Map ClientAddress (Map.Map (Text,Text) (TChan Stanza,JoinedRoom ClientAddress)))
1385 -> TMVar () 1422 -> TMVar ()
1386 -> XMPPServerParameters 1423 -> XMPPServerParameters
1387 -> StanzaWrap (LockedChan Event) 1424 -> StanzaWrap (LockedChan Event)
1388 -> IO () 1425 -> IO ()
1389 1426
1390applyStanza sv quitVar xmpp stanza = case stanzaOrigin stanza of 1427applyStanza sv joined_rooms quitVar xmpp stanza = case stanzaOrigin stanza of
1391 ClientOrigin k replyto -> 1428 ClientOrigin k replyto ->
1392 case stanzaType stanza of 1429 case stanzaType stanza of
1393 RequestResource clientsNameForMe wanted -> do 1430 RequestResource clientsNameForMe wanted -> do
@@ -1446,7 +1483,12 @@ applyStanza sv quitVar xmpp stanza = case stanzaOrigin stanza of
1446 [ ("by", [ContentText roomjid]) ] 1483 [ ("by", [ContentText roomjid]) ]
1447 sendReply quitVar (Error JidMalformed (head reply)) reply replyto 1484 sendReply quitVar (Error JidMalformed (head reply)) reply replyto
1448 Just nick -> do 1485 Just nick -> do
1449 mucJoinRoom muc room nick k stanza 1486 jid <- xmppTellClientHisName xmpp k
1487 r <- mucJoinRoom muc jid nick room k -- stanza
1488 atomically $ do
1489 jrs <- readTVar joined_rooms
1490 let m = Map.findWithDefault Map.empty k jrs
1491 writeTVar joined_rooms $ Map.insert k (Map.insert (room,mucname) (replyto,r) m) jrs
1450 | otherwise -> do 1492 | otherwise -> do
1451 -- Handle presence stanza that is not a chatroom join. 1493 -- Handle presence stanza that is not a chatroom join.
1452 xmppInformClientPresence xmpp k stanza 1494 xmppInformClientPresence xmpp k stanza