diff options
author | joe <joe@jerkface.net> | 2014-03-15 19:24:15 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-03-15 19:24:15 -0400 |
commit | 0a4d745e1f08c7c7a89f8c79ffb90170c13d2c88 (patch) | |
tree | 5d514502b1def357bd3b62a959d5955482f202be | |
parent | 600a7f5a562357ea30c51ff52f2c2f950afe47f6 (diff) |
notify remote peers of utmp presences
-rw-r--r-- | Presence/ConsoleWriter.hs | 40 | ||||
-rw-r--r-- | Presence/UTmp.hs | 1 | ||||
-rw-r--r-- | xmppServer.hs | 69 |
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 | |||
28 | import UTmp ( users2, utmp_file, UtmpRecord(..), UT_Type(..) ) | 28 | import UTmp ( users2, utmp_file, UtmpRecord(..), UT_Type(..) ) |
29 | import FGConsole ( monitorTTY ) | 29 | import FGConsole ( monitorTTY ) |
30 | import XMPPServer ( Stanza, makePresenceStanza, JabberShow(..), stanzaType | 30 | import XMPPServer ( Stanza, makePresenceStanza, JabberShow(..), stanzaType |
31 | , LangSpecificMessage(..), msgLangMap ) | 31 | , LangSpecificMessage(..), msgLangMap, cloneStanza ) |
32 | import ClientState | ||
32 | 33 | ||
33 | data ConsoleWriter = ConsoleWriter | 34 | data 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 | ||
39 | tshow x = Text.pack . show $ x | 41 | tshow 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 ) | |||
28 | import Unsafe.Coerce ( unsafeCoerce ) | 28 | import Unsafe.Coerce ( unsafeCoerce ) |
29 | import Network.Socket ( SockAddr(..) ) | 29 | import Network.Socket ( SockAddr(..) ) |
30 | import qualified Data.Text.Encoding as Text | 30 | import qualified Data.Text.Encoding as Text |
31 | import SockAddr () | ||
31 | 32 | ||
32 | 33 | ||
33 | utmp_file = Paths.utmp -- "/var/run/utmp" | 34 | utmp_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 | |||
37 | import XMPPServer | 37 | import XMPPServer |
38 | import PeerResolve | 38 | import PeerResolve |
39 | import ConsoleWriter | 39 | import ConsoleWriter |
40 | import ClientState | ||
40 | 41 | ||
41 | type UserName = Text | 42 | type UserName = Text |
42 | type ResourceName = Text | 43 | type 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 | ||
77 | cf_available :: Int8 | ||
78 | cf_available = 0x1 | ||
79 | cf_interested :: Int8 | ||
80 | cf_interested = 0x2 | ||
81 | |||
82 | data 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 | ||
91 | clientIsAvailable c = do | ||
92 | flgs <- readTVar (clientFlags c) | ||
93 | return $ flgs .&. cf_available /= 0 | ||
94 | |||
95 | -- | True if the client has requested a roster | ||
96 | clientIsInterested c = do | ||
97 | flgs <- readTVar (clientFlags c) | ||
98 | return $ flgs .&. cf_interested /= 0 | ||
99 | 78 | ||
100 | data LocalPresence = LocalPresence | 79 | data LocalPresence = LocalPresence |
101 | { networkClients :: Map ConnectionKey ClientState | 80 | { networkClients :: Map ConnectionKey ClientState |
@@ -130,7 +109,7 @@ pcIsEmpty pc = Map.null (networkClients pc) | |||
130 | data PresenceState = PresenceState | 109 | data 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 | {- |
338 | rewriteJIDForClient1:: Text -> IO (Maybe ((Maybe Text,Text,Maybe Text),SockAddr)) | 317 | rewriteJIDForClient1:: Text -> IO (Maybe ((Maybe Text,Text,Maybe Text),SockAddr)) |
@@ -480,10 +459,13 @@ deliverMessage state fail msg = | |||
480 | 459 | ||
481 | 460 | ||
482 | setClientFlag state k flag = | 461 | setClientFlag 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 | |||
467 | setClientFlag0 client flag = | ||
468 | modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag) | ||
487 | 469 | ||
488 | informSentRoster state k = do | 470 | informSentRoster 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. |
505 | informClientPresence state k stanza = do | 487 | informClientPresence 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 | |||
491 | informClientPresence0 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 ()) |