summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xmppServer.hs91
1 files changed, 88 insertions, 3 deletions
diff --git a/xmppServer.hs b/xmppServer.hs
index 118a16b2..803b4324 100644
--- a/xmppServer.hs
+++ b/xmppServer.hs
@@ -32,7 +32,11 @@ import qualified ConfigFiles
32import Data.Maybe (maybeToList,listToMaybe,mapMaybe) 32import Data.Maybe (maybeToList,listToMaybe,mapMaybe)
33import Data.Bits 33import Data.Bits
34import Data.Int (Int8) 34import Data.Int (Int8)
35import Data.XML.Types (Event)
36import System.Posix.Types (UserID,CPid)
37import Control.Applicative
35 38
39import LockedChan (LockedChan)
36import TraversableT 40import TraversableT
37import UTmp (ProcessID,users) 41import UTmp (ProcessID,users)
38import LocalPeerCred 42import LocalPeerCred
@@ -71,8 +75,10 @@ isPeerKey k = case k of { PeerKey {} -> True ; _ -> False }
71isClientKey :: ConnectionKey -> Bool 75isClientKey :: ConnectionKey -> Bool
72isClientKey k = case k of { ClientKey {} -> True ; _ -> False } 76isClientKey k = case k of { ClientKey {} -> True ; _ -> False }
73 77
78textHostName :: IO Text
74textHostName = fmap Text.pack BSD.getHostName 79textHostName = fmap Text.pack BSD.getHostName
75 80
81localJID :: Text -> Text -> IO Text
76localJID user resource = do 82localJID user resource = do
77 hostname <- textHostName 83 hostname <- textHostName
78 return $ user <> "@" <> hostname <> "/" <> resource 84 return $ user <> "@" <> hostname <> "/" <> resource
@@ -92,19 +98,25 @@ data RemotePresence = RemotePresence
92 98
93 99
94 100
101pcSingletonNetworkClient :: ConnectionKey
102 -> ClientState -> LocalPresence
95pcSingletonNetworkClient key client = 103pcSingletonNetworkClient key client =
96 LocalPresence 104 LocalPresence
97 { networkClients = Map.singleton key client 105 { networkClients = Map.singleton key client
98 } 106 }
99 107
108pcInsertNetworkClient :: ConnectionKey -> ClientState -> LocalPresence -> LocalPresence
100pcInsertNetworkClient key client pc = 109pcInsertNetworkClient key client pc =
101 pc { networkClients = Map.insert key client (networkClients pc) } 110 pc { networkClients = Map.insert key client (networkClients pc) }
102 111
112pcRemoveNewtworkClient :: ConnectionKey
113 -> LocalPresence -> Maybe LocalPresence
103pcRemoveNewtworkClient key pc = if pcIsEmpty pc' then Nothing 114pcRemoveNewtworkClient key pc = if pcIsEmpty pc' then Nothing
104 else Just pc' 115 else Just pc'
105 where 116 where
106 pc' = pc { networkClients = Map.delete key (networkClients pc) } 117 pc' = pc { networkClients = Map.delete key (networkClients pc) }
107 118
119pcIsEmpty :: LocalPresence -> Bool
108pcIsEmpty pc = Map.null (networkClients pc) 120pcIsEmpty pc = Map.null (networkClients pc)
109 121
110 122
@@ -127,14 +139,23 @@ getConsolePids state = do
127 us <- UTmp.users 139 us <- UTmp.users
128 return $ map (\(_,tty,pid)->(lazyByteStringToText tty,pid)) us 140 return $ map (\(_,tty,pid)->(lazyByteStringToText tty,pid)) us
129 141
142lazyByteStringToText :: L.ByteString -> Text
130lazyByteStringToText = (foldr1 (<>) . map Text.decodeUtf8 . L.toChunks) 143lazyByteStringToText = (foldr1 (<>) . map Text.decodeUtf8 . L.toChunks)
144
145textToLazyByteString :: Text -> L.ByteString
131textToLazyByteString s = L.fromChunks [Text.encodeUtf8 s] 146textToLazyByteString s = L.fromChunks [Text.encodeUtf8 s]
132 147
148identifyTTY' :: [(Text, ProcessID)]
149 -> System.Posix.Types.UserID
150 -> L.ByteString
151 -> IO (Maybe Text, Maybe System.Posix.Types.CPid)
133identifyTTY' ttypids uid inode = ttypid 152identifyTTY' ttypids uid inode = ttypid
134 where ttypids' = map (\(tty,pid)->(L.fromChunks [Text.encodeUtf8 tty], pid)) ttypids 153 where ttypids' = map (\(tty,pid)->(L.fromChunks [Text.encodeUtf8 tty], pid)) ttypids
135 ttypid = fmap textify $ identifyTTY ttypids' uid inode 154 ttypid = fmap textify $ identifyTTY ttypids' uid inode
136 textify (tty,pid) = (fmap lazyByteStringToText tty, pid) 155 textify (tty,pid) = (fmap lazyByteStringToText tty, pid)
137 156
157chooseResourceName :: PresenceState
158 -> ConnectionKey -> SockAddr -> t -> IO Text
138chooseResourceName state k addr desired = do 159chooseResourceName state k addr desired = do
139 muid <- getLocalPeerCred' addr 160 muid <- getLocalPeerCred' addr
140 (mtty,pid) <- getTTYandPID muid 161 (mtty,pid) <- getTTYandPID muid
@@ -184,17 +205,21 @@ chooseResourceName state k addr desired = do
184 ) 205 )
185 muid 206 muid
186 207
208forClient :: PresenceState
209 -> ConnectionKey -> IO b -> (ClientState -> IO b) -> IO b
187forClient state k fallback f = do 210forClient state k fallback f = do
188 mclient <- atomically $ do 211 mclient <- atomically $ do
189 cs <- readTVar (clients state) 212 cs <- readTVar (clients state)
190 return $ Map.lookup k cs 213 return $ Map.lookup k cs
191 maybe fallback f mclient 214 maybe fallback f mclient
192 215
216tellClientHisName :: PresenceState -> ConnectionKey -> IO Text
193tellClientHisName state k = forClient state k fallback go 217tellClientHisName state k = forClient state k fallback go
194 where 218 where
195 fallback = localJID "nobody" "fallback" 219 fallback = localJID "nobody" "fallback"
196 go client = localJID (clientUser client) (clientResource client) 220 go client = localJID (clientUser client) (clientResource client)
197 221
222toMapUnit :: Ord k => [k] -> Map k ()
198toMapUnit xs = Map.fromList $ map (,()) xs 223toMapUnit xs = Map.fromList $ map (,()) xs
199 224
200resolveAllPeers :: [Text] -> IO (Map SockAddr ()) 225resolveAllPeers :: [Text] -> IO (Map SockAddr ())
@@ -237,6 +262,8 @@ rosterGetSubscribers = rosterGetStuff ConfigFiles.getSubscribers
237data Conn = Conn { connChan :: TChan Stanza 262data Conn = Conn { connChan :: TChan Stanza
238 , auxAddr :: SockAddr } 263 , auxAddr :: SockAddr }
239 264
265configText :: Functor f =>
266 (L.ByteString -> f [L.ByteString]) -> Text -> f [Text]
240configText what u = fmap (map lazyByteStringToText) 267configText what u = fmap (map lazyByteStringToText)
241 $ what (textToLazyByteString u) 268 $ what (textToLazyByteString u)
242 269
@@ -245,6 +272,8 @@ getBuddies' = configText ConfigFiles.getBuddies
245getSolicited' :: Text -> IO [Text] 272getSolicited' :: Text -> IO [Text]
246getSolicited' = configText ConfigFiles.getSolicited 273getSolicited' = configText ConfigFiles.getSolicited
247 274
275sendProbesAndSolicitations :: PresenceState
276 -> ConnectionKey -> SockAddr -> TChan Stanza -> IO ()
248sendProbesAndSolicitations state k laddr chan = do 277sendProbesAndSolicitations state k laddr chan = do
249 -- get all buddies & solicited matching k for all users 278 -- get all buddies & solicited matching k for all users
250 xs <- runTraversableT $ do 279 xs <- runTraversableT $ do
@@ -279,6 +308,7 @@ sendProbesAndSolicitations state k laddr chan = do
279 atomically $ writeTChan chan stanza 308 atomically $ writeTChan chan stanza
280 -- reverse xs `seq` return () 309 -- reverse xs `seq` return ()
281 310
311newConn :: PresenceState -> ConnectionKey -> SockAddr -> TChan Stanza -> IO ()
282newConn state k addr outchan = do 312newConn state k addr outchan = do
283 atomically $ modifyTVar' (keyToChan state) 313 atomically $ modifyTVar' (keyToChan state)
284 $ Map.insert k Conn { connChan = outchan 314 $ Map.insert k Conn { connChan = outchan
@@ -286,12 +316,15 @@ newConn state k addr outchan = do
286 when (isPeerKey k) 316 when (isPeerKey k)
287 $ sendProbesAndSolicitations state k addr outchan 317 $ sendProbesAndSolicitations state k addr outchan
288 318
319delclient :: (Alternative m, Monad m) =>
320 ConnectionKey -> m LocalPresence -> m LocalPresence
289delclient k mlp = do 321delclient k mlp = do
290 lp <- mlp 322 lp <- mlp
291 let nc = Map.delete k $ networkClients lp 323 let nc = Map.delete k $ networkClients lp
292 guard $ not (Map.null nc) 324 guard $ not (Map.null nc)
293 return $ lp { networkClients = nc } 325 return $ lp { networkClients = nc }
294 326
327eofConn :: PresenceState -> ConnectionKey -> IO ()
295eofConn state k = do 328eofConn state k = do
296 atomically $ modifyTVar' (keyToChan state) $ Map.delete k 329 atomically $ modifyTVar' (keyToChan state) $ Map.delete k
297 case k of 330 case k of
@@ -325,9 +358,6 @@ rewriteJIDForClient1 jid = do
325 return $ Just ((n,h',r), addr) 358 return $ Just ((n,h',r), addr)
326-} 359-}
327 360
328todo = error "Unimplemented"
329
330
331-- | for example: 2001-db8-85a3-8d3-1319-8a2e-370-7348.ipv6-literal.net 361-- | for example: 2001-db8-85a3-8d3-1319-8a2e-370-7348.ipv6-literal.net
332ip6literal :: Text -> Text 362ip6literal :: Text -> Text
333ip6literal addr = Text.map dash addr <> ".ipv6-literal.net" 363ip6literal addr = Text.map dash addr <> ".ipv6-literal.net"
@@ -351,6 +381,7 @@ rewriteJIDForClient laddr jid buds = do
351 else peerKeyToResolvedName buds (PeerKey addr) 381 else peerKeyToResolvedName buds (PeerKey addr)
352 return (mine,(n,h',r)) 382 return (mine,(n,h',r))
353 383
384sameAddress :: SockAddr -> SockAddr -> Bool
354sameAddress laddr addr = laddr `withPort` 0 == addr `withPort` 0 385sameAddress laddr addr = laddr `withPort` 0 == addr `withPort` 0
355 386
356peerKeyToResolvedName :: [Text] -> ConnectionKey -> IO Text 387peerKeyToResolvedName :: [Text] -> ConnectionKey -> IO Text
@@ -373,10 +404,12 @@ multiplyJIDForClient laddr jid = do
373 return (mine,map (\h' -> (n,h',r)) names) 404 return (mine,map (\h' -> (n,h',r)) names)
374 405
375 406
407addrTextToKey :: Text -> IO (Maybe ConnectionKey)
376addrTextToKey h = do 408addrTextToKey h = do
377 maddr <- parseAddress (strip_brackets h) 409 maddr <- parseAddress (strip_brackets h)
378 return (fmap PeerKey maddr) 410 return (fmap PeerKey maddr)
379 411
412guardPortStrippedAddress :: Text -> SockAddr -> IO (Maybe ())
380guardPortStrippedAddress h laddr = do 413guardPortStrippedAddress h laddr = do
381 maddr <- fmap (fmap (`withPort` 0)) $ parseAddress (strip_brackets h) 414 maddr <- fmap (fmap (`withPort` 0)) $ parseAddress (strip_brackets h)
382 let laddr' = laddr `withPort` 0 415 let laddr' = laddr `withPort` 0
@@ -398,6 +431,7 @@ rewriteJIDForPeer jid = do
398 to' = unsplitJID (n,h',r) 431 to' = unsplitJID (n,h',r)
399 in (to',addr) 432 in (to',addr)
400 433
434deliverToConsole :: PresenceState -> IO () -> Stanza -> IO ()
401deliverToConsole state fail msg = do 435deliverToConsole state fail msg = do
402 putStrLn $ "TODO: deliver to console" 436 putStrLn $ "TODO: deliver to console"
403 did1 <- writeActiveTTY (consoleWriter state) msg 437 did1 <- writeActiveTTY (consoleWriter state) msg
@@ -405,6 +439,10 @@ deliverToConsole state fail msg = do
405 if not (did1 || did2) then fail else return () 439 if not (did1 || did2) then fail else return ()
406 440
407-- | deliver <message/> or error stanza 441-- | deliver <message/> or error stanza
442deliverMessage :: PresenceState
443 -> IO ()
444 -> StanzaWrap (LockedChan Event)
445 -> IO ()
408deliverMessage state fail msg = 446deliverMessage state fail msg =
409 case stanzaOrigin msg of 447 case stanzaOrigin msg of
410 NetworkOrigin senderk@(ClientKey {}) _ -> do 448 NetworkOrigin senderk@(ClientKey {}) _ -> do
@@ -465,25 +503,30 @@ deliverMessage state fail msg =
465 chan 503 chan
466 504
467 505
506setClientFlag :: PresenceState -> ConnectionKey -> Int8 -> IO ()
468setClientFlag state k flag = 507setClientFlag state k flag =
469 atomically $ do 508 atomically $ do
470 cmap <- readTVar (clients state) 509 cmap <- readTVar (clients state)
471 flip (maybe $ return ()) (Map.lookup k cmap) $ \client -> do 510 flip (maybe $ return ()) (Map.lookup k cmap) $ \client -> do
472 setClientFlag0 client flag 511 setClientFlag0 client flag
473 512
513setClientFlag0 :: ClientState -> Int8 -> STM ()
474setClientFlag0 client flag = 514setClientFlag0 client flag =
475 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag) 515 modifyTVar' (clientFlags client) (\flgs -> flgs .|. flag)
476 516
517informSentRoster :: PresenceState -> ConnectionKey -> IO ()
477informSentRoster state k = do 518informSentRoster state k = do
478 setClientFlag state k cf_interested 519 setClientFlag state k cf_interested
479 520
480 521
522subscribedPeers :: Text -> IO [SockAddr]
481subscribedPeers user = do 523subscribedPeers user = do
482 jids <- configText ConfigFiles.getSubscribers user 524 jids <- configText ConfigFiles.getSubscribers user
483 let hosts = map ((\(_,h,_)->h) . splitJID) jids 525 let hosts = map ((\(_,h,_)->h) . splitJID) jids
484 fmap Map.keys $ resolveAllPeers hosts 526 fmap Map.keys $ resolveAllPeers hosts
485 527
486-- | this JID is suitable for peers, not clients. 528-- | this JID is suitable for peers, not clients.
529clientJID :: Conn -> ClientState -> Text
487clientJID con client = unsplitJID ( Just $ clientUser client 530clientJID con client = unsplitJID ( Just $ clientUser client
488 , addrToText $ auxAddr con 531 , addrToText $ auxAddr con
489 , Just $ clientResource client) 532 , Just $ clientResource client)
@@ -491,10 +534,17 @@ clientJID con client = unsplitJID ( Just $ clientUser client
491-- | Send presence notification to subscribed peers. 534-- | Send presence notification to subscribed peers.
492-- Note that a full JID from address will be added to the 535-- Note that a full JID from address will be added to the
493-- stanza if it is not present. 536-- stanza if it is not present.
537informClientPresence :: PresenceState
538 -> ConnectionKey -> StanzaWrap (LockedChan Event) -> IO ()
494informClientPresence state k stanza = do 539informClientPresence state k stanza = do
495 forClient state k (return ()) $ \client -> do 540 forClient state k (return ()) $ \client -> do
496 informClientPresence0 state (Just k) client stanza 541 informClientPresence0 state (Just k) client stanza
497 542
543informClientPresence0 :: PresenceState
544 -> Maybe ConnectionKey
545 -> ClientState
546 -> StanzaWrap (LockedChan Event)
547 -> IO ()
498informClientPresence0 state mbk client stanza = do 548informClientPresence0 state mbk client stanza = do
499 dup <- cloneStanza stanza 549 dup <- cloneStanza stanza
500 atomically $ writeTVar (clientStatus client) $ Just dup 550 atomically $ writeTVar (clientStatus client) $ Just dup
@@ -516,6 +566,10 @@ informClientPresence0 state mbk client stanza = do
516 , stanzaTo = mto } 566 , stanzaTo = mto }
517 (connChan con) 567 (connChan con)
518 568
569informPeerPresence :: PresenceState
570 -> ConnectionKey
571 -> StanzaWrap (LockedChan Event)
572 -> IO ()
519informPeerPresence state k stanza = do 573informPeerPresence state k stanza = do
520 -- Presence must indicate full JID with resource... 574 -- Presence must indicate full JID with resource...
521 putStrLn $ "xmppInformPeerPresence checking from address..." 575 putStrLn $ "xmppInformPeerPresence checking from address..."
@@ -579,6 +633,8 @@ informPeerPresence state k stanza = do
579 sendModifiedStanzaToClient (dup { stanzaFrom=Just from' }) 633 sendModifiedStanzaToClient (dup { stanzaFrom=Just from' })
580 (connChan con) 634 (connChan con)
581 635
636answerProbe :: PresenceState
637 -> Maybe Text -> ConnectionKey -> TChan Stanza -> IO ()
582answerProbe state mto k chan = do 638answerProbe state mto k chan = do
583 -- putStrLn $ "answerProbe! " ++ show (stanzaType stanza) 639 -- putStrLn $ "answerProbe! " ++ show (stanzaType stanza)
584 ktc <- atomically $ readTVar (keyToChan state) 640 ktc <- atomically $ readTVar (keyToChan state)
@@ -634,6 +690,7 @@ answerProbe state mto k chan = do
634 pstanza <- makePresenceStanza "jabber:server" (Just jid) Offline 690 pstanza <- makePresenceStanza "jabber:server" (Just jid) Offline
635 atomically $ writeTChan (connChan conn) pstanza 691 atomically $ writeTChan (connChan conn) pstanza
636 692
693sendCachedPresence :: PresenceState -> ConnectionKey -> IO ()
637sendCachedPresence state k = do 694sendCachedPresence state k = do
638 forClient state k (return ()) $ \client -> do 695 forClient state k (return ()) $ \client -> do
639 rbp <- atomically $ readTVar (remotesByPeer state) 696 rbp <- atomically $ readTVar (remotesByPeer state)
@@ -671,12 +728,27 @@ sendCachedPresence state k = do
671 -- send local buddies. 728 -- send local buddies.
672 return () 729 return ()
673 730
731addToRosterFile :: (MonadPlus t, Traversable t) =>
732 (L.ByteString -> (L.ByteString -> IO (t L.ByteString))
733 -> Maybe L.ByteString
734 -> t1)
735 -> Text -> Text -> [SockAddr] -> t1
674addToRosterFile doit whose to addrs = 736addToRosterFile doit whose to addrs =
675 modifyRosterFile doit whose to addrs True 737 modifyRosterFile doit whose to addrs True
676 738
739removeFromRosterFile :: (MonadPlus t, Traversable t) =>
740 (L.ByteString -> (L.ByteString -> IO (t L.ByteString))
741 -> Maybe L.ByteString
742 -> t1)
743 -> Text -> Text -> [SockAddr] -> t1
677removeFromRosterFile doit whose to addrs = 744removeFromRosterFile doit whose to addrs =
678 modifyRosterFile doit whose to addrs False 745 modifyRosterFile doit whose to addrs False
679 746
747modifyRosterFile :: (Traversable t, MonadPlus t) =>
748 (L.ByteString -> (L.ByteString -> IO (t L.ByteString))
749 -> Maybe L.ByteString
750 -> t1)
751 -> Text -> Text -> [SockAddr] -> Bool -> t1
680modifyRosterFile doit whose to addrs bAdd = do 752modifyRosterFile doit whose to addrs bAdd = do
681 let (mu,_,_) = splitJID to 753 let (mu,_,_) = splitJID to
682 cmp jid = runTraversableT $ do 754 cmp jid = runTraversableT $ do
@@ -769,6 +841,8 @@ resolvedFromRoster doit u = do
769 addr <- liftMT $ fmap nub $ resolvePeer h 841 addr <- liftMT $ fmap nub $ resolvePeer h
770 return (mu,PeerKey addr) 842 return (mu,PeerKey addr)
771 843
844clientCons :: PresenceState
845 -> Map ConnectionKey t -> Text -> IO [(t, ClientState)]
772clientCons state ktc u = do 846clientCons state ktc u = do
773 mlp <- atomically $ do 847 mlp <- atomically $ do
774 cmap <- readTVar $ clientsByUser state 848 cmap <- readTVar $ clientsByUser state
@@ -848,6 +922,11 @@ peerSubscriptionRequest state fail k stanza chan = do
848 chan 922 chan
849 923
850 924
925clientInformSubscription :: PresenceState
926 -> IO ()
927 -> ConnectionKey
928 -> StanzaWrap (LockedChan Event)
929 -> IO ()
851clientInformSubscription state fail k stanza = do 930clientInformSubscription state fail k stanza = do
852 forClient state k fail $ \client -> do 931 forClient state k fail $ \client -> do
853 flip (maybe fail) (stanzaTo stanza) $ \to -> do 932 flip (maybe fail) (stanzaTo stanza) $ \to -> do
@@ -907,6 +986,11 @@ clientInformSubscription state fail k stanza = do
907 (connChan con) 986 (connChan con)
908 answerProbe state (Just from) pk (connChan con) 987 answerProbe state (Just from) pk (connChan con)
909 988
989peerInformSubscription :: PresenceState
990 -> IO ()
991 -> ConnectionKey
992 -> StanzaWrap (LockedChan Event)
993 -> IO ()
910peerInformSubscription state fail k stanza = do 994peerInformSubscription state fail k stanza = do
911 putStrLn $ "TODO: peerInformSubscription" 995 putStrLn $ "TODO: peerInformSubscription"
912 -- remove from solicited 996 -- remove from solicited
@@ -957,6 +1041,7 @@ peerInformSubscription state fail k stanza = do
957 , stanzaTo = Just to' } 1041 , stanzaTo = Just to' }
958 chan 1042 chan
959 1043
1044main :: IO ()
960main = runResourceT $ do 1045main = runResourceT $ do
961 args <- liftIO getArgs 1046 args <- liftIO getArgs
962 let verbosity = getSum $ flip foldMap args $ \case 1047 let verbosity = getSum $ flip foldMap args $ \case