summaryrefslogtreecommitdiff
path: root/Presence/Chat.hs
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/Chat.hs
parentc2cce27bc86c5aefccc5e2afa9b2063e8c915336 (diff)
MUC: Two-step nominate/comit chat transactions.
Diffstat (limited to 'Presence/Chat.hs')
-rw-r--r--Presence/Chat.hs120
1 files changed, 104 insertions, 16 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