diff options
author | joe <joe@jerkface.net> | 2013-07-08 21:15:37 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2013-07-08 21:15:37 -0400 |
commit | 88274ed5e6d6ffa37683c7e213095d23fd31decd (patch) | |
tree | c0d82589db94c5134d745a939670bcd9e4bde1c9 /Presence | |
parent | d163ee7393bcfcc2503698ea58db646546cfb55f (diff) |
Send roster push events to client in case of subscription requests.
Diffstat (limited to 'Presence')
-rw-r--r-- | Presence/XMPP.hs | 67 | ||||
-rw-r--r-- | Presence/XMPPTypes.hs | 6 | ||||
-rw-r--r-- | Presence/main.hs | 42 |
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 | ||
473 | toClient :: MonadIO m => TChan Presence -> TChan Commands -> Source m [XML.Event] | 473 | |
474 | toClient pchan cmdChan = fix $ \loop -> do | 474 | rosterPush 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 | |||
494 | data EventsForClient = CmdChan Commands | ||
495 | | PChan Presence | ||
496 | | RChan RosterEvent | ||
497 | |||
498 | toClient :: (MonadIO m, JabberClientSession session ) => | ||
499 | session -> TChan Presence -> TChan Commands -> TChan RosterEvent -> Source m [XML.Event] | ||
500 | toClient 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 | ||
488 | handleClient | 528 | handleClient |
@@ -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 | ||
700 | peerRequestsSubsription session stanza = do | 743 | peerRequestsSubsription 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 | |||
72 | data Presence = Presence JID JabberShow | 73 | data Presence = Presence JID JabberShow |
73 | deriving Prelude.Show | 74 | deriving Prelude.Show |
74 | 75 | ||
76 | data RosterEvent = RequestedSubscription | ||
77 | {- user: -} ByteString | ||
78 | {- contact: -} ByteString | ||
79 | deriving Prelude.Show | ||
80 | |||
75 | data Peer = LocalHost | RemotePeer SockAddr | 81 | data 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 #-} | ||
4 | module Main where | 5 | module Main where |
5 | 6 | ||
6 | import System.Directory | 7 | import 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 | ||
280 | data RefCountedChan = forall a. RefCountedChan (TMVar (RefCount,TChan a)) | ||
281 | |||
282 | subscribeToChan :: TMVar (RefCount, TChan a) -> STM (TChan a) | ||
254 | subscribeToChan tmvar = | 283 | subscribeToChan 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 ) |
292 | unsubscribeToChan :: TMVar (RefCount,TChan a) -> STM () | ||
293 | unsubscribeToChan 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 | ||
264 | getRefFromMap tvar key newObject copyObject = do | 299 | getRefFromMap 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 | ||
329 | sendProbes state jid = do | 365 | sendProbes state jid = do |
330 | L.putStrLn $ "sending probes for " <++> bshow jid | 366 | L.putStrLn $ "sending probes for " <++> bshow jid |