summaryrefslogtreecommitdiff
path: root/Presence/Presence.hs
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2018-06-24 02:27:18 -0400
committerjoe <joe@jerkface.net>2018-06-24 03:10:43 -0400
commit55db1198b3da0c706f2b9f1ed9c8fd11fc4ae552 (patch)
treede035195ed188f8611da54e6e339d9124d2a5b3f /Presence/Presence.hs
parent3054de811f4ae7659dfc4dc338aab2c3d11b5c27 (diff)
XMPP: Type-checking on various uses of SockAddr.
Diffstat (limited to 'Presence/Presence.hs')
-rw-r--r--Presence/Presence.hs389
1 files changed, 206 insertions, 183 deletions
diff --git a/Presence/Presence.hs b/Presence/Presence.hs
index af6597b6..244bbead 100644
--- a/Presence/Presence.hs
+++ b/Presence/Presence.hs
@@ -53,11 +53,13 @@ import Network.Tox.NodeId (key2id)
53import Crypto.Tox (decodeSecret) 53import Crypto.Tox (decodeSecret)
54import DPut 54import DPut
55 55
56isPeerKey :: ConnectionKey -> Bool 56{-
57isPeerKey :: ClientAddress -> Bool
57isPeerKey k = case k of { PeerKey {} -> True ; _ -> False } 58isPeerKey k = case k of { PeerKey {} -> True ; _ -> False }
58 59
59isClientKey :: ConnectionKey -> Bool 60isClientKey :: ClientAddress -> Bool
60isClientKey k = case k of { ClientKey {} -> True ; _ -> False } 61isClientKey k = case k of { ClientKey {} -> True ; _ -> False }
62-}
61 63
62localJID :: Text -> Text -> Text -> IO Text 64localJID :: Text -> Text -> Text -> IO Text
63localJID user "." resource = do 65localJID user "." resource = do
@@ -86,20 +88,21 @@ data ToxManager k = ToxManager
86 } 88 }
87 89
88data PresenceState = forall status. PresenceState 90data PresenceState = forall status. PresenceState
89 { clients :: TVar (Map ConnectionKey ClientState) 91 { clients :: TVar (Map ClientAddress ClientState)
90 , clientsByUser :: TVar (Map Text LocalPresence) 92 , clientsByUser :: TVar (Map Text LocalPresence)
91 , clientsByProfile :: TVar (Map Text LocalPresence) 93 , clientsByProfile :: TVar (Map Text LocalPresence)
92 , remotesByPeer :: TVar (Map ConnectionKey 94 , remotesByPeer :: TVar (Map PeerAddress
93 (Map UserName RemotePresence)) 95 (Map UserName RemotePresence))
94 , server :: TMVar (XMPPServer, Connection.Manager status Text) 96 , server :: TMVar (XMPPServer, Connection.Manager status Text)
95 , keyToChan :: TVar (Map ConnectionKey Conn) 97 , ckeyToChan :: TVar (Map ClientAddress Conn)
98 , pkeyToChan :: TVar (Map PeerAddress Conn)
96 , consoleWriter :: Maybe ConsoleWriter 99 , consoleWriter :: Maybe ConsoleWriter
97 , toxManager :: Maybe (ToxManager ConnectionKey) 100 , toxManager :: Maybe (ToxManager ClientAddress)
98 } 101 }
99 102
100 103
101newPresenceState :: Maybe ConsoleWriter 104newPresenceState :: Maybe ConsoleWriter
102 -> Maybe (PresenceState -> ToxManager ConnectionKey) 105 -> Maybe (PresenceState -> ToxManager ClientAddress)
103 -> TMVar (XMPPServer, Connection.Manager status Text) 106 -> TMVar (XMPPServer, Connection.Manager status Text)
104 -> IO PresenceState 107 -> IO PresenceState
105newPresenceState cw toxman xmpp = atomically $ do 108newPresenceState cw toxman xmpp = atomically $ do
@@ -107,13 +110,15 @@ newPresenceState cw toxman xmpp = atomically $ do
107 clientsByUser <- newTVar Map.empty 110 clientsByUser <- newTVar Map.empty
108 clientsByProfile <- newTVar Map.empty 111 clientsByProfile <- newTVar Map.empty
109 remotesByPeer <- newTVar Map.empty 112 remotesByPeer <- newTVar Map.empty
110 keyToChan <- newTVar Map.empty 113 ckeyToChan <- newTVar Map.empty
114 pkeyToChan <- newTVar Map.empty
111 let st = PresenceState 115 let st = PresenceState
112 { clients = clients 116 { clients = clients
113 , clientsByUser = clientsByUser 117 , clientsByUser = clientsByUser
114 , clientsByProfile = clientsByProfile 118 , clientsByProfile = clientsByProfile
115 , remotesByPeer = remotesByPeer 119 , remotesByPeer = remotesByPeer
116 , keyToChan = keyToChan 120 , ckeyToChan = ckeyToChan
121 , pkeyToChan = pkeyToChan
117 , server = xmpp 122 , server = xmpp
118 , consoleWriter = cw 123 , consoleWriter = cw
119 , toxManager = Nothing 124 , toxManager = Nothing
@@ -121,7 +126,7 @@ newPresenceState cw toxman xmpp = atomically $ do
121 return $ st { toxManager = fmap ($ st) toxman } 126 return $ st { toxManager = fmap ($ st) toxman }
122 127
123 128
124nameForClient :: PresenceState -> ConnectionKey -> IO Text 129nameForClient :: PresenceState -> ClientAddress -> IO Text
125nameForClient state k = do 130nameForClient state k = do
126 mc <- atomically $ do 131 mc <- atomically $ do
127 cmap <- readTVar (clients state) 132 cmap <- readTVar (clients state)
@@ -139,9 +144,8 @@ presenceHooks state verbosity mclient mpeer = XMPPServerParameters
139 { xmppChooseResourceName = chooseResourceName state 144 { xmppChooseResourceName = chooseResourceName state
140 , xmppTellClientHisName = tellClientHisName state 145 , xmppTellClientHisName = tellClientHisName state
141 , xmppTellMyNameToClient = nameForClient state 146 , xmppTellMyNameToClient = nameForClient state
142 , xmppTellMyNameToPeer = \addr -> return $ addrToText addr 147 , xmppTellMyNameToPeer = \(Local addr) -> return $ addrToText addr
143 , xmppTellPeerHisName = return . peerKeyToText 148 , xmppTellPeerHisName = return . peerKeyToText
144 , xmppTellClientNameOfPeer = flip peerKeyToResolvedName
145 , xmppNewConnection = newConn state 149 , xmppNewConnection = newConn state
146 , xmppEOF = eofConn state 150 , xmppEOF = eofConn state
147 , xmppRosterBuddies = rosterGetBuddies state 151 , xmppRosterBuddies = rosterGetBuddies state
@@ -164,7 +168,7 @@ presenceHooks state verbosity mclient mpeer = XMPPServerParameters
164 168
165 169
166data LocalPresence = LocalPresence 170data LocalPresence = LocalPresence
167 { networkClients :: Map ConnectionKey ClientState 171 { networkClients :: Map ClientAddress ClientState
168 -- TODO: loginClients 172 -- TODO: loginClients
169 } 173 }
170 174
@@ -177,18 +181,17 @@ data RemotePresence = RemotePresence
177 181
178 182
179 183
180pcSingletonNetworkClient :: ConnectionKey 184pcSingletonNetworkClient :: ClientAddress -> ClientState -> LocalPresence
181 -> ClientState -> LocalPresence
182pcSingletonNetworkClient key client = 185pcSingletonNetworkClient key client =
183 LocalPresence 186 LocalPresence
184 { networkClients = Map.singleton key client 187 { networkClients = Map.singleton key client
185 } 188 }
186 189
187pcInsertNetworkClient :: ConnectionKey -> ClientState -> LocalPresence -> LocalPresence 190pcInsertNetworkClient :: ClientAddress -> ClientState -> LocalPresence -> LocalPresence
188pcInsertNetworkClient key client pc = 191pcInsertNetworkClient key client pc =
189 pc { networkClients = Map.insert key client (networkClients pc) } 192 pc { networkClients = Map.insert key client (networkClients pc) }
190 193
191pcRemoveNewtworkClient :: ConnectionKey 194pcRemoveNewtworkClient :: ClientAddress
192 -> LocalPresence -> Maybe LocalPresence 195 -> LocalPresence -> Maybe LocalPresence
193pcRemoveNewtworkClient key pc = if pcIsEmpty pc' then Nothing 196pcRemoveNewtworkClient key pc = if pcIsEmpty pc' then Nothing
194 else Just pc' 197 else Just pc'
@@ -215,8 +218,8 @@ identifyTTY' ttypids uid inode = ttypid
215 textify (tty,pid) = (fmap lazyByteStringToText tty, pid) 218 textify (tty,pid) = (fmap lazyByteStringToText tty, pid)
216 219
217chooseResourceName :: PresenceState 220chooseResourceName :: PresenceState
218 -> ConnectionKey -> SockAddr -> Maybe Text -> Maybe Text -> IO Text 221 -> ClientAddress -> Remote SockAddr -> Maybe Text -> Maybe Text -> IO Text
219chooseResourceName state k addr clientsNameForMe desired = do 222chooseResourceName state k (Remote addr) clientsNameForMe desired = do
220 muid <- getLocalPeerCred' addr 223 muid <- getLocalPeerCred' addr
221 (mtty,pid) <- getTTYandPID muid 224 (mtty,pid) <- getTTYandPID muid
222 user <- getJabberUserForId muid 225 user <- getJabberUserForId muid
@@ -297,17 +300,17 @@ chooseResourceName state k addr clientsNameForMe desired = do
297 ) 300 )
298 muid 301 muid
299 302
300-- Perform action with 'ClientState' associated with the given 'ConnectionKey'. 303-- Perform action with 'ClientState' associated with the given 'ClientAddress'.
301-- If there is no associated 'ClientState', then perform the supplied fallback 304-- If there is no associated 'ClientState', then perform the supplied fallback
302-- action. 305-- action.
303forClient :: PresenceState -> ConnectionKey -> IO b -> (ClientState -> IO b) -> IO b 306forClient :: PresenceState -> ClientAddress -> IO b -> (ClientState -> IO b) -> IO b
304forClient state k fallback f = do 307forClient state k fallback f = do
305 mclient <- atomically $ do 308 mclient <- atomically $ do
306 cs <- readTVar (clients state) 309 cs <- readTVar (clients state)
307 return $ Map.lookup k cs 310 return $ Map.lookup k cs
308 maybe fallback f mclient 311 maybe fallback f mclient
309 312
310tellClientHisName :: PresenceState -> ConnectionKey -> IO Text 313tellClientHisName :: PresenceState -> ClientAddress -> IO Text
311tellClientHisName state k = forClient state k fallback go 314tellClientHisName state k = forClient state k fallback go
312 where 315 where
313 fallback = localJID "nobody" "." "fallback" 316 fallback = localJID "nobody" "." "fallback"
@@ -316,14 +319,14 @@ tellClientHisName state k = forClient state k fallback go
316toMapUnit :: Ord k => [k] -> Map k () 319toMapUnit :: Ord k => [k] -> Map k ()
317toMapUnit xs = Map.fromList $ map (,()) xs 320toMapUnit xs = Map.fromList $ map (,()) xs
318 321
319resolveAllPeers :: [Text] -> IO (Map SockAddr ()) 322resolveAllPeers :: [Text] -> IO (Map PeerAddress ())
320resolveAllPeers hosts = fmap (toMapUnit . concat) $ Prelude.mapM (fmap (take 1) . resolvePeer) hosts 323resolveAllPeers hosts = fmap (toMapUnit . concat) $ Prelude.mapM (fmap (take 1) . resolvePeer) hosts
321 324
322 325
323-- Read a roster file and start trying to connect to all relevent peers. 326-- Read a roster file and start trying to connect to all relevent peers.
324rosterGetStuff 327rosterGetStuff
325 :: (ConfigFiles.User -> ConfigFiles.Profile -> IO [L.ByteString]) 328 :: (ConfigFiles.User -> ConfigFiles.Profile -> IO [L.ByteString])
326 -> PresenceState -> ConnectionKey -> IO [Text] 329 -> PresenceState -> ClientAddress -> IO [Text]
327rosterGetStuff what state k = forClient state k (return []) 330rosterGetStuff what state k = forClient state k (return [])
328 $ \client -> do 331 $ \client -> do
329 jids <- configText what (clientUser client) (clientProfile client) 332 jids <- configText what (clientUser client) (clientProfile client)
@@ -335,7 +338,7 @@ rosterGetStuff what state k = forClient state k (return [])
335 -- Grok peers to associate with from the roster: 338 -- Grok peers to associate with from the roster:
336 forM_ hosts $ \host -> do 339 forM_ hosts $ \host -> do
337 -- We need either conns :: Connection.Manager TCPStatus Text 340 -- We need either conns :: Connection.Manager TCPStatus Text
338 -- or toxman :: ToxManager ConnectionKey 341 -- or toxman :: ToxManager ClientAddress
339 -- It is decided by checking hostnames for .tox ending. 342 -- It is decided by checking hostnames for .tox ending.
340 let policySetter = fromMaybe (Connection.setPolicy conns host) $ do 343 let policySetter = fromMaybe (Connection.setPolicy conns host) $ do
341 toxman <- toxManager state 344 toxman <- toxManager state
@@ -346,17 +349,17 @@ rosterGetStuff what state k = forClient state k (return [])
346 atomically $ putTMVar svVar (sv,conns) 349 atomically $ putTMVar svVar (sv,conns)
347 return jids 350 return jids
348 351
349rosterGetBuddies :: PresenceState -> ConnectionKey -> IO [Text] 352rosterGetBuddies :: PresenceState -> ClientAddress -> IO [Text]
350rosterGetBuddies state k = rosterGetStuff ConfigFiles.getBuddies state k 353rosterGetBuddies state k = rosterGetStuff ConfigFiles.getBuddies state k
351 354
352rosterGetSolicited :: PresenceState -> ConnectionKey -> IO [Text] 355rosterGetSolicited :: PresenceState -> ClientAddress -> IO [Text]
353rosterGetSolicited = rosterGetStuff ConfigFiles.getSolicited 356rosterGetSolicited = rosterGetStuff ConfigFiles.getSolicited
354 357
355-- XXX: Should we be connecting to these peers? 358-- XXX: Should we be connecting to these peers?
356rosterGetOthers :: PresenceState -> ConnectionKey -> IO [Text] 359rosterGetOthers :: PresenceState -> ClientAddress -> IO [Text]
357rosterGetOthers = rosterGetStuff ConfigFiles.getOthers 360rosterGetOthers = rosterGetStuff ConfigFiles.getOthers
358 361
359rosterGetSubscribers :: PresenceState -> ConnectionKey -> IO [Text] 362rosterGetSubscribers :: PresenceState -> ClientAddress -> IO [Text]
360rosterGetSubscribers = rosterGetStuff ConfigFiles.getSubscribers 363rosterGetSubscribers = rosterGetStuff ConfigFiles.getSubscribers
361 364
362data Conn = Conn { connChan :: TChan Stanza 365data Conn = Conn { connChan :: TChan Stanza
@@ -413,23 +416,20 @@ getBuddiesAndSolicited state pred
413 -- of (Bool,Text) for processing outside. 416 -- of (Bool,Text) for processing outside.
414 return (isbud,u,user,profile) 417 return (isbud,u,user,profile)
415 418
416sendProbesAndSolicitations :: PresenceState 419sendProbesAndSolicitations :: PresenceState -> PeerAddress -> Local SockAddr -> TChan Stanza -> IO ()
417 -> ConnectionKey -> SockAddr -> TChan Stanza -> IO () 420sendProbesAndSolicitations state k (Local laddr) chan = do
418sendProbesAndSolicitations state k laddr chan = do
419 -- get all buddies & solicited matching k for all users 421 -- get all buddies & solicited matching k for all users
420 xs <- getBuddiesAndSolicited state $ \case 422 xs <- getBuddiesAndSolicited state $ \case
421 h | ".tox" `Text.isSuffixOf` h -> return False -- Tox probes/solicitations are handled in ToxToXMPP module. 423 h | ".tox" `Text.isSuffixOf` h -> return False -- Tox probes/solicitations are handled in ToxToXMPP module.
422 h -> do 424 h -> do
423 addrs <- nub `fmap` resolvePeer h 425 addrs <- nub `fmap` resolvePeer h
424 case k of 426 return $ k `elem` addrs -- Only for this peer /k/.
425 ClientKey _ -> return False -- Solicitations and probes are only for peers.
426 PeerKey a -> return $ a `elem` addrs -- Only for this peer /k/.
427 forM_ xs $ \(isbud,u,user,profile) -> do 427 forM_ xs $ \(isbud,u,user,profile) -> do
428 let make = if isbud then presenceProbe 428 let make = if isbud then presenceProbe
429 else presenceSolicitation 429 else presenceSolicitation
430 toh = peerKeyToText k 430 toh = peerKeyToText k
431 jid = unsplitJID (u,toh,Nothing) 431 jid = unsplitJID (u,toh,Nothing)
432 me = addrToText laddr 432 me = addrToText laddr -- xmppTellMyNameToPeer
433 from = if isbud then me -- probe from server 433 from = if isbud then me -- probe from server
434 else -- solicitation from particular user 434 else -- solicitation from particular user
435 unsplitJID (Just user,me,Nothing) 435 unsplitJID (Just user,me,Nothing)
@@ -439,38 +439,35 @@ sendProbesAndSolicitations state k laddr chan = do
439 atomically $ writeTChan chan stanza 439 atomically $ writeTChan chan stanza
440 -- reverse xs `seq` return () 440 -- reverse xs `seq` return ()
441 441
442newConn :: PresenceState -> ConnectionKey -> ConnectionData -> TChan Stanza -> IO () 442
443newConn state k cdta outchan = do 443newConn :: PresenceState -> SockAddr -> ConnectionData -> TChan Stanza -> IO ()
444 atomically $ modifyTVar' (keyToChan state) 444newConn state saddr cdta outchan =
445 $ Map.insert k Conn { connChan = outchan 445 case classifyConnection saddr cdta of
446 , auxData = cdta } 446 Left (pkey,laddr) -> do
447 when (isPeerKey k) 447 atomically $ modifyTVar' (pkeyToChan state)
448 $ sendProbesAndSolicitations state k (cdAddr cdta) outchan 448 $ Map.insert pkey Conn { connChan = outchan
449 , auxData = cdta }
450 sendProbesAndSolicitations state pkey laddr outchan
451 Right (ckey,_) -> do
452 atomically $ modifyTVar' (ckeyToChan state)
453 $ Map.insert ckey Conn { connChan = outchan
454 , auxData = cdta }
449 455
450delclient :: (Alternative m, Monad m) => 456delclient :: (Alternative m, Monad m) =>
451 ConnectionKey -> m LocalPresence -> m LocalPresence 457 ClientAddress -> m LocalPresence -> m LocalPresence
452delclient k mlp = do 458delclient k mlp = do
453 lp <- mlp 459 lp <- mlp
454 let nc = Map.delete k $ networkClients lp 460 let nc = Map.delete k $ networkClients lp
455 guard $ not (Map.null nc) 461 guard $ not (Map.null nc)
456 return $ lp { networkClients = nc } 462 return $ lp { networkClients = nc }
457 463
458eofConn :: PresenceState -> ConnectionKey -> IO () 464eofConn :: PresenceState -> SockAddr -> ConnectionData -> IO ()
459eofConn state k = do 465eofConn state saddr cdta = do
460 atomically $ modifyTVar' (keyToChan state) $ Map.delete k 466 atomically $ case classifyConnection saddr cdta of
461 case k of 467 Left (pkey,_) -> modifyTVar' (pkeyToChan state) $ Map.delete pkey
462 ClientKey {} -> do 468 Right (ckey,_) -> modifyTVar' (ckeyToChan state) $ Map.delete ckey
463 forClient state k (return ()) $ \client -> do 469 case classifyConnection saddr cdta of
464 forM_ (toxManager state) $ \toxman -> do 470 Left (k,_) -> do
465 case Text.splitAt 43 (clientProfile client) of
466 (pub,".tox") -> deactivateAccount toxman k (clientProfile client)
467 _ -> return ()
468 stanza <- makePresenceStanza "jabber:server" Nothing Offline
469 informClientPresence state k stanza
470 atomically $ do
471 modifyTVar' (clientsByUser state) $ Map.alter (delclient k) (clientUser client)
472 modifyTVar' (clientsByProfile state) $ Map.alter (delclient k) (clientProfile client)
473 PeerKey {} -> do
474 let h = peerKeyToText k 471 let h = peerKeyToText k
475 jids <- atomically $ do 472 jids <- atomically $ do
476 rbp <- readTVar (remotesByPeer state) 473 rbp <- readTVar (remotesByPeer state)
@@ -482,29 +479,26 @@ eofConn state k = do
482 forM_ jids $ \jid -> do 479 forM_ jids $ \jid -> do
483 stanza <- makePresenceStanza "jabber:client" (Just jid) Offline 480 stanza <- makePresenceStanza "jabber:client" (Just jid) Offline
484 informPeerPresence state k stanza 481 informPeerPresence state k stanza
482 Right (k,_) -> do
483 forClient state k (return ()) $ \client -> do
484 forM_ (toxManager state) $ \toxman -> do
485 case Text.splitAt 43 (clientProfile client) of
486 (pub,".tox") -> deactivateAccount toxman k (clientProfile client)
487 _ -> return ()
488 stanza <- makePresenceStanza "jabber:server" Nothing Offline
489 informClientPresence state k stanza
490 atomically $ do
491 modifyTVar' (clientsByUser state) $ Map.alter (delclient k) (clientUser client)
492 modifyTVar' (clientsByProfile state) $ Map.alter (delclient k) (clientProfile client)
485 493
486-- | The given address is taken to be the local address for the socket this JID 494parseRemoteAddress :: Text -> IO (Maybe (Remote SockAddr))
487-- came in on. The returned JID parts are suitable for unsplitJID to create a 495parseRemoteAddress s = fmap Remote <$> parseAddress s
488-- valid JID for communicating to a client. The returned Bool is True when the
489-- host part refers to this local host (i.e. it equals the given SockAddr).
490-- If there are multiple results, it will prefer one which is a member of the
491-- given list in the last argument.
492rewriteJIDForClient :: SockAddr -> Text -> [Text] -> IO (Bool,(Maybe Text,Text,Maybe Text))
493rewriteJIDForClient laddr jid buds = do
494 let (n,h,r) = splitJID jid
495 maddr <- parseAddress (strip_brackets h)
496 fromMaybe (return (False,(n,ip6literal h,r))) $ maddr <&> \addr -> do
497 let mine = laddr `withPort` 0 == addr `withPort` 0
498 h' <- if mine then textHostName
499 else peerKeyToResolvedName buds (PeerKey addr)
500 return (mine,(n,h',r))
501 496
502-- This attempts to reverse resolve a peers address to give the human-friendly 497-- This attempts to reverse resolve a peers address to give the human-friendly
503-- domain name as it appears in the roster. It prefers host names that occur 498-- domain name as it appears in the roster. It prefers host names that occur
504-- in the given list of JIDs, but will fall back to any reverse-resolved name 499-- in the given list of JIDs, but will fall back to any reverse-resolved name
505-- and if it was unable to reverse the address, it will yield an ip address. 500-- and if it was unable to reverse the address, it will yield an ip address.
506peerKeyToResolvedName :: [Text] -> ConnectionKey -> IO Text 501peerKeyToResolvedName :: [Text] -> PeerAddress -> IO Text
507peerKeyToResolvedName buds k@(ClientKey { localAddress=addr }) = return "ErrorClIeNt1"
508peerKeyToResolvedName buds pk = do 502peerKeyToResolvedName buds pk = do
509 ns <- peerKeyToResolvedNames pk 503 ns <- peerKeyToResolvedNames pk
510 let hs = map (\jid -> let (_,h,_)=splitJID jid in h) buds 504 let hs = map (\jid -> let (_,h,_)=splitJID jid in h) buds
@@ -512,27 +506,39 @@ peerKeyToResolvedName buds pk = do
512 return $ fromMaybe (peerKeyToText pk) (listToMaybe ns') 506 return $ fromMaybe (peerKeyToText pk) (listToMaybe ns')
513 507
514 508
509-- | The given address is taken to be the local address for the socket this JID
510-- came in on. The returned JID parts are suitable for unsplitJID to create a
511-- valid JID for communicating to a client. The returned Bool is True when the
512-- host part refers to this local host (i.e. it equals the given SockAddr).
513-- If there are multiple results, it will prefer one which is a member of the
514-- given list in the last argument.
515rewriteJIDForClient :: Local SockAddr -> Text -> [Text] -> IO (Bool,(Maybe Text,Text,Maybe Text))
516rewriteJIDForClient (Local laddr) jid buds = do
517 let (n,h,r) = splitJID jid
518 maddr <- parseAddress (strip_brackets h)
519 fromMaybe (return (False,(n,ip6literal h,r))) $ maddr <&> \saddr -> do
520 let mine = sameAddress laddr saddr
521 h' <- if mine then textHostName
522 else peerKeyToResolvedName buds (addrToPeerKey $ Remote saddr)
523 return (mine,(n,h',r))
524
515-- Given a local address and an IP-address JID, we return True if the JID is 525-- Given a local address and an IP-address JID, we return True if the JID is
516-- local, False otherwise. Additionally, a list of equivalent hostname JIDS 526-- local, False otherwise. Additionally, a list of equivalent hostname JIDS
517-- are returned. 527-- are returned.
518multiplyJIDForClient :: SockAddr -> Text -> IO (Bool,[(Maybe Text,Text,Maybe Text)]) 528multiplyJIDForClient :: ClientAddress -> Text -> IO (Bool,[(Maybe Text,Text,Maybe Text)])
519multiplyJIDForClient laddr jid = do 529multiplyJIDForClient k jid = do
520 let (n,h,r) = splitJID jid 530 let (n,h,r) = splitJID jid
521 maddr <- parseAddress (strip_brackets h) 531 maddr <- parseAddress (strip_brackets h)
522 fromMaybe (return (False,[(n,ip6literal h,r)])) $ maddr <&> \addr -> do 532 fromMaybe (return (False,[(n,ip6literal h,r)])) $ maddr <&> \saddr -> do
523 let mine = sameAddress laddr addr 533 let Local laddr = addrFromClientKey k
534 mine = sameAddress laddr saddr
524 names <- if mine then fmap (:[]) textHostName 535 names <- if mine then fmap (:[]) textHostName
525 else peerKeyToResolvedNames (PeerKey addr) 536 else peerKeyToResolvedNames (addrToPeerKey $ Remote saddr)
526 return (mine,map (\h' -> (n,h',r)) names) 537 return (mine,map (\h' -> (n,h',r)) names)
527 538
528 539
529addrTextToKey :: Text -> IO (Maybe ConnectionKey) 540guardPortStrippedAddress :: Text -> Local SockAddr -> IO (Maybe ())
530addrTextToKey h = do 541guardPortStrippedAddress h (Local laddr) = do
531 maddr <- parseAddress (strip_brackets h)
532 return (fmap PeerKey maddr)
533
534guardPortStrippedAddress :: Text -> SockAddr -> IO (Maybe ())
535guardPortStrippedAddress h laddr = do
536 maddr <- fmap (fmap (`withPort` 0)) $ parseAddress (strip_brackets h) 542 maddr <- fmap (fmap (`withPort` 0)) $ parseAddress (strip_brackets h)
537 let laddr' = laddr `withPort` 0 543 let laddr' = laddr `withPort` 0
538 return $ maddr >>= guard . (==laddr') 544 return $ maddr >>= guard . (==laddr')
@@ -541,15 +547,15 @@ guardPortStrippedAddress h laddr = do
541-- | Accepts a textual representation of a domainname 547-- | Accepts a textual representation of a domainname
542-- JID suitable for client connections, and returns the 548-- JID suitable for client connections, and returns the
543-- coresponding ipv6 address JID suitable for peers paired 549-- coresponding ipv6 address JID suitable for peers paired
544-- with a SockAddr with the address part of that JID in 550-- with a PeerAddress with the address part of that JID in
545-- binary form. If no suitable address could be resolved 551-- binary form. If no suitable address could be resolved
546-- for the given name, Nothing is returned. 552-- for the given name, Nothing is returned.
547rewriteJIDForPeer :: Text -> IO (Maybe (Text,SockAddr)) 553rewriteJIDForPeer :: Text -> IO (Maybe (Text,PeerAddress))
548rewriteJIDForPeer jid = do 554rewriteJIDForPeer jid = do
549 let (n,h,r) = splitJID jid 555 let (n,h,r) = splitJID jid
550 maddr <- fmap listToMaybe $ resolvePeer h 556 maddr <- fmap listToMaybe $ resolvePeer h
551 return $ flip fmap maddr $ \addr -> 557 return $ flip fmap maddr $ \addr ->
552 let h' = addrToText addr 558 let h' = peerKeyToText addr
553 to' = unsplitJID (n,h',r) 559 to' = unsplitJID (n,h',r)
554 in (to',addr) 560 in (to',addr)
555 561
@@ -567,14 +573,15 @@ deliverMessage :: PresenceState
567 -> IO () 573 -> IO ()
568deliverMessage state fail msg = 574deliverMessage state fail msg =
569 case stanzaOrigin msg of 575 case stanzaOrigin msg of
570 NetworkOrigin senderk@(ClientKey {}) _ -> do 576 ClientOrigin senderk _ -> do
571 -- Case 1. Client -> Peer 577 -- Case 1. Client -> Peer
572 mto <- fmap join $ mapM rewriteJIDForPeer (stanzaTo msg) 578 mto <- fmap join $ mapM rewriteJIDForPeer (stanzaTo msg)
573 fromMaybe fail {- reverse lookup failure -} $ mto <&> \(to',addr) -> do 579 fromMaybe fail {- reverse lookup failure -} $ mto <&> \(to',k) -> do
574 let k = PeerKey addr 580 chans <- atomically $ readTVar (pkeyToChan state)
575 chans <- atomically $ readTVar (keyToChan state) 581 fromMaybe fail $ (Map.lookup k chans) <&> \Conn { connChan = chan
576 fromMaybe fail $ (Map.lookup k chans) <&> \(Conn { connChan = chan 582 , auxData = ConnectionData (Left (Local laddr))
577 , auxData = ConnectionData laddr ctyp }) -> do 583 ctyp
584 } -> do
578 (n,r) <- forClient state senderk (return (Nothing,Nothing)) 585 (n,r) <- forClient state senderk (return (Nothing,Nothing))
579 $ \c -> return (Just (clientUser c), Just (clientResource c)) 586 $ \c -> return (Just (clientUser c), Just (clientResource c))
580 -- original 'from' address is discarded. 587 -- original 'from' address is discarded.
@@ -582,11 +589,14 @@ deliverMessage state fail msg =
582 -- dup <- atomically $ cloneStanza (msg { stanzaTo=Just to', stanzaFrom=Just from' }) 589 -- dup <- atomically $ cloneStanza (msg { stanzaTo=Just to', stanzaFrom=Just from' })
583 let dup = (msg { stanzaTo=Just to', stanzaFrom=Just from' }) 590 let dup = (msg { stanzaTo=Just to', stanzaFrom=Just from' })
584 sendModifiedStanzaToPeer dup chan 591 sendModifiedStanzaToPeer dup chan
585 NetworkOrigin senderk@(PeerKey {}) _ -> do 592 PeerOrigin senderk _ -> do
586 key_to_chan <- atomically $ readTVar (keyToChan state) 593 (pchans,cchans) <- atomically $ do
587 fromMaybe fail $ (Map.lookup senderk key_to_chan) 594 pc <- readTVar (pkeyToChan state)
595 cc <- readTVar (ckeyToChan state)
596 return (pc,cc)
597 fromMaybe fail $ (Map.lookup senderk pchans)
588 <&> \(Conn { connChan = sender_chan 598 <&> \(Conn { connChan = sender_chan
589 , auxData = ConnectionData laddr ctyp }) -> do 599 , auxData = ConnectionData (Left laddr) ctyp }) -> do
590 fromMaybe fail $ (stanzaTo msg) <&> \to -> do 600 fromMaybe fail $ (stanzaTo msg) <&> \to -> do
591 (mine,(n,h,r)) <- rewriteJIDForClient laddr to [] 601 (mine,(n,h,r)) <- rewriteJIDForClient laddr to []
592 if not mine then fail else do 602 if not mine then fail else do
@@ -600,7 +610,7 @@ deliverMessage state fail msg =
600 let ks = Map.keys (networkClients presence_container) 610 let ks = Map.keys (networkClients presence_container)
601 chans = do 611 chans = do
602 (k,client) <- Map.toList $ networkClients presence_container 612 (k,client) <- Map.toList $ networkClients presence_container
603 chan <- maybeToList $ Map.lookup k key_to_chan 613 chan <- maybeToList $ Map.lookup k cchans
604 return (clientProfile client, clientUser client, chan) 614 return (clientProfile client, clientUser client, chan)
605 forM chans $ \(profile,user,chan) -> do 615 forM chans $ \(profile,user,chan) -> do
606 buds <- configText ConfigFiles.getBuddies user profile 616 buds <- configText ConfigFiles.getBuddies user profile
@@ -642,7 +652,7 @@ deliverMessage state fail msg =
642 chan 652 chan
643 653
644 654
645setClientFlag :: PresenceState -> ConnectionKey -> Int8 -> IO () 655setClientFlag :: PresenceState -> ClientAddress -> Int8 -> IO ()
646setClientFlag state k flag = 656setClientFlag state k flag =
647 atomically $ do 657 atomically $ do
648 cmap <- readTVar (clients state) 658 cmap <- readTVar (clients state)
@@ -653,12 +663,12 @@ setClientFlag0 :: ClientState -> Int8 -> STM ()
653setClientFlag0 client flag = 663setClientFlag0 client flag =
654 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag) 664 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag)
655 665
656informSentRoster :: PresenceState -> ConnectionKey -> IO () 666informSentRoster :: PresenceState -> ClientAddress -> IO ()
657informSentRoster state k = do 667informSentRoster state k = do
658 setClientFlag state k cf_interested 668 setClientFlag state k cf_interested
659 669
660 670
661subscribedPeers :: Text -> Text -> IO [SockAddr] 671subscribedPeers :: Text -> Text -> IO [PeerAddress]
662subscribedPeers user profile = do 672subscribedPeers user profile = do
663 jids <- configText ConfigFiles.getSubscribers user profile 673 jids <- configText ConfigFiles.getSubscribers user profile
664 let hosts = map ((\(_,h,_)->h) . splitJID) jids 674 let hosts = map ((\(_,h,_)->h) . splitJID) jids
@@ -667,20 +677,23 @@ subscribedPeers user profile = do
667-- | this JID is suitable for peers, not clients. 677-- | this JID is suitable for peers, not clients.
668clientJID :: Conn -> ClientState -> Text 678clientJID :: Conn -> ClientState -> Text
669clientJID con client = unsplitJID ( Just $ clientUser client 679clientJID con client = unsplitJID ( Just $ clientUser client
670 , addrToText $ cdAddr $ auxData con 680 , either (\(Local a) -> addrToText a) -- my host name, for peers
681 (error $ unlines [ "clientJID wrongly used for client connection!"
682 , "TODO: my host name for clients? nameForClient? localJID?"])
683 $ cdAddr $ auxData con
671 , Just $ clientResource client) 684 , Just $ clientResource client)
672 685
673-- | Send presence notification to subscribed peers. 686-- | Send presence notification to subscribed peers.
674-- Note that a full JID from address will be added to the 687-- Note that a full JID from address will be added to the
675-- stanza if it is not present. 688-- stanza if it is not present.
676informClientPresence :: PresenceState 689informClientPresence :: PresenceState
677 -> ConnectionKey -> StanzaWrap (LockedChan Event) -> IO () 690 -> ClientAddress -> StanzaWrap (LockedChan Event) -> IO ()
678informClientPresence state k stanza = do 691informClientPresence state k stanza = do
679 forClient state k (return ()) $ \client -> do 692 forClient state k (return ()) $ \client -> do
680 informClientPresence0 state (Just k) client stanza 693 informClientPresence0 state (Just k) client stanza
681 694
682informClientPresence0 :: PresenceState 695informClientPresence0 :: PresenceState
683 -> Maybe ConnectionKey 696 -> Maybe ClientAddress
684 -> ClientState 697 -> ClientState
685 -> StanzaWrap (LockedChan Event) 698 -> StanzaWrap (LockedChan Event)
686 -> IO () 699 -> IO ()
@@ -692,8 +705,8 @@ informClientPresence0 state mbk client stanza = do
692 atomically $ setClientFlag0 client cf_available 705 atomically $ setClientFlag0 client cf_available
693 maybe (return ()) (sendCachedPresence state) mbk 706 maybe (return ()) (sendCachedPresence state) mbk
694 addrs <- subscribedPeers (clientUser client) (clientProfile client) 707 addrs <- subscribedPeers (clientUser client) (clientProfile client)
695 ktc <- atomically $ readTVar (keyToChan state) 708 ktc <- atomically $ readTVar (pkeyToChan state)
696 let connected = mapMaybe (flip Map.lookup ktc . PeerKey) addrs 709 let connected = mapMaybe (flip Map.lookup ktc) addrs
697 forM_ connected $ \con -> do 710 forM_ connected $ \con -> do
698 let from' = clientJID con client 711 let from' = clientJID con client
699 mto <- runTraversableT $ do 712 mto <- runTraversableT $ do
@@ -706,7 +719,7 @@ informClientPresence0 state mbk client stanza = do
706 (connChan con) 719 (connChan con)
707 720
708informPeerPresence :: PresenceState 721informPeerPresence :: PresenceState
709 -> ConnectionKey 722 -> PeerAddress
710 -> StanzaWrap (LockedChan Event) 723 -> StanzaWrap (LockedChan Event)
711 -> IO () 724 -> IO ()
712informPeerPresence state k stanza = do 725informPeerPresence state k stanza = do
@@ -749,7 +762,7 @@ informPeerPresence state k stanza = do
749 762
750 -- all clients, we'll filter available/authorized later 763 -- all clients, we'll filter available/authorized later
751 764
752 ktc <- readTVar (keyToChan state) 765 ktc <- readTVar (ckeyToChan state)
753 runTraversableT $ do 766 runTraversableT $ do
754 (ck,client) <- liftMT $ fmap Map.toList $ readTVar (clients state) 767 (ck,client) <- liftMT $ fmap Map.toList $ readTVar (clients state)
755 con <- liftMaybe $ Map.lookup ck ktc 768 con <- liftMaybe $ Map.lookup ck ktc
@@ -762,8 +775,7 @@ informPeerPresence state k stanza = do
762 when is_avail $ do 775 when is_avail $ do
763 putStrLn $ "reversing for client: " ++ show from 776 putStrLn $ "reversing for client: " ++ show from
764 froms <- do -- flip (maybe $ return [from]) k . const $ do 777 froms <- do -- flip (maybe $ return [from]) k . const $ do
765 let ClientKey laddr = ck 778 (_,trip) <- multiplyJIDForClient ck from
766 (_,trip) <- multiplyJIDForClient laddr from
767 return (map unsplitJID trip) 779 return (map unsplitJID trip)
768 780
769 putStrLn $ "sending to client: " ++ show (stanzaType stanza,froms) 781 putStrLn $ "sending to client: " ++ show (stanzaType stanza,froms)
@@ -777,35 +789,37 @@ consoleClients PresenceState{ consoleWriter = Just cw } = readTVar (cwClients cw
777consoleClients _ = return Map.empty 789consoleClients _ = return Map.empty
778 790
779 791
780answerProbe :: PresenceState 792answerProbe :: PresenceState -> Maybe Text -> PeerAddress -> TChan Stanza -> IO ()
781 -> Maybe Text -> ConnectionKey -> TChan Stanza -> IO ()
782answerProbe state mto k chan = do 793answerProbe state mto k chan = do
783 -- putStrLn $ "answerProbe! " ++ show (stanzaType stanza) 794 -- putStrLn $ "answerProbe! " ++ show (stanzaType stanza)
784 (ktc,cmap) <- atomically $ (,) <$> readTVar (keyToChan state) 795 ktc <- atomically $ readTVar (pkeyToChan state)
785 <*> readTVar (clients state)
786 muser <- runTraversableT $ do 796 muser <- runTraversableT $ do
787 to <- liftT $ mto 797 to <- liftT $ mto
788 conn <- liftT $ Map.lookup k ktc 798 conn <- liftT $ Map.lookup k ktc
789 let (mu,h,_) = splitJID to -- TODO: currently resource-id is ignored on presence 799 let (mu,h,_) = splitJID to -- TODO: currently resource-id is ignored on presence
790 -- probes. Is this correct? Check the spec. 800 -- probes. Is this correct? Check the spec.
791 liftMT $ guardPortStrippedAddress h (cdAddr $ auxData conn) 801 Left laddr = cdAddr $ auxData conn
802 liftMT $ guardPortStrippedAddress h laddr
792 u <- liftT mu 803 u <- liftT mu
793 let ch = addrToText (cdAddr $ auxData conn) 804 -- ORIG let ch = addrToText (auxAddr conn)
794 profile = fromMaybe "." $ clientProfile <$> Map.lookup k cmap 805 -- ORIG return (u,conn,ch)
795 return (u,profile,conn,ch) 806 let ch = addrToText a where Local a = laddr
807 return (u,conn,ch)
796 808
797 forM_ muser $ \(u,profile,conn,ch) -> do 809 forM_ muser $ \(u,conn,ch) -> do
798 810
811 profiles <- releventProfiles (cdType $ auxData conn) u
812 forM_ profiles $ \profile -> do
813
814 -- only subscribed peers should get probe replies
799 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers u profile 815 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers u profile
800 let gaddrs = groupBy (\a b -> snd a == snd b) (sort resolved_subs) 816 let gaddrs = groupBy sameHost (sort resolved_subs)
817 sameHost a b = (snd a == snd b) -- (==) `on` snd
801 whitelist = do 818 whitelist = do
802 xs <- gaddrs 819 xs <- gaddrs -- group of subscribed jids on the same host
803 x <- take 1 xs 820 x <- take 1 xs -- the host from the group
804 guard $ snd x==k 821 guard $ snd x==k -- only hosts matching the key /k/
805 mapMaybe fst xs 822 mapMaybe fst xs -- all users subscribed at the remote peer /k/
806
807 -- -- only subscribed peers should get probe replies
808 -- addrs <- subscribedPeers u
809 823
810 -- TODO: notify remote peer that they are unsubscribed? 824 -- TODO: notify remote peer that they are unsubscribed?
811 -- reply <- makeInformSubscription "jabber:server" to from False 825 -- reply <- makeInformSubscription "jabber:server" to from False
@@ -838,16 +852,15 @@ answerProbe state mto k chan = do
838 852
839-- Send friend requests and remote presences stored in remotesByPeer to XMPP 853-- Send friend requests and remote presences stored in remotesByPeer to XMPP
840-- clients. 854-- clients.
841sendCachedPresence :: PresenceState -> ConnectionKey -> IO () 855sendCachedPresence :: PresenceState -> ClientAddress -> IO ()
842sendCachedPresence state k = do 856sendCachedPresence state k = do
843 forClient state k (return ()) $ \client -> do 857 forClient state k (return ()) $ \client -> do
844 rbp <- atomically $ readTVar (remotesByPeer state) 858 rbp <- atomically $ readTVar (remotesByPeer state)
845 jids <- configText ConfigFiles.getBuddies (clientUser client) (clientProfile client) 859 jids <- configText ConfigFiles.getBuddies (clientUser client) (clientProfile client)
846 let hosts = map ((\(_,h,_)->h) . splitJID) jids 860 let hosts = map ((\(_,h,_)->h) . splitJID) jids
847 addrs <- resolveAllPeers hosts 861 addrs <- resolveAllPeers hosts
848 let onlines = rbp `Map.intersection` Map.mapKeys PeerKey addrs 862 let onlines = rbp `Map.intersection` addrs
849 ClientKey laddr = k 863 mcon <- atomically $ do ktc <- readTVar (ckeyToChan state)
850 mcon <- atomically $ do ktc <- readTVar (keyToChan state)
851 return $ Map.lookup k ktc 864 return $ Map.lookup k ktc
852 forM_ mcon $ \con -> do 865 forM_ mcon $ \con -> do
853 forM_ (Map.toList onlines) $ \(pk, umap) -> do 866 forM_ (Map.toList onlines) $ \(pk, umap) -> do
@@ -855,7 +868,7 @@ sendCachedPresence state k = do
855 let h = peerKeyToText pk 868 let h = peerKeyToText pk
856 forM_ (Map.toList $ resources rp) $ \(resource,stanza) -> do 869 forM_ (Map.toList $ resources rp) $ \(resource,stanza) -> do
857 let jid = unsplitJID (Just user,h,Just resource) 870 let jid = unsplitJID (Just user,h,Just resource)
858 (mine,js) <- multiplyJIDForClient laddr jid 871 (mine,js) <- multiplyJIDForClient k jid
859 forM_ js $ \jid -> do 872 forM_ js $ \jid -> do
860 let from' = unsplitJID jid 873 let from' = unsplitJID jid
861 dup <- cloneStanza stanza 874 dup <- cloneStanza stanza
@@ -883,7 +896,7 @@ addToRosterFile ::
883 -> t1) 896 -> t1)
884 -> Text -- user 897 -> Text -- user
885 -> Text -- profile 898 -> Text -- profile
886 -> Text -> [SockAddr] -> t1 899 -> Text -> [PeerAddress] -> t1
887addToRosterFile doit whose profile to addrs = 900addToRosterFile doit whose profile to addrs =
888 modifyRosterFile doit whose profile to addrs True 901 modifyRosterFile doit whose profile to addrs True
889 902
@@ -895,7 +908,7 @@ removeFromRosterFile ::
895 -> t1) 908 -> t1)
896 -> Text -- user 909 -> Text -- user
897 -> Text -- profile 910 -> Text -- profile
898 -> Text -> [SockAddr] -> t1 911 -> Text -> [PeerAddress] -> t1
899removeFromRosterFile doit whose profile to addrs = 912removeFromRosterFile doit whose profile to addrs =
900 modifyRosterFile doit whose profile to addrs False 913 modifyRosterFile doit whose profile to addrs False
901 914
@@ -920,7 +933,7 @@ modifyRosterFile ::
920 -> Text -- ^ user 933 -> Text -- ^ user
921 -> Text -- ^ profile 934 -> Text -- ^ profile
922 -> Text -- ^ JID that will be added or removed a hostname 935 -> Text -- ^ JID that will be added or removed a hostname
923 -> [SockAddr] -- ^ Alias addresses for hostname in the JID. 936 -> [PeerAddress] -- ^ Alias addresses for hostname in the JID.
924 -> Bool -- ^ True if adding, otherwise False 937 -> Bool -- ^ True if adding, otherwise False
925 -> t1 938 -> t1
926modifyRosterFile doit whose profile to addrs bAdd = do 939modifyRosterFile doit whose profile to addrs bAdd = do
@@ -951,7 +964,7 @@ modifyRosterFile doit whose profile to addrs bAdd = do
951 (guard bAdd >> Just (textToLazyByteString to)) 964 (guard bAdd >> Just (textToLazyByteString to))
952 965
953 966
954clientSubscriptionRequest :: PresenceState -> IO () -> ConnectionKey -> Stanza -> TChan Stanza -> IO () 967clientSubscriptionRequest :: PresenceState -> IO () -> ClientAddress -> Stanza -> TChan Stanza -> IO ()
955clientSubscriptionRequest state fail k stanza chan = do 968clientSubscriptionRequest state fail k stanza chan = do
956 forClient state k fail $ \client -> do 969 forClient state k fail $ \client -> do
957 fromMaybe fail $ (splitJID <$> stanzaTo stanza) <&> \(mu,h,_) -> do 970 fromMaybe fail $ (splitJID <$> stanzaTo stanza) <&> \(mu,h,_) -> do
@@ -967,7 +980,7 @@ clientSubscriptionRequest state fail k stanza chan = do
967 addToRosterFile ConfigFiles.modifySolicited cuser cprof to addrs 980 addToRosterFile ConfigFiles.modifySolicited cuser cprof to addrs
968 removeFromRosterFile ConfigFiles.modifyBuddies cuser cprof to addrs 981 removeFromRosterFile ConfigFiles.modifyBuddies cuser cprof to addrs
969 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers cuser cprof 982 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers cuser cprof
970 let is_subscribed = not . null $ [ (mu, PeerKey a) | a <- addrs ] 983 let is_subscribed = not . null $ [ (mu, a) | a <- addrs ]
971 `intersect` resolved_subs 984 `intersect` resolved_subs
972 -- subscribers: "from" 985 -- subscribers: "from"
973 -- buddies: "to" 986 -- buddies: "to"
@@ -975,16 +988,18 @@ clientSubscriptionRequest state fail k stanza chan = do
975 case state of 988 case state of
976 PresenceState { server = svVar } -> do 989 PresenceState { server = svVar } -> do
977 990
978 (ktc,(sv,conns)) <- atomically $ 991 (cktc,pktc,(sv,conns)) <- atomically $ do
979 liftM2 (,) (readTVar $ keyToChan state) 992 cktc <- readTVar $ ckeyToChan state
980 (takeTMVar svVar) 993 pktc <- readTVar $ pkeyToChan state
994 sc <- takeTMVar svVar
995 return (cktc,pktc,sc)
981 996
982 -- Update roster for each client. 997 -- Update roster for each client.
983 case stanzaType stanza of 998 case stanzaType stanza of
984 PresenceRequestSubscription True -> do 999 PresenceRequestSubscription True -> do
985 hostname <- nameForClient state k 1000 hostname <- nameForClient state k
986 let cjid = unsplitJID (Just $ clientUser client, hostname,Nothing) 1001 let cjid = unsplitJID (Just $ clientUser client, hostname,Nothing)
987 chans <- clientCons state ktc (clientUser client) 1002 chans <- clientCons state cktc (clientUser client)
988 forM_ chans $ \( Conn { connChan=chan }, client ) -> do 1003 forM_ chans $ \( Conn { connChan=chan }, client ) -> do
989 -- roster update ask="subscribe" 1004 -- roster update ask="subscribe"
990 update <- makeRosterUpdate cjid to 1005 update <- makeRosterUpdate cjid to
@@ -996,13 +1011,13 @@ clientSubscriptionRequest state fail k stanza chan = do
996 _ -> return () 1011 _ -> return ()
997 1012
998 -- Send friend request to peer. 1013 -- Send friend request to peer.
999 let dsts = ktc `Map.intersection` 1014 let dsts = pktc `Map.intersection` toMapUnit addrs
1000 Map.fromList [ (PeerKey a, ()) | a <- addrs ]
1001 forM_ (Map.toList dsts) $ \(pk,con) -> do 1015 forM_ (Map.toList dsts) $ \(pk,con) -> do
1002 -- if already connected, send solicitation ... 1016 -- if already connected, send solicitation ...
1003 -- let from = clientJID con client 1017 -- let from = clientJID con client
1004 let from = unsplitJID ( Just $ clientUser client 1018 let Left laddr = cdAddr $ auxData con
1005 , addrToText $ cdAddr $ auxData con 1019 from = unsplitJID ( Just $ clientUser client
1020 , (\(Local a) -> addrToText a) $ laddr
1006 , Nothing ) 1021 , Nothing )
1007 mb <- rewriteJIDForPeer to 1022 mb <- rewriteJIDForPeer to
1008 forM_ mb $ \(to',addr) -> do 1023 forM_ mb $ \(to',addr) -> do
@@ -1022,20 +1037,20 @@ clientSubscriptionRequest state fail k stanza chan = do
1022 1037
1023resolvedFromRoster 1038resolvedFromRoster
1024 :: (ConfigFiles.User -> ConfigFiles.Profile -> IO [L.ByteString]) 1039 :: (ConfigFiles.User -> ConfigFiles.Profile -> IO [L.ByteString])
1025 -> UserName -> Text -> IO [(Maybe UserName, ConnectionKey)] 1040 -> UserName -> Text -> IO [(Maybe UserName, PeerAddress)]
1026resolvedFromRoster doit u profile = do 1041resolvedFromRoster doit u profile = do
1027 subs <- configText doit u profile 1042 subs <- configText doit u profile
1028 runTraversableT $ do 1043 runTraversableT $ do
1029 (mu,h,_) <- liftT $ splitJID `fmap` subs 1044 (mu,h,_) <- liftT $ splitJID `fmap` subs
1030 addr <- liftMT $ fmap nub $ resolvePeer h 1045 addr <- liftMT $ fmap nub $ resolvePeer h
1031 return (mu,PeerKey addr) 1046 return (mu,addr)
1032 1047
1033clientCons :: PresenceState 1048clientCons :: PresenceState
1034 -> Map ConnectionKey t -> Text -> IO [(t, ClientState)] 1049 -> Map ClientAddress t -> Text -> IO [(t, ClientState)]
1035clientCons state ktc u = map snd <$> clientCons' state ktc u 1050clientCons state ktc u = map snd <$> clientCons' state ktc u
1036 1051
1037clientCons' :: PresenceState 1052clientCons' :: PresenceState
1038 -> Map ConnectionKey t -> Text -> IO [(ConnectionKey,(t, ClientState))] 1053 -> Map ClientAddress t -> Text -> IO [(ClientAddress,(t, ClientState))]
1039clientCons' state ktc u = do 1054clientCons' state ktc u = do
1040 mlp <- atomically $ do 1055 mlp <- atomically $ do
1041 cmap <- readTVar $ clientsByUser state 1056 cmap <- readTVar $ clientsByUser state
@@ -1047,7 +1062,14 @@ clientCons' state ktc u = do
1047 return (k,(con,client)) 1062 return (k,(con,client))
1048 return $ mapMaybe doit ks 1063 return $ mapMaybe doit ks
1049 1064
1050peerSubscriptionRequest :: PresenceState -> IO () -> ConnectionKey -> Stanza -> TChan Stanza -> IO () 1065releventProfiles :: ConnectionType -> Text -> IO [Text]
1066releventProfiles XMPP _ = return ["."]
1067releventProfiles ctyp user = do
1068 -- TODO: Return all the ".tox" profiles that a user has under his
1069 -- .presence/ directory.
1070 return []
1071
1072peerSubscriptionRequest :: PresenceState -> IO () -> PeerAddress -> Stanza -> TChan Stanza -> IO ()
1051peerSubscriptionRequest state fail k stanza chan = do 1073peerSubscriptionRequest state fail k stanza chan = do
1052 putStrLn $ "Handling pending subscription from remote" 1074 putStrLn $ "Handling pending subscription from remote"
1053 fromMaybe fail $ (stanzaFrom stanza) <&> \from -> do 1075 fromMaybe fail $ (stanzaFrom stanza) <&> \from -> do
@@ -1056,24 +1078,20 @@ peerSubscriptionRequest state fail k stanza chan = do
1056 (mfrom_u,from_h,_) = splitJID from 1078 (mfrom_u,from_h,_) = splitJID from
1057 to <- return $ unsplitJID (mto_u,h,Nothing) -- delete resource 1079 to <- return $ unsplitJID (mto_u,h,Nothing) -- delete resource
1058 from <- return $ unsplitJID (mfrom_u,from_h,Nothing) -- delete resource 1080 from <- return $ unsplitJID (mfrom_u,from_h,Nothing) -- delete resource
1059 (ktc,cmap) <- atomically $ (,) <$> readTVar (keyToChan state) 1081 (pktc,cktc,cmap) <- atomically $ do
1060 <*> readTVar (clients state) 1082 cktc <- readTVar (ckeyToChan state)
1061 fromMaybe fail $ (Map.lookup k ktc) 1083 pktc <- readTVar (pkeyToChan state)
1062 <&> \Conn { auxData=ConnectionData laddr ctyp } -> do 1084 cmap <- readTVar (clients state)
1085 return (pktc,cktc,cmap)
1086 fromMaybe fail $ (Map.lookup k pktc)
1087 <&> \Conn { auxData=ConnectionData (Left laddr) ctyp } -> do
1063 (mine,totup) <- rewriteJIDForClient laddr to [] 1088 (mine,totup) <- rewriteJIDForClient laddr to []
1064 if not mine then fail else do 1089 if not mine then fail else do
1065 (_,fromtup) <- rewriteJIDForClient laddr from [] 1090 (_,fromtup) <- rewriteJIDForClient laddr from []
1066 fromMaybe fail $ mto_u <&> \u -> do 1091 fromMaybe fail $ mto_u <&> \u -> do
1067 fromMaybe fail $ mfrom_u <&> \from_u -> do 1092 fromMaybe fail $ mfrom_u <&> \from_u -> do
1068 let profile = fromMaybe "." $ clientProfile <$> Map.lookup k cmap 1093 profiles <- releventProfiles ctyp u
1069 -- XXX profile is wrong. 1094 forM_ profiles $ \profile -> do
1070 -- TODO Likely the problem is that k is a peer ConnectionKey and of course
1071 -- will have no entry in the cmap. Thus giving "." even though it ought
1072 -- to be using a tox profile.
1073 --
1074 -- Solution 1: Only .tox peers go in a tox profile.
1075 -- Solution 2: Duplicate non .tox peers in all profiles.
1076 -- Solution 3: Only one profile is active at a time.
1077 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers u profile 1095 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers u profile
1078 let already_subscribed = elem (mfrom_u,k) resolved_subs 1096 let already_subscribed = elem (mfrom_u,k) resolved_subs
1079 is_wanted = case stanzaType stanza of 1097 is_wanted = case stanzaType stanza of
@@ -1116,7 +1134,7 @@ peerSubscriptionRequest state fail k stanza chan = do
1116 when (not already_pending) $ do 1134 when (not already_pending) $ do
1117 -- contact ∉ subscribers & contact ∉ pending --> MUST 1135 -- contact ∉ subscribers & contact ∉ pending --> MUST
1118 1136
1119 chans <- clientCons state ktc u 1137 chans <- clientCons state cktc u
1120 forM_ chans $ \( Conn { connChan=chan }, client ) -> do 1138 forM_ chans $ \( Conn { connChan=chan }, client ) -> do
1121 -- send to clients 1139 -- send to clients
1122 -- TODO: interested/available clients only? 1140 -- TODO: interested/available clients only?
@@ -1128,7 +1146,7 @@ peerSubscriptionRequest state fail k stanza chan = do
1128 1146
1129clientInformSubscription :: PresenceState 1147clientInformSubscription :: PresenceState
1130 -> IO () 1148 -> IO ()
1131 -> ConnectionKey 1149 -> ClientAddress
1132 -> StanzaWrap (LockedChan Event) 1150 -> StanzaWrap (LockedChan Event)
1133 -> IO () 1151 -> IO ()
1134clientInformSubscription state fail k stanza = do 1152clientInformSubscription state fail k stanza = do
@@ -1139,7 +1157,7 @@ clientInformSubscription state fail k stanza = do
1139 addrs <- resolvePeer h 1157 addrs <- resolvePeer h
1140 -- remove from pending 1158 -- remove from pending
1141 buds <- resolvedFromRoster ConfigFiles.getBuddies (clientUser client) (clientProfile client) 1159 buds <- resolvedFromRoster ConfigFiles.getBuddies (clientUser client) (clientProfile client)
1142 let is_buddy = not . null $ map ((mu,) . PeerKey) addrs `intersect` buds 1160 let is_buddy = not . null $ map (mu,) addrs `intersect` buds
1143 removeFromRosterFile ConfigFiles.modifyPending (clientUser client) (clientProfile client) to addrs 1161 removeFromRosterFile ConfigFiles.modifyPending (clientUser client) (clientProfile client) to addrs
1144 let (relationship,addf,remf) = 1162 let (relationship,addf,remf) =
1145 case stanzaType stanza of 1163 case stanzaType stanza of
@@ -1160,12 +1178,13 @@ clientInformSubscription state fail k stanza = do
1160 putStrLn $ "cbu = " ++ show (fmap (fmap clientPid . networkClients) cbu) 1178 putStrLn $ "cbu = " ++ show (fmap (fmap clientPid . networkClients) cbu)
1161 1179
1162 -- send roster update to clients 1180 -- send roster update to clients
1163 (clients,ktc) <- atomically $ do 1181 (clients,ktc,pktc) <- atomically $ do
1164 cbu <- readTVar (clientsByUser state) 1182 cbu <- readTVar (clientsByUser state)
1165 let mlp = Map.lookup (clientUser client) cbu 1183 let mlp = Map.lookup (clientUser client) cbu
1166 let cs = maybe [] (Map.toList . networkClients) mlp 1184 let cs = maybe [] (Map.toList . networkClients) mlp
1167 ktc <- readTVar (keyToChan state) 1185 ktc <- readTVar (ckeyToChan state)
1168 return (cs,ktc) 1186 pktc <- readTVar (pkeyToChan state)
1187 return (cs,ktc,pktc)
1169 forM_ clients $ \(ck, client) -> do 1188 forM_ clients $ \(ck, client) -> do
1170 is_intereseted <- atomically $ clientIsInterested client 1189 is_intereseted <- atomically $ clientIsInterested client
1171 putStrLn $ "clientIsInterested: "++show is_intereseted 1190 putStrLn $ "clientIsInterested: "++show is_intereseted
@@ -1179,8 +1198,8 @@ clientInformSubscription state fail k stanza = do
1179 sendModifiedStanzaToClient update (connChan con) 1198 sendModifiedStanzaToClient update (connChan con)
1180 1199
1181 -- notify peer 1200 -- notify peer
1182 let dsts = Map.fromList $ map ((,()) . PeerKey) addrs 1201 let dsts = toMapUnit addrs
1183 cdsts = ktc `Map.intersection` dsts 1202 cdsts = pktc `Map.intersection` dsts
1184 forM_ (Map.toList cdsts) $ \(pk,con) -> do 1203 forM_ (Map.toList cdsts) $ \(pk,con) -> do
1185 let from = clientJID con client 1204 let from = clientJID con client
1186 to' = unsplitJID (mu, peerKeyToText pk, Nothing) 1205 to' = unsplitJID (mu, peerKeyToText pk, Nothing)
@@ -1192,18 +1211,21 @@ clientInformSubscription state fail k stanza = do
1192 1211
1193peerInformSubscription :: PresenceState 1212peerInformSubscription :: PresenceState
1194 -> IO () 1213 -> IO ()
1195 -> ConnectionKey 1214 -> PeerAddress
1196 -> StanzaWrap (LockedChan Event) 1215 -> StanzaWrap (LockedChan Event)
1197 -> IO () 1216 -> IO ()
1198peerInformSubscription state fail k stanza = do 1217peerInformSubscription state fail k stanza = do
1199 putStrLn $ "TODO: peerInformSubscription" 1218 putStrLn $ "TODO: peerInformSubscription"
1200 -- remove from solicited 1219 -- remove from solicited
1201 fromMaybe fail $ (stanzaFrom stanza) <&> \from -> do 1220 fromMaybe fail $ (stanzaFrom stanza) <&> \from -> do
1202 (ktc,cmap) <- atomically $ (,) <$> readTVar (keyToChan state) 1221 (ktc,cktc,cmap) <- atomically $ do
1203 <*> readTVar (clients state) 1222 pktc <- readTVar (pkeyToChan state)
1223 cktc <- readTVar (ckeyToChan state)
1224 cmap <- readTVar (clients state)
1225 return (pktc,cktc,cmap)
1204 fromMaybe fail $ (Map.lookup k ktc) 1226 fromMaybe fail $ (Map.lookup k ktc)
1205 <&> \(Conn { connChan=sender_chan 1227 <&> \(Conn { connChan=sender_chan
1206 , auxData =ConnectionData laddr ctyp }) -> do 1228 , auxData =ConnectionData (Left laddr) ctyp }) -> do
1207 (_,(from_u,from_h,_)) <- rewriteJIDForClient laddr from [] 1229 (_,(from_u,from_h,_)) <- rewriteJIDForClient laddr from []
1208 let from'' = unsplitJID (from_u,from_h,Nothing) 1230 let from'' = unsplitJID (from_u,from_h,Nothing)
1209 muser = do 1231 muser = do
@@ -1215,10 +1237,11 @@ peerInformSubscription state fail k stanza = do
1215 -- This would allow us to answer anonymous probes with 'unsubscribed'. 1237 -- This would allow us to answer anonymous probes with 'unsubscribed'.
1216 fromMaybe fail $ muser <&> \user -> do 1238 fromMaybe fail $ muser <&> \user -> do
1217 addrs <- resolvePeer from_h 1239 addrs <- resolvePeer from_h
1218 let profile = fromMaybe "." $ clientProfile <$> Map.lookup k cmap 1240 profiles <- releventProfiles ctyp user
1241 forM_ profiles $ \profile -> do
1219 was_solicited <- removeFromRosterFile ConfigFiles.modifySolicited user profile from'' addrs 1242 was_solicited <- removeFromRosterFile ConfigFiles.modifySolicited user profile from'' addrs
1220 subs <- resolvedFromRoster ConfigFiles.getSubscribers user profile 1243 subs <- resolvedFromRoster ConfigFiles.getSubscribers user profile
1221 let is_sub = not . null $ map ((from_u,) . PeerKey) addrs `intersect` subs 1244 let is_sub = not . null $ map (from_u,) addrs `intersect` subs
1222 putStrLn $ "DEBUG peerInformSubscription (is_sub,typ)=" ++ show (is_sub,stanzaType stanza) 1245 putStrLn $ "DEBUG peerInformSubscription (is_sub,typ)=" ++ show (is_sub,stanzaType stanza)
1223 let (relationship,addf,remf) = 1246 let (relationship,addf,remf) =
1224 case stanzaType stanza of 1247 case stanzaType stanza of
@@ -1234,7 +1257,7 @@ peerInformSubscription state fail k stanza = do
1234 addToRosterFile addf user profile from'' addrs 1257 addToRosterFile addf user profile from'' addrs
1235 removeFromRosterFile remf user profile from'' addrs 1258 removeFromRosterFile remf user profile from'' addrs
1236 1259
1237 chans <- clientCons' state ktc user 1260 chans <- clientCons' state cktc user
1238 forM_ chans $ \(ckey,(Conn { connChan=chan }, client)) -> do 1261 forM_ chans $ \(ckey,(Conn { connChan=chan }, client)) -> do
1239 hostname <- nameForClient state ckey 1262 hostname <- nameForClient state ckey
1240 let to' = unsplitJID (Just user, hostname, Nothing) 1263 let to' = unsplitJID (Just user, hostname, Nothing)