summaryrefslogtreecommitdiff
path: root/xmppServer.hs
diff options
context:
space:
mode:
Diffstat (limited to 'xmppServer.hs')
-rw-r--r--xmppServer.hs90
1 files changed, 62 insertions, 28 deletions
diff --git a/xmppServer.hs b/xmppServer.hs
index 74adc620..5771510a 100644
--- a/xmppServer.hs
+++ b/xmppServer.hs
@@ -15,7 +15,7 @@ import Network.Socket
15 , SockAddr(..) 15 , SockAddr(..)
16 ) 16 )
17import System.Endian (fromBE32) 17import System.Endian (fromBE32)
18import Data.List (nub, (\\) ) 18import Data.List (nub, (\\), intersect )
19import Data.Monoid ( (<>) ) 19import Data.Monoid ( (<>) )
20import qualified Data.Text as Text 20import qualified Data.Text as Text
21import qualified Data.Text.IO as Text 21import qualified Data.Text.IO as Text
@@ -86,14 +86,18 @@ data ClientState = ClientState
86 , clientUser :: Text 86 , clientUser :: Text
87 , clientPid :: Maybe ProcessID 87 , clientPid :: Maybe ProcessID
88 , clientStatus :: TVar (Maybe Stanza) 88 , clientStatus :: TVar (Maybe Stanza)
89 , clientFlags :: Int8 89 , clientFlags :: TVar Int8
90 } 90 }
91 91
92-- | True if the client has sent an initial presence 92-- | True if the client has sent an initial presence
93clientIsAvailable c = clientFlags c .&. cf_available /= 0 93clientIsAvailable c = do
94 flgs <- readTVar (clientFlags c)
95 return $ flgs .&. cf_available /= 0
94 96
95-- | True if the client has requested a roster 97-- | True if the client has requested a roster
96clientIsInterested c = clientFlags c .&. cf_interested /= 0 98clientIsInterested c = do
99 flgs <- readTVar (clientFlags c)
100 return $ flgs .&. cf_interested /= 0
97 101
98data LocalPresence = LocalPresence 102data LocalPresence = LocalPresence
99 { networkClients :: Map ConnectionKey ClientState 103 { networkClients :: Map ConnectionKey ClientState
@@ -171,11 +175,12 @@ chooseResourceName state k addr desired = do
171 (mtty,pid) <- getTTYandPID muid 175 (mtty,pid) <- getTTYandPID muid
172 user <- getJabberUserForId muid 176 user <- getJabberUserForId muid
173 status <- atomically $ newTVar Nothing 177 status <- atomically $ newTVar Nothing
178 flgs <- atomically $ newTVar 0
174 let client = ClientState { clientResource = maybe "fallback" id mtty 179 let client = ClientState { clientResource = maybe "fallback" id mtty
175 , clientUser = user 180 , clientUser = user
176 , clientPid = pid 181 , clientPid = pid
177 , clientStatus = status 182 , clientStatus = status
178 , clientFlags = 0 } 183 , clientFlags = flgs }
179 184
180 atomically $ do 185 atomically $ do
181 modifyTVar' (clients state) $ Map.insert k client 186 modifyTVar' (clients state) $ Map.insert k client
@@ -310,12 +315,22 @@ newConn state k addr outchan = do
310 when (isPeerKey k) 315 when (isPeerKey k)
311 $ sendProbesAndSolicitations state k addr outchan 316 $ sendProbesAndSolicitations state k addr outchan
312 317
318delclient k mlp = do
319 lp <- mlp
320 let nc = Map.delete k $ networkClients lp
321 guard $ not (Map.null nc)
322 return $ lp { networkClients = nc }
323
313eofConn state k = do 324eofConn state k = do
314 atomically $ modifyTVar' (keyToChan state) $ Map.delete k 325 atomically $ modifyTVar' (keyToChan state) $ Map.delete k
315 case k of 326 case k of
316 ClientKey {} -> do 327 ClientKey {} -> do
328 forClient state k (return ()) $ \client -> do
317 stanza <- makePresenceStanza "jabber:server" Nothing Offline 329 stanza <- makePresenceStanza "jabber:server" Nothing Offline
318 informClientPresence state k stanza 330 informClientPresence state k stanza
331 atomically $ do
332 modifyTVar' (clientsByUser state)
333 $ Map.alter (delclient k) (clientUser client)
319 PeerKey {} -> do 334 PeerKey {} -> do
320 let h = peerKeyToText k 335 let h = peerKeyToText k
321 jids <- atomically $ do 336 jids <- atomically $ do
@@ -465,12 +480,13 @@ deliverMessage state fail msg =
465 480
466 481
467setClientFlag state k flag = 482setClientFlag state k flag =
468 atomically $ modifyTVar' (clients state) 483 atomically $ do
469 $ Map.adjust 484 cmap <- readTVar (clients state)
470 (\c -> c { clientFlags = clientFlags c .|. flag }) 485 flip (maybe $ return ()) (Map.lookup k cmap) $ \client -> do
471 k 486 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag)
472 487
473informSentRoster state k = setClientFlag state k cf_interested 488informSentRoster state k = do
489 setClientFlag state k cf_interested
474 490
475 491
476subscribedPeers user = do 492subscribedPeers user = do
@@ -493,7 +509,8 @@ informClientPresence state k stanza = do
493 flip (maybe $ return ()) mb $ \cstate -> do 509 flip (maybe $ return ()) mb $ \cstate -> do
494 writeTVar (clientStatus cstate) $ Just dup 510 writeTVar (clientStatus cstate) $ Just dup
495 forClient state k (return ()) $ \client -> do 511 forClient state k (return ()) $ \client -> do
496 when (not $ clientIsAvailable client) $ do 512 is_avail <- atomically $ clientIsAvailable client
513 when (not is_avail) $ do
497 setClientFlag state k cf_available 514 setClientFlag state k cf_available
498 sendCachedPresence state k 515 sendCachedPresence state k
499 addrs <- subscribedPeers (clientUser client) 516 addrs <- subscribedPeers (clientUser client)
@@ -546,7 +563,8 @@ informPeerPresence state k stanza = do
546 forM_ clients $ \(ck,con,client) -> do 563 forM_ clients $ \(ck,con,client) -> do
547 -- (TODO: appropriately authorized clients only.) 564 -- (TODO: appropriately authorized clients only.)
548 -- For now, all "available" clients (available = sent initial presence) 565 -- For now, all "available" clients (available = sent initial presence)
549 when (clientIsAvailable client) $ do 566 is_avail <- atomically $ clientIsAvailable client
567 when is_avail $ do
550 froms <- do 568 froms <- do
551 let ClientKey laddr = ck 569 let ClientKey laddr = ck
552 (_,trip) <- multiplyJIDForClient laddr from 570 (_,trip) <- multiplyJIDForClient laddr from
@@ -557,11 +575,11 @@ informPeerPresence state k stanza = do
557 sendModifiedStanzaToClient (dup { stanzaFrom=Just from' }) 575 sendModifiedStanzaToClient (dup { stanzaFrom=Just from' })
558 (connChan con) 576 (connChan con)
559 577
560answerProbe state k stanza chan = do 578answerProbe state mto k chan = do
561 -- putStrLn $ "answerProbe! " ++ show (stanzaType stanza) 579 -- putStrLn $ "answerProbe! " ++ show (stanzaType stanza)
562 ktc <- atomically $ readTVar (keyToChan state) 580 ktc <- atomically $ readTVar (keyToChan state)
563 muser <- runTraversableT $ do 581 muser <- runTraversableT $ do
564 to <- liftT $ stanzaTo stanza 582 to <- liftT $ mto
565 conn <- liftT $ Map.lookup k ktc 583 conn <- liftT $ Map.lookup k ktc
566 let (mu,h,_) = splitJID to -- TODO: currently resource-id is ignored on presence 584 let (mu,h,_) = splitJID to -- TODO: currently resource-id is ignored on presence
567 -- probes. Is this correct? Check the spec. 585 -- probes. Is this correct? Check the spec.
@@ -665,6 +683,11 @@ clientSubscriptionRequest state fail k stanza chan = do
665 if null addrs then fail else do 683 if null addrs then fail else do
666 -- add to-address to from's solicited 684 -- add to-address to from's solicited
667 addToRosterFile ConfigFiles.modifySolicited (clientUser client) to addrs 685 addToRosterFile ConfigFiles.modifySolicited (clientUser client) to addrs
686 removeFromRosterFile ConfigFiles.modifyBuddies (clientUser client) to addrs
687 resolved_subs <- resolvedFromRoster ConfigFiles.getSubscribers (clientUser client)
688 let is_subscribed = not . null $ intersect (map ((mu,).PeerKey) addrs) resolved_subs
689 -- subscribers: "from"
690 -- buddies: "to"
668 691
669 (ktc,ap) <- atomically $ 692 (ktc,ap) <- atomically $
670 liftM2 (,) (readTVar $ keyToChan state) 693 liftM2 (,) (readTVar $ keyToChan state)
@@ -677,7 +700,11 @@ clientSubscriptionRequest state fail k stanza chan = do
677 chans <- clientCons state ktc (clientUser client) 700 chans <- clientCons state ktc (clientUser client)
678 forM_ chans $ \( Conn { connChan=chan }, client ) -> do 701 forM_ chans $ \( Conn { connChan=chan }, client ) -> do
679 -- roster update ask="subscribe" 702 -- roster update ask="subscribe"
680 update <- makeRosterUpdate cjid to ("ask","subscribe") 703 update <- makeRosterUpdate cjid to
704 [ ("ask","subscribe")
705 , if is_subscribed then ("subscription","from")
706 else ("subscription","none")
707 ]
681 sendModifiedStanzaToClient update chan 708 sendModifiedStanzaToClient update chan
682 _ -> return () 709 _ -> return ()
683 710
@@ -700,6 +727,7 @@ clientSubscriptionRequest state fail k stanza chan = do
700 -- Add peer if we are not already associated ... 727 -- Add peer if we are not already associated ...
701 sv <- atomically $ takeTMVar $ server state 728 sv <- atomically $ takeTMVar $ server state
702 addPeer sv (head addrs) 729 addPeer sv (head addrs)
730 atomically $ putTMVar (server state) sv
703 731
704 732
705resolvedFromRoster 733resolvedFromRoster
@@ -755,6 +783,7 @@ peerSubscriptionRequest state fail k stanza chan = do
755 -- (note: swapping to and from for reply) 783 -- (note: swapping to and from for reply)
756 reply <- makeInformSubscription "jabber:server" to from is_wanted 784 reply <- makeInformSubscription "jabber:server" to from is_wanted
757 sendModifiedStanzaToPeer reply chan 785 sendModifiedStanzaToPeer reply chan
786 answerProbe state (Just to) k chan
758 else do 787 else do
759 788
760 -- TODO: if peer-connection is to self, then auto-approve local user. 789 -- TODO: if peer-connection is to self, then auto-approve local user.
@@ -797,7 +826,7 @@ clientInformSubscription state fail k stanza = do
797 addrs <- resolvePeer h 826 addrs <- resolvePeer h
798 -- remove from pending 827 -- remove from pending
799 buds <- resolvedFromRoster ConfigFiles.getBuddies (clientUser client) 828 buds <- resolvedFromRoster ConfigFiles.getBuddies (clientUser client)
800 let is_buddy = not . null $ map ((mu,) . PeerKey) addrs \\ buds 829 let is_buddy = not . null $ map ((mu,) . PeerKey) addrs `intersect` buds
801 removeFromRosterFile ConfigFiles.modifyPending (clientUser client) to addrs 830 removeFromRosterFile ConfigFiles.modifyPending (clientUser client) to addrs
802 let (relationship,addf,remf) = 831 let (relationship,addf,remf) =
803 case stanzaType stanza of 832 case stanzaType stanza of
@@ -813,20 +842,27 @@ clientInformSubscription state fail k stanza = do
813 addToRosterFile addf (clientUser client) to addrs 842 addToRosterFile addf (clientUser client) to addrs
814 removeFromRosterFile remf (clientUser client) to addrs 843 removeFromRosterFile remf (clientUser client) to addrs
815 844
845 do
846 cbu <- atomically $ readTVar (clientsByUser state)
847 putStrLn $ "cbu = " ++ show (fmap (fmap clientPid . networkClients) cbu)
848
816 -- send roster update to clients 849 -- send roster update to clients
817 (clients,ktc) <- atomically $ do 850 (clients,ktc) <- atomically $ do
818 cbu <- readTVar (clientsByUser state) 851 cbu <- readTVar (clientsByUser state)
819 let mlp = mu >>= flip Map.lookup cbu 852 let mlp = Map.lookup (clientUser client) cbu
820 let cs = maybe [] (Map.toList . networkClients) mlp 853 let cs = maybe [] (Map.toList . networkClients) mlp
821 ktc <- readTVar (keyToChan state) 854 ktc <- readTVar (keyToChan state)
822 return (cs,ktc) 855 return (cs,ktc)
823 forM_ clients $ \(ck, client) -> do 856 forM_ clients $ \(ck, client) -> do
824 when (clientIsInterested client) $ do 857 is_intereseted <- atomically $ clientIsInterested client
858 putStrLn $ "clientIsInterested: "++show is_intereseted
859 is_intereseted <- atomically $ clientIsInterested client
860 when is_intereseted $ do
825 flip (maybe $ return ()) (Map.lookup ck ktc) $ \con -> do 861 flip (maybe $ return ()) (Map.lookup ck ktc) $ \con -> do
826 hostname <- textHostName 862 hostname <- textHostName
827 -- TODO: Should cjid include the resource? 863 -- TODO: Should cjid include the resource?
828 let cjid = unsplitJID (mu, hostname, Nothing) 864 let cjid = unsplitJID (mu, hostname, Nothing)
829 update <- makeRosterUpdate cjid to relationship 865 update <- makeRosterUpdate cjid to [relationship]
830 sendModifiedStanzaToClient update (connChan con) 866 sendModifiedStanzaToClient update (connChan con)
831 867
832 -- notify peer 868 -- notify peer
@@ -839,6 +875,7 @@ clientInformSubscription state fail k stanza = do
839 sendModifiedStanzaToPeer (dup { stanzaTo = Just $ to' 875 sendModifiedStanzaToPeer (dup { stanzaTo = Just $ to'
840 , stanzaFrom = Just from }) 876 , stanzaFrom = Just from })
841 (connChan con) 877 (connChan con)
878 answerProbe state (Just from) pk (connChan con)
842 879
843peerInformSubscription state fail k stanza = do 880peerInformSubscription state fail k stanza = do
844 putStrLn $ "TODO: peerInformSubscription" 881 putStrLn $ "TODO: peerInformSubscription"
@@ -858,7 +895,7 @@ peerInformSubscription state fail k stanza = do
858 addrs <- resolvePeer from_h 895 addrs <- resolvePeer from_h
859 was_solicited <- removeFromRosterFile ConfigFiles.modifySolicited user from'' addrs 896 was_solicited <- removeFromRosterFile ConfigFiles.modifySolicited user from'' addrs
860 subs <- resolvedFromRoster ConfigFiles.getSubscribers user 897 subs <- resolvedFromRoster ConfigFiles.getSubscribers user
861 let is_sub = not . null $ map ((from_u,) . PeerKey) addrs \\ subs 898 let is_sub = not . null $ map ((from_u,) . PeerKey) addrs `intersect` subs
862 let (relationship,addf,remf) = 899 let (relationship,addf,remf) =
863 case stanzaType stanza of 900 case stanzaType stanza of
864 PresenceInformSubscription True -> 901 PresenceInformSubscription True ->
@@ -873,20 +910,17 @@ peerInformSubscription state fail k stanza = do
873 addToRosterFile addf user from'' addrs 910 addToRosterFile addf user from'' addrs
874 removeFromRosterFile remf user from'' addrs 911 removeFromRosterFile remf user from'' addrs
875 912
876 ktc <- atomically $ readTVar (keyToChan state)
877 flip (maybe fail) (Map.lookup k ktc)
878 $ \Conn { auxAddr=laddr } -> do
879 hostname <- textHostName 913 hostname <- textHostName
880 let to' = unsplitJID (Just user, hostname, Nothing) 914 let to' = unsplitJID (Just user, hostname, Nothing)
881 (_,fromtup) <- rewriteJIDForClient laddr from
882 chans <- clientCons state ktc user 915 chans <- clientCons state ktc user
883 forM_ chans $ \(Conn { connChan=chan }, client) -> do 916 forM_ chans $ \(Conn { connChan=chan }, client) -> do
884 update <- makeRosterUpdate to' from relationship 917 update <- makeRosterUpdate to' from'' [relationship]
885 when (clientIsInterested client) $ do 918 is_intereseted <- atomically $ clientIsInterested client
919 when is_intereseted $ do
886 sendModifiedStanzaToClient update chan 920 sendModifiedStanzaToClient update chan
887 -- TODO: interested/availabe clients only? 921 -- TODO: interested/availabe clients only?
888 dup <- cloneStanza stanza 922 dup <- cloneStanza stanza
889 sendModifiedStanzaToClient dup { stanzaFrom = Just $ unsplitJID fromtup 923 sendModifiedStanzaToClient dup { stanzaFrom = Just $ from''
890 , stanzaTo = Just to' } 924 , stanzaTo = Just to' }
891 chan 925 chan
892 926
@@ -924,7 +958,7 @@ main = runResourceT $ do
924 , xmppDeliverMessage = deliverMessage state 958 , xmppDeliverMessage = deliverMessage state
925 , xmppInformClientPresence = informClientPresence state 959 , xmppInformClientPresence = informClientPresence state
926 , xmppInformPeerPresence = informPeerPresence state 960 , xmppInformPeerPresence = informPeerPresence state
927 , xmppAnswerProbe = answerProbe state 961 , xmppAnswerProbe = \k stanza chan -> answerProbe state (stanzaTo stanza) k chan
928 , xmppClientSubscriptionRequest = clientSubscriptionRequest state 962 , xmppClientSubscriptionRequest = clientSubscriptionRequest state
929 , xmppPeerSubscriptionRequest = peerSubscriptionRequest state 963 , xmppPeerSubscriptionRequest = peerSubscriptionRequest state
930 , xmppClientInformSubscription = clientInformSubscription state 964 , xmppClientInformSubscription = clientInformSubscription state