summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2014-03-15 19:24:15 -0400
committerjoe <joe@jerkface.net>2014-03-15 19:24:15 -0400
commit0a4d745e1f08c7c7a89f8c79ffb90170c13d2c88 (patch)
tree5d514502b1def357bd3b62a959d5955482f202be
parent600a7f5a562357ea30c51ff52f2c2f950afe47f6 (diff)
notify remote peers of utmp presences
-rw-r--r--Presence/ConsoleWriter.hs40
-rw-r--r--Presence/UTmp.hs1
-rw-r--r--xmppServer.hs69
3 files changed, 58 insertions, 52 deletions
diff --git a/Presence/ConsoleWriter.hs b/Presence/ConsoleWriter.hs
index 5222258e..3b02dbbc 100644
--- a/Presence/ConsoleWriter.hs
+++ b/Presence/ConsoleWriter.hs
@@ -28,12 +28,14 @@ import qualified Network.BSD as BSD
28import UTmp ( users2, utmp_file, UtmpRecord(..), UT_Type(..) ) 28import UTmp ( users2, utmp_file, UtmpRecord(..), UT_Type(..) )
29import FGConsole ( monitorTTY ) 29import FGConsole ( monitorTTY )
30import XMPPServer ( Stanza, makePresenceStanza, JabberShow(..), stanzaType 30import XMPPServer ( Stanza, makePresenceStanza, JabberShow(..), stanzaType
31 , LangSpecificMessage(..), msgLangMap ) 31 , LangSpecificMessage(..), msgLangMap, cloneStanza )
32import ClientState
32 33
33data ConsoleWriter = ConsoleWriter 34data ConsoleWriter = ConsoleWriter
34 { cwPresenceChan :: TChan Stanza 35 { cwPresenceChan :: TChan (ClientState,Stanza)
35 , csActiveTTY :: TVar Word8 36 , csActiveTTY :: TVar Word8
36 , csUtmp :: TVar (Map Text (TVar (Maybe UtmpRecord))) 37 , csUtmp :: TVar (Map Text (TVar (Maybe UtmpRecord)))
38 , cwClients :: TVar (Map Text ClientState)
37 } 39 }
38 40
39tshow x = Text.pack . show $ x 41tshow x = Text.pack . show $ x
@@ -87,9 +89,11 @@ newConsoleWriter = do
87 cs <- atomically $ do 89 cs <- atomically $ do
88 ttyvar <- newTVar 0 90 ttyvar <- newTVar 0
89 utmpvar <- newTVar Map.empty 91 utmpvar <- newTVar Map.empty
92 clients <- newTVar Map.empty
90 return $ ConsoleWriter { cwPresenceChan = chan 93 return $ ConsoleWriter { cwPresenceChan = chan
91 , csActiveTTY = ttyvar 94 , csActiveTTY = ttyvar
92 , csUtmp = utmpvar 95 , csUtmp = utmpvar
96 , cwClients = clients
93 } 97 }
94 outvar <- atomically $ newTMVar () 98 outvar <- atomically $ newTMVar ()
95 let logit outvar s = do 99 let logit outvar s = do
@@ -216,7 +220,22 @@ newCon log cw activeTTY utmp = do
216 else "") 220 else "")
217 <> " session=" <> tshow (utmpSession u) 221 <> " session=" <> tshow (utmpSession u)
218 <> " addr=" <> tshow (utmpRemoteAddr u) 222 <> " addr=" <> tshow (utmpRemoteAddr u)
219 loop tty tu (Just u) 223 let r = resource u
224 stanza <- makePresenceStanza
225 "jabber:client"
226 (Just jid)
227 (jstatus r tty tu)
228 statusv <- atomically $ newTVar (Just stanza)
229 flgs <- atomically $ newTVar 0
230 let client = ClientState { clientResource = r
231 , clientUser = utmpUser u
232 , clientPid = Nothing
233 , clientStatus = statusv
234 , clientFlags = flgs }
235 atomically $ do
236 modifyTVar (cwClients cw) $ Map.insert r client
237 writeTChan (cwPresenceChan cw) (client,stanza)
238 loop client tty tu (Just u)
220 where 239 where
221 bstatus r ttynum mtu 240 bstatus r ttynum mtu
222 = r == ttystr 241 = r == ttystr
@@ -237,7 +256,7 @@ newCon log cw activeTTY utmp = do
237 where 256 where
238 (fst3,rst) = Text.splitAt 3 r 257 (fst3,rst) = Text.splitAt 3 r
239 258
240 loop tty tu u = do 259 loop client tty tu u = do
241 what <- atomically $ foldr1 orElse 260 what <- atomically $ foldr1 orElse
242 [ do (tty',tu') <- retryWhen activeTTY 261 [ do (tty',tu') <- retryWhen activeTTY
243 (\ttyu -> bstatus r tty tu == uncurry (bstatus r) ttyu) 262 (\ttyu -> bstatus r tty tu == uncurry (bstatus r) ttyu)
@@ -255,9 +274,12 @@ newCon log cw activeTTY utmp = do
255 "jabber:client" 274 "jabber:client"
256 (Just jid) 275 (Just jid)
257 (jstatus r tty' tu') 276 (jstatus r tty' tu')
258 atomically $ writeTChan (cwPresenceChan cw) stanza 277 dup <- cloneStanza stanza
278 atomically $ do
279 writeTVar (clientStatus client) $ Just dup
280 writeTChan (cwPresenceChan cw) (client,stanza)
259 log $ status r tty' tu' <> " " <> jid 281 log $ status r tty' tu' <> " " <> jid
260 loop tty' tu' u 282 loop client tty' tu' u
261 283
262 utmpChanged u' = maybe dead changed u' 284 utmpChanged u' = maybe dead changed u'
263 where 285 where
@@ -265,10 +287,12 @@ newCon log cw activeTTY utmp = do
265 jid0 <- maybe (return "") ujid u 287 jid0 <- maybe (return "") ujid u
266 jid <- ujid u' 288 jid <- ujid u'
267 log $ "changed: " <> jid0 <> " --> " <> jid 289 log $ "changed: " <> jid0 <> " --> " <> jid
268 loop tty tu (Just u') 290 loop client tty tu (Just u')
269 dead = do 291 dead = do
270 jid <- maybe (return "") ujid u 292 jid <- maybe (return "") ujid u
271 stanza <- makePresenceStanza "jabber:client" (Just jid) Offline 293 stanza <- makePresenceStanza "jabber:client" (Just jid) Offline
272 atomically $ writeTChan (cwPresenceChan cw) stanza 294 atomically $ do
295 modifyTVar (cwClients cw) $ Map.delete (clientResource client)
296 writeTChan (cwPresenceChan cw) (client,stanza)
273 log $ "Offline " <> jid 297 log $ "Offline " <> jid
274 298
diff --git a/Presence/UTmp.hs b/Presence/UTmp.hs
index 99d51205..2cfbdf38 100644
--- a/Presence/UTmp.hs
+++ b/Presence/UTmp.hs
@@ -28,6 +28,7 @@ import Data.Text ( Text )
28import Unsafe.Coerce ( unsafeCoerce ) 28import Unsafe.Coerce ( unsafeCoerce )
29import Network.Socket ( SockAddr(..) ) 29import Network.Socket ( SockAddr(..) )
30import qualified Data.Text.Encoding as Text 30import qualified Data.Text.Encoding as Text
31import SockAddr ()
31 32
32 33
33utmp_file = Paths.utmp -- "/var/run/utmp" 34utmp_file = Paths.utmp -- "/var/run/utmp"
diff --git a/xmppServer.hs b/xmppServer.hs
index d5ad7c6a..1ab36cd6 100644
--- a/xmppServer.hs
+++ b/xmppServer.hs
@@ -37,6 +37,7 @@ import LocalPeerCred
37import XMPPServer 37import XMPPServer
38import PeerResolve 38import PeerResolve
39import ConsoleWriter 39import ConsoleWriter
40import ClientState
40 41
41type UserName = Text 42type UserName = Text
42type ResourceName = Text 43type ResourceName = Text
@@ -74,28 +75,6 @@ localJID user resource = do
74 hostname <- textHostName 75 hostname <- textHostName
75 return $ user <> "@" <> hostname <> "/" <> resource 76 return $ user <> "@" <> hostname <> "/" <> resource
76 77
77cf_available :: Int8
78cf_available = 0x1
79cf_interested :: Int8
80cf_interested = 0x2
81
82data ClientState = ClientState
83 { clientResource :: Text
84 , clientUser :: Text
85 , clientPid :: Maybe ProcessID
86 , clientStatus :: TVar (Maybe Stanza)
87 , clientFlags :: TVar Int8
88 }
89
90-- | True if the client has sent an initial presence
91clientIsAvailable c = do
92 flgs <- readTVar (clientFlags c)
93 return $ flgs .&. cf_available /= 0
94
95-- | True if the client has requested a roster
96clientIsInterested c = do
97 flgs <- readTVar (clientFlags c)
98 return $ flgs .&. cf_interested /= 0
99 78
100data LocalPresence = LocalPresence 79data LocalPresence = LocalPresence
101 { networkClients :: Map ConnectionKey ClientState 80 { networkClients :: Map ConnectionKey ClientState
@@ -130,7 +109,7 @@ pcIsEmpty pc = Map.null (networkClients pc)
130data PresenceState = PresenceState 109data PresenceState = PresenceState
131 { clients :: TVar (Map ConnectionKey ClientState) 110 { clients :: TVar (Map ConnectionKey ClientState)
132 , clientsByUser :: TVar (Map Text LocalPresence) 111 , clientsByUser :: TVar (Map Text LocalPresence)
133 , remotesByPeer :: TVar (Map (Maybe ConnectionKey) 112 , remotesByPeer :: TVar (Map ConnectionKey
134 (Map UserName 113 (Map UserName
135 RemotePresence)) 114 RemotePresence))
136 , associatedPeers :: TVar (Map SockAddr ()) 115 , associatedPeers :: TVar (Map SockAddr ())
@@ -326,13 +305,13 @@ eofConn state k = do
326 jids <- atomically $ do 305 jids <- atomically $ do
327 rbp <- readTVar (remotesByPeer state) 306 rbp <- readTVar (remotesByPeer state)
328 return $ do 307 return $ do
329 umap <- maybeToList $ Map.lookup (Just k) rbp 308 umap <- maybeToList $ Map.lookup k rbp
330 (u,rp) <- Map.toList umap 309 (u,rp) <- Map.toList umap
331 r <- Map.keys (resources rp) 310 r <- Map.keys (resources rp)
332 return $ unsplitJID (Just u, h, Just r) 311 return $ unsplitJID (Just u, h, Just r)
333 forM_ jids $ \jid -> do 312 forM_ jids $ \jid -> do
334 stanza <- makePresenceStanza "jabber:client" (Just jid) Offline 313 stanza <- makePresenceStanza "jabber:client" (Just jid) Offline
335 informPeerPresence state (Just k) stanza 314 informPeerPresence state k stanza
336 315
337{- 316{-
338rewriteJIDForClient1:: Text -> IO (Maybe ((Maybe Text,Text,Maybe Text),SockAddr)) 317rewriteJIDForClient1:: Text -> IO (Maybe ((Maybe Text,Text,Maybe Text),SockAddr))
@@ -480,10 +459,13 @@ deliverMessage state fail msg =
480 459
481 460
482setClientFlag state k flag = 461setClientFlag state k flag =
483 atomically $ do 462 atomically $ do
484 cmap <- readTVar (clients state) 463 cmap <- readTVar (clients state)
485 flip (maybe $ return ()) (Map.lookup k cmap) $ \client -> do 464 flip (maybe $ return ()) (Map.lookup k cmap) $ \client -> do
486 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag) 465 setClientFlag0 client flag
466
467setClientFlag0 client flag =
468 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag)
487 469
488informSentRoster state k = do 470informSentRoster state k = do
489 setClientFlag state k cf_interested 471 setClientFlag state k cf_interested
@@ -503,16 +485,16 @@ clientJID con client = unsplitJID ( Just $ clientUser client
503-- Note that a full JID from address will be added to the 485-- Note that a full JID from address will be added to the
504-- stanza if it is not present. 486-- stanza if it is not present.
505informClientPresence state k stanza = do 487informClientPresence state k stanza = do
506 dup <- cloneStanza stanza
507 atomically $ do
508 mb <- fmap (Map.lookup k) $ readTVar (clients state)
509 flip (maybe $ return ()) mb $ \cstate -> do
510 writeTVar (clientStatus cstate) $ Just dup
511 forClient state k (return ()) $ \client -> do 488 forClient state k (return ()) $ \client -> do
489 informClientPresence0 state (Just k) client stanza
490
491informClientPresence0 state mbk client stanza = do
492 dup <- cloneStanza stanza
493 atomically $ writeTVar (clientStatus client) $ Just dup
512 is_avail <- atomically $ clientIsAvailable client 494 is_avail <- atomically $ clientIsAvailable client
513 when (not is_avail) $ do 495 when (not is_avail) $ do
514 setClientFlag state k cf_available 496 atomically $ setClientFlag0 client cf_available
515 sendCachedPresence state k 497 maybe (return ()) (sendCachedPresence state) mbk
516 addrs <- subscribedPeers (clientUser client) 498 addrs <- subscribedPeers (clientUser client)
517 ktc <- atomically $ readTVar (keyToChan state) 499 ktc <- atomically $ readTVar (keyToChan state)
518 let connected = mapMaybe (flip Map.lookup ktc . PeerKey) addrs 500 let connected = mapMaybe (flip Map.lookup ktc . PeerKey) addrs
@@ -579,7 +561,7 @@ informPeerPresence state k stanza = do
579 is_avail <- atomically $ clientIsAvailable client 561 is_avail <- atomically $ clientIsAvailable client
580 when is_avail $ do 562 when is_avail $ do
581 putStrLn $ "reversing for client: " ++ show from 563 putStrLn $ "reversing for client: " ++ show from
582 froms <- flip (maybe $ return [from]) k . const $ do 564 froms <- do -- flip (maybe $ return [from]) k . const $ do
583 let ClientKey laddr = ck 565 let ClientKey laddr = ck
584 (_,trip) <- multiplyJIDForClient laddr from 566 (_,trip) <- multiplyJIDForClient laddr from
585 return (map unsplitJID trip) 567 return (map unsplitJID trip)
@@ -649,16 +631,15 @@ sendCachedPresence state k = do
649 jids <- configText ConfigFiles.getBuddies (clientUser client) 631 jids <- configText ConfigFiles.getBuddies (clientUser client)
650 let hosts = map ((\(_,h,_)->h) . splitJID) jids 632 let hosts = map ((\(_,h,_)->h) . splitJID) jids
651 addrs <- resolveAllPeers hosts 633 addrs <- resolveAllPeers hosts
652 let onlines = rbp `Map.intersection` (Map.insert Nothing () -- send console presences 634 let onlines = rbp `Map.intersection` Map.mapKeys PeerKey addrs
653 $ Map.mapKeys (Just . PeerKey) addrs)
654 ClientKey laddr = k 635 ClientKey laddr = k
655 mcon <- atomically $ do ktc <- readTVar (keyToChan state) 636 mcon <- atomically $ do ktc <- readTVar (keyToChan state)
656 return $ Map.lookup k ktc 637 return $ Map.lookup k ktc
657 flip (maybe $ return ()) mcon $ \con -> do 638 flip (maybe $ return ()) mcon $ \con -> do
658 me <- textHostName 639 -- me <- textHostName
659 forM_ (Map.toList onlines) $ \(pk, umap) -> do 640 forM_ (Map.toList onlines) $ \(pk, umap) -> do
660 forM_ (Map.toList umap) $ \(user,rp) -> do 641 forM_ (Map.toList umap) $ \(user,rp) -> do
661 let h = maybe me peerKeyToText pk 642 let h = peerKeyToText pk
662 forM_ (Map.toList $ resources rp) $ \(resource,stanza) -> do 643 forM_ (Map.toList $ resources rp) $ \(resource,stanza) -> do
663 let jid = unsplitJID (Just user,h,Just resource) 644 let jid = unsplitJID (Just user,h,Just resource)
664 (mine,js) <- multiplyJIDForClient laddr jid 645 (mine,js) <- multiplyJIDForClient laddr jid
@@ -1002,7 +983,7 @@ main = runResourceT $ do
1002 , xmppSubscribeToRoster = informSentRoster state 983 , xmppSubscribeToRoster = informSentRoster state
1003 , xmppDeliverMessage = deliverMessage state 984 , xmppDeliverMessage = deliverMessage state
1004 , xmppInformClientPresence = informClientPresence state 985 , xmppInformClientPresence = informClientPresence state
1005 , xmppInformPeerPresence = \k -> informPeerPresence state (Just k) 986 , xmppInformPeerPresence = informPeerPresence state
1006 , xmppAnswerProbe = \k stanza chan -> answerProbe state (stanzaTo stanza) k chan 987 , xmppAnswerProbe = \k stanza chan -> answerProbe state (stanzaTo stanza) k chan
1007 , xmppClientSubscriptionRequest = clientSubscriptionRequest state 988 , xmppClientSubscriptionRequest = clientSubscriptionRequest state
1008 , xmppPeerSubscriptionRequest = peerSubscriptionRequest state 989 , xmppPeerSubscriptionRequest = peerSubscriptionRequest state
@@ -1020,8 +1001,8 @@ main = runResourceT $ do
1020 console <- atomically $ dupTChan (cwPresenceChan $ consoleWriter state) 1001 console <- atomically $ dupTChan (cwPresenceChan $ consoleWriter state)
1021 fix $ \loop -> do 1002 fix $ \loop -> do
1022 what <- atomically 1003 what <- atomically
1023 $ orElse (do stanza <- readTChan console 1004 $ orElse (do (client,stanza) <- readTChan console
1024 return $ do informPeerPresence state Nothing stanza 1005 return $ do informClientPresence0 state Nothing client stanza
1025 loop) 1006 loop)
1026 (do readTMVar quitVar 1007 (do readTMVar quitVar
1027 return $ return ()) 1008 return $ return ())