summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2013-07-08 21:15:37 -0400
committerjoe <joe@jerkface.net>2013-07-08 21:15:37 -0400
commit88274ed5e6d6ffa37683c7e213095d23fd31decd (patch)
treec0d82589db94c5134d745a939670bcd9e4bde1c9
parentd163ee7393bcfcc2503698ea58db646546cfb55f (diff)
Send roster push events to client in case of subscription requests.
-rw-r--r--Presence/XMPP.hs67
-rw-r--r--Presence/XMPPTypes.hs6
-rw-r--r--Presence/main.hs42
3 files changed, 100 insertions, 15 deletions
diff --git a/Presence/XMPP.hs b/Presence/XMPP.hs
index 1520839e..a08c1a0e 100644
--- a/Presence/XMPP.hs
+++ b/Presence/XMPP.hs
@@ -470,19 +470,59 @@ prettyPrint prefix xs =
470 liftIO $ do 470 liftIO $ do
471 CL.sourceList xs $= renderBytes (def { rsPretty=True }) =$= CB.lines $$ CL.mapM_ (S.putStrLn . (prefix `S.append`)) 471 CL.sourceList xs $= renderBytes (def { rsPretty=True }) =$= CB.lines $$ CL.mapM_ (S.putStrLn . (prefix `S.append`))
472 472
473toClient :: MonadIO m => TChan Presence -> TChan Commands -> Source m [XML.Event] 473
474toClient pchan cmdChan = fix $ \loop -> do 474rosterPush to contact attrs = do
475 let send xs = yield xs >> prettyPrint ">C: " xs 475 let n = name to
476 rsc = resource to
477 names <- getNamesForPeer (peer to)
478 let tostr p = L.decodeUtf8 $ n <$++> "@" <?++> L.fromChunks [p] <++?> "/" <++$> rsc
479 jidstrs = fmap (toStrict . tostr) names
480 tojid = head jidstrs
481 return
482 [ EventBeginElement "{jabber:client}iq"
483 [ attr "to" tojid
484 , attr "id" "someid"
485 , attr "type" "set"
486 ]
487 , EventBeginElement "{jabber:iq:roster}query" []
488 , EventBeginElement "{jabber:iq:roster}item" (attr "jid" contact : attrs)
489 , EventEndElement "{jabber:iq:roster}item"
490 , EventEndElement "{jabber:iq:roster}query"
491 , EventEndElement "{jabber:client}iq"
492 ]
493
494data EventsForClient = CmdChan Commands
495 | PChan Presence
496 | RChan RosterEvent
497
498toClient :: (MonadIO m, JabberClientSession session ) =>
499 session -> TChan Presence -> TChan Commands -> TChan RosterEvent -> Source m [XML.Event]
500toClient session pchan cmdChan rchan = toClient' False False
501 where
502 toClient' isBound isInterested = do
503 let loop = toClient' isBound isInterested
504 send xs = yield xs >> prettyPrint ">C: " xs
476 event <- liftIO . atomically $ 505 event <- liftIO . atomically $
477 orElse (fmap Left $ readTChan pchan) 506 foldr1 orElse [fmap PChan $ readTChan pchan
478 (fmap Right $ readTChan cmdChan) 507 ,fmap RChan $ readTChan rchan
508 ,fmap CmdChan $ readTChan cmdChan
509 ]
479 case event of 510 case event of
480 Right QuitThread -> return () 511 CmdChan QuitThread -> return ()
481 Right (Send xs) -> send xs >> loop 512 CmdChan (Send xs) -> send xs >> loop
482 Right cmd -> liftIO (putStrLn $ "unhandled event: "++show cmd) >> loop 513 CmdChan BoundToResource -> toClient' True isInterested
483 Left presence -> do 514 CmdChan InterestedInRoster -> toClient' isBound True
484 xs <- liftIO $ xmlifyPresenceForClient presence 515 CmdChan cmd -> liftIO (putStrLn $ "unhandled event: "++show cmd) >> loop
485 send xs 516 RChan (RequestedSubscription who contact) -> do
517 jid <- liftIO $ getJID session
518 when (isInterested && Just who==name jid) $ do
519 r <- liftIO $ rosterPush jid (toStrict . L.decodeUtf8 $ contact) [attr "ask" "subscribe"]
520 send r
521 loop
522 PChan presence -> do
523 when isBound $ do
524 xs <- liftIO $ xmlifyPresenceForClient presence
525 send xs
486 loop 526 loop
487 527
488handleClient 528handleClient
@@ -496,10 +536,11 @@ handleClient st src snk = do
496 session <- newSession session_factory sock 536 session <- newSession session_factory sock
497 Prelude.putStrLn $ "PEER NAME: "++Prelude.show pname 537 Prelude.putStrLn $ "PEER NAME: "++Prelude.show pname
498 pchan <- subscribe session Nothing 538 pchan <- subscribe session Nothing
539 rchan <- subscribeToRoster session
499 cmdChan <- atomically newTChan 540 cmdChan <- atomically newTChan
500 541
501#ifdef RENDERFLUSH 542#ifdef RENDERFLUSH
502 writer <- async ( toClient pchan cmdChan 543 writer <- async ( toClient session pchan cmdChan rchan
503 $$ flushList 544 $$ flushList
504 =$= renderBuilderFlush def 545 =$= renderBuilderFlush def
505 =$= builderToByteStringFlush 546 =$= builderToByteStringFlush
@@ -695,6 +736,8 @@ clientRequestsSubscription session cmdChan stanza = do
695 addSolicited session (L.fromChunks [S.encodeUtf8 to_str]) -- jid 736 addSolicited session (L.fromChunks [S.encodeUtf8 to_str]) -- jid
696 putStrLn $ "added to solicited: " ++ show to_jid 737 putStrLn $ "added to solicited: " ++ show to_jid
697 -- TODO: create roster item and push to interested clients 738 -- TODO: create roster item and push to interested clients
739 -- addSolicited should write event to a roster channel
740 -- that toClient will be listening on.
698 return () 741 return ()
699 742
700peerRequestsSubsription session stanza = do 743peerRequestsSubsription session stanza = do
diff --git a/Presence/XMPPTypes.hs b/Presence/XMPPTypes.hs
index 301f19fd..2bba8614 100644
--- a/Presence/XMPPTypes.hs
+++ b/Presence/XMPPTypes.hs
@@ -38,6 +38,7 @@ class JabberClientSession session where
38 getJID :: session -> IO JID 38 getJID :: session -> IO JID
39 closeSession :: session -> IO () 39 closeSession :: session -> IO ()
40 subscribe :: session -> Maybe JID -> IO (TChan Presence) 40 subscribe :: session -> Maybe JID -> IO (TChan Presence)
41 subscribeToRoster :: session -> IO (TChan RosterEvent)
41 forCachedPresence :: session -> (Presence -> IO ()) -> IO () 42 forCachedPresence :: session -> (Presence -> IO ()) -> IO ()
42 getMyBuddies :: session -> IO [ByteString] 43 getMyBuddies :: session -> IO [ByteString]
43 getMySubscribers :: session -> IO [ByteString] 44 getMySubscribers :: session -> IO [ByteString]
@@ -72,6 +73,11 @@ data JabberShow = Offline
72data Presence = Presence JID JabberShow 73data Presence = Presence JID JabberShow
73 deriving Prelude.Show 74 deriving Prelude.Show
74 75
76data RosterEvent = RequestedSubscription
77 {- user: -} ByteString
78 {- contact: -} ByteString
79 deriving Prelude.Show
80
75data Peer = LocalHost | RemotePeer SockAddr 81data Peer = LocalHost | RemotePeer SockAddr
76 deriving (Eq,Prelude.Show) 82 deriving (Eq,Prelude.Show)
77 83
diff --git a/Presence/main.hs b/Presence/main.hs
index bf4809a8..a7ff5e5a 100644
--- a/Presence/main.hs
+++ b/Presence/main.hs
@@ -1,6 +1,7 @@
1{-# LANGUAGE CPP #-} 1{-# LANGUAGE CPP #-}
2{-# LANGUAGE OverloadedStrings #-} 2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE TypeFamilies #-} 3{-# LANGUAGE TypeFamilies #-}
4{-# LANGUAGE ExistentialQuantification #-}
4module Main where 5module Main where
5 6
6import System.Directory 7import System.Directory
@@ -79,6 +80,7 @@ data PresenceState = PresenceState
79 , subscriberMap :: TVar (Map JID (RefCount,TChan Presence)) -- UNUSED as yet 80 , subscriberMap :: TVar (Map JID (RefCount,TChan Presence)) -- UNUSED as yet
80 , localSubscriber :: TMVar (RefCount,TChan Presence) -- TODO: rename this, its not just locals 81 , localSubscriber :: TMVar (RefCount,TChan Presence) -- TODO: rename this, its not just locals
81 -- ... or make a seperate channel for remotes 82 -- ... or make a seperate channel for remotes
83 , rosterChannel :: TMVar (RefCount,TChan RosterEvent)
82 , remoteUsers :: TVar (Map Peer (RefCount,TVar (MultiMap JabberUser (JabberResource,JabberShow)))) 84 , remoteUsers :: TVar (Map Peer (RefCount,TVar (MultiMap JabberUser (JabberResource,JabberShow))))
83 , outGoingConnections :: TVar (Map Peer (TChan OutBoundMessage, ThreadId)) 85 , outGoingConnections :: TVar (Map Peer (TChan OutBoundMessage, ThreadId))
84 } 86 }
@@ -99,6 +101,7 @@ data ClientSession = ClientSession {
99 localhost :: Peer, -- ByteString, 101 localhost :: Peer, -- ByteString,
100 unix_uid :: (IORef (Maybe (UserID,L.ByteString))), 102 unix_uid :: (IORef (Maybe (UserID,L.ByteString))),
101 unix_resource :: (IORef (Maybe L.ByteString)), 103 unix_resource :: (IORef (Maybe L.ByteString)),
104 chans :: TVar [RefCountedChan],
102 presence_state :: PresenceState 105 presence_state :: PresenceState
103} 106}
104 107
@@ -112,7 +115,8 @@ instance JabberClientSession ClientSession where
112 L.putStrLn $ "CLIENT SESSION: open " <++> bshow muid 115 L.putStrLn $ "CLIENT SESSION: open " <++> bshow muid
113 uid_ref <- newIORef muid 116 uid_ref <- newIORef muid
114 res_ref <- newIORef Nothing 117 res_ref <- newIORef Nothing
115 return $ ClientSession (hostname state) uid_ref res_ref state 118 chans <- atomically $ newTVar []
119 return $ ClientSession (hostname state) uid_ref res_ref chans state
116 120
117 setResource s resource = do 121 setResource s resource = do
118 -- TODO: handle resource = empty string 122 -- TODO: handle resource = empty string
@@ -137,15 +141,30 @@ instance JabberClientSession ClientSession where
137 return (JID (Just user) host rsc) 141 return (JID (Just user) host rsc)
138 142
139 closeSession session = do 143 closeSession session = do
144 atomically $ do
145 cs <- readTVar (chans session)
146 forM_ cs $ \(RefCountedChan c) -> do
147 unsubscribeToChan c
140 L.putStrLn "CLIENT SESSION: close" 148 L.putStrLn "CLIENT SESSION: close"
141 149
142 subscribe session Nothing = do 150 subscribe session Nothing = do
143 let tmvar = localSubscriber (presence_state session) 151 let tmvar = localSubscriber (presence_state session)
144 atomically $ subscribeToChan tmvar 152 atomically $ do
153 cs <- readTVar (chans session)
154 writeTVar (chans session) (RefCountedChan tmvar:cs)
155 subscribeToChan tmvar
145 subscribe session (Just jid) = do -- UNUSED as yet 156 subscribe session (Just jid) = do -- UNUSED as yet
146 let tvar = subscriberMap (presence_state session) 157 let tvar = subscriberMap (presence_state session)
147 atomically $ subscribeToMap tvar jid 158 atomically $ subscribeToMap tvar jid
148 159
160 subscribeToRoster session = do
161 let rchan = rosterChannel . presence_state $ session
162 atomically $ do
163 cs <- readTVar (chans session)
164 writeTVar (chans session) (RefCountedChan rchan:cs)
165 subscribeToChan rchan
166
167
149 forCachedPresence s action = do 168 forCachedPresence s action = do
150 jid <- getJID s 169 jid <- getJID s
151 L.putStrLn $ "forCachedPresence "<++> bshow jid 170 L.putStrLn $ "forCachedPresence "<++> bshow jid
@@ -171,6 +190,13 @@ instance JabberClientSession ClientSession where
171 addSolicited s jid = do 190 addSolicited s jid = do
172 user <- readIORef (unix_uid s) >>= getJabberUserForId 191 user <- readIORef (unix_uid s) >>= getJabberUserForId
173 ConfigFiles.addSolicited user jid -- (L.show jid) 192 ConfigFiles.addSolicited user jid -- (L.show jid)
193 let rchan = rosterChannel . presence_state $ s
194 atomically $ do
195 isempty <- isEmptyTMVar rchan
196 when (not isempty) $ do
197 (_,ch) <- readTMVar rchan
198 writeTChan ch (RequestedSubscription user jid)
199
174 200
175 getMyBuddies s = do 201 getMyBuddies s = do
176 user <- readIORef (unix_uid s) >>= getJabberUserForId 202 user <- readIORef (unix_uid s) >>= getJabberUserForId
@@ -251,6 +277,9 @@ instance JabberPeerSession PeerSession where
251 getSubscribers _ user = ConfigFiles.getSubscribers user 277 getSubscribers _ user = ConfigFiles.getSubscribers user
252 278
253 279
280data RefCountedChan = forall a. RefCountedChan (TMVar (RefCount,TChan a))
281
282subscribeToChan :: TMVar (RefCount, TChan a) -> STM (TChan a)
254subscribeToChan tmvar = 283subscribeToChan tmvar =
255 (do (cnt,chan) <- takeTMVar tmvar 284 (do (cnt,chan) <- takeTMVar tmvar
256 putTMVar tmvar (cnt+1,chan) 285 putTMVar tmvar (cnt+1,chan)
@@ -260,6 +289,12 @@ subscribeToChan tmvar =
260 (do chan <- newTChan 289 (do chan <- newTChan
261 putTMVar tmvar (1,chan) 290 putTMVar tmvar (1,chan)
262 return chan ) 291 return chan )
292unsubscribeToChan :: TMVar (RefCount,TChan a) -> STM ()
293unsubscribeToChan tmvar = do
294 isEmpty <- isEmptyTMVar tmvar
295 when (not isEmpty) $ do
296 (cnt,chan) <- takeTMVar tmvar
297 when (cnt>1) (putTMVar tmvar (cnt-1,chan))
263 298
264getRefFromMap tvar key newObject copyObject = do 299getRefFromMap tvar key newObject copyObject = do
265 subs <- readTVar tvar 300 subs <- readTVar tvar
@@ -322,9 +357,10 @@ newPresenceState hostname = atomically $ do
322 us <- newTVar (Set.empty) 357 us <- newTVar (Set.empty)
323 subs <- newTVar (Map.empty) 358 subs <- newTVar (Map.empty)
324 locals_greedy <- newEmptyTMVar 359 locals_greedy <- newEmptyTMVar
360 rchan <- newEmptyTMVar
325 remotes <- newTVar (Map.empty) 361 remotes <- newTVar (Map.empty)
326 server_connections <- newServerConnections 362 server_connections <- newServerConnections
327 return $ PresenceState hostname tty us subs locals_greedy remotes server_connections 363 return $ PresenceState hostname tty us subs locals_greedy rchan remotes server_connections
328 364
329sendProbes state jid = do 365sendProbes state jid = do
330 L.putStrLn $ "sending probes for " <++> bshow jid 366 L.putStrLn $ "sending probes for " <++> bshow jid