summaryrefslogtreecommitdiff
path: root/Presence/XMPPServer.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Presence/XMPPServer.hs')
-rw-r--r--Presence/XMPPServer.hs157
1 files changed, 86 insertions, 71 deletions
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs
index 7eb0fbc5..d41e06cb 100644
--- a/Presence/XMPPServer.hs
+++ b/Presence/XMPPServer.hs
@@ -1,5 +1,6 @@
1{-# LANGUAGE CPP #-} 1{-# LANGUAGE CPP #-}
2{-# LANGUAGE OverloadedStrings #-} 2{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE RankNTypes #-}
3module XMPPServer 4module XMPPServer
4 ( xmppServer 5 ( xmppServer
5 , ConnectionKey(..) 6 , ConnectionKey(..)
@@ -18,6 +19,7 @@ module XMPPServer
18 , sendModifiedStanzaToPeer 19 , sendModifiedStanzaToPeer
19 , sendModifiedStanzaToClient 20 , sendModifiedStanzaToClient
20 ) where 21 ) where
22
21import Debug.Trace 23import Debug.Trace
22import Control.Monad.Trans.Resource (runResourceT) 24import Control.Monad.Trans.Resource (runResourceT)
23import Control.Monad.Trans (lift) 25import Control.Monad.Trans (lift)
@@ -28,7 +30,6 @@ import Control.Concurrent (forkIO)
28import Control.Concurrent.STM 30import Control.Concurrent.STM
29-- import Control.Concurrent.STM.TChan 31-- import Control.Concurrent.STM.TChan
30import Network.Socket 32import Network.Socket
31import XMPPTypes (withPort)
32import Text.Printf 33import Text.Printf
33import System.Posix.Signals 34import System.Posix.Signals
34import Data.ByteString (ByteString) 35import Data.ByteString (ByteString)
@@ -55,17 +56,26 @@ import Data.Set (Set, (\\) )
55import qualified Data.Set as Set 56import qualified Data.Set as Set
56import qualified System.Random 57import qualified System.Random
57import qualified Network.BSD as BSD 58import qualified Network.BSD as BSD
59import Data.Void (Void)
58 60
59import GetHostByAddr (getHostByAddr) 61import GetHostByAddr (getHostByAddr)
60import qualified Control.Concurrent.STM.UpdateStream as Slotted 62import qualified Control.Concurrent.STM.UpdateStream as Slotted
61import ControlMaybe
62import Nesting 63import Nesting
63import EventUtil
64import Server 64import Server
65import EventUtil
66import ControlMaybe
65 67
68withPort :: SockAddr -> Int -> SockAddr
69withPort (SockAddrInet _ a) port = SockAddrInet (toEnum port) a
70withPort (SockAddrInet6 _ a b c) port = SockAddrInet6 (toEnum port) a b c
71
72
73peerport :: PortNumber
66peerport = 5269 74peerport = 5269
75clientport :: PortNumber
67clientport = 5222 76clientport = 5222
68 77
78my_uuid :: Text
69my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" 79my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574"
70 80
71data ConnectionKey 81data ConnectionKey
@@ -164,6 +174,7 @@ data XMPPServerParameters =
164 , xmppInformClientPresence :: ConnectionKey -> Stanza -> IO () 174 , xmppInformClientPresence :: ConnectionKey -> Stanza -> IO ()
165 } 175 }
166 176
177
167-- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error 178-- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error
168-- client connection 179-- client connection
169-- socat script to send stanza fragment 180-- socat script to send stanza fragment
@@ -252,6 +263,7 @@ copyToChannel f chan closer_stack = awaitForever copy
252 yield x 263 yield x
253 264
254 265
266prettyPrint :: ByteString -> ConduitM Event Void IO ()
255prettyPrint prefix = 267prettyPrint prefix =
256 XML.renderBytes (XML.def { XML.rsPretty=True }) 268 XML.renderBytes (XML.def { XML.rsPretty=True })
257 =$= CB.lines 269 =$= CB.lines
@@ -279,6 +291,44 @@ fixHeaders Stanza { stanzaTo=mto, stanzaFrom=mfrom } = do
279 as' = maybe as (\to->attr "to" to:as) mto 291 as' = maybe as (\to->attr "to" to:as) mto
280 as'' = maybe as' (\from->attr "from" from:as') mfrom 292 as'' = maybe as' (\from->attr "from" from:as') mfrom
281 293
294conduitToChan
295 :: Conduit () IO Event
296 -> IO (TChan Event, TVar (Maybe [Event]), TMVar a)
297conduitToChan c = do
298 chan <- atomically newTChan
299 clsrs <- atomically $ newTVar (Just [])
300 quitvar <- atomically $ newEmptyTMVar
301 forkIO $ do
302 c =$= copyToChannel id chan clsrs $$ awaitForever (const $ return ())
303 atomically $ writeTVar clsrs Nothing
304 return (chan,clsrs,quitvar)
305
306ioWriteChan :: MonadIO m => TChan a -> a -> m ()
307ioWriteChan c v = liftIO . atomically $ writeTChan c v
308
309stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m ()
310stanzaToConduit stanza = do
311 let xchan = stanzaChan stanza
312 xfin = stanzaClosers stanza
313 rdone = stanzaInterrupt stanza
314 loop = return ()
315 fix $ \inner -> do
316 what <- liftIO . atomically $ foldr1 orElse
317 [readTChan xchan >>= \xml -> return $ do
318 yield xml -- atomically $ Slotted.push slots Nothing xml
319 inner
320 ,do mb <- readTVar xfin
321 cempty <- isEmptyTChan xchan
322 if isNothing mb
323 then if cempty then return loop else retry
324 else do done <- tryReadTMVar rdone
325 check (isJust done)
326 trace "todo: send closers" retry
327 ,do isEmptyTChan xchan >>= check
328 readTMVar rdone
329 return (return ())]
330 what
331
282 332
283sendModifiedStanzaToPeer :: Stanza -> TChan Stanza -> IO () 333sendModifiedStanzaToPeer :: Stanza -> TChan Stanza -> IO ()
284sendModifiedStanzaToPeer stanza chan = do 334sendModifiedStanzaToPeer stanza chan = do
@@ -374,9 +424,19 @@ C->Unrecognized xmlns="jabber:client">
374C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> 424C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
375C->Unrecognized </iq> 425C->Unrecognized </iq>
376-} 426-}
427chanContents :: TChan x -> IO [x]
428chanContents ch = do
429 x <- atomically $ do
430 bempty <- isEmptyTChan ch
431 if bempty
432 then return Nothing
433 else fmap Just $ readTChan ch
434 maybe (return [])
435 (\x -> do
436 xs <- chanContents ch
437 return (x:xs))
438 x
377 439
378ioWriteChan :: MonadIO m => TChan a -> a -> m ()
379ioWriteChan c v = liftIO . atomically $ writeTChan c v
380 440
381parsePresenceStatus 441parsePresenceStatus
382 :: ( MonadThrow m 442 :: ( MonadThrow m
@@ -499,7 +559,7 @@ grokMessage ns stanzaTag = do
499 559
500grokStanza 560grokStanza
501 :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType) 561 :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType)
502ggrokStanza "jabber:server" stanzaTag = 562grokStanza "jabber:server" stanzaTag =
503 case () of 563 case () of
504 _ | stanzaTag `isServerIQOf` "get" -> grokStanzaIQGet stanzaTag 564 _ | stanzaTag `isServerIQOf` "get" -> grokStanzaIQGet stanzaTag
505 _ | stanzaTag `isServerIQOf` "result" -> grokStanzaIQResult stanzaTag 565 _ | stanzaTag `isServerIQOf` "result" -> grokStanzaIQResult stanzaTag
@@ -516,6 +576,24 @@ grokStanza "jabber:client" stanzaTag =
516 _ | tagName stanzaTag == "{jabber:client}message" -> grokMessage "jabber:client" stanzaTag 576 _ | tagName stanzaTag == "{jabber:client}message" -> grokMessage "jabber:client" stanzaTag
517 _ -> return $ Just Unrecognized 577 _ -> return $ Just Unrecognized
518 578
579mkname :: Text -> Text -> XML.Name
580mkname namespace name = (Name name (Just namespace) Nothing)
581
582makePong :: Text -> Maybe Text -> Text -> Text -> [XML.Event]
583makePong namespace mid to from =
584 -- Note: similar to session reply
585 [ EventBeginElement (mkname namespace "iq")
586 $(case mid of
587 Just c -> (("id",[ContentText c]):)
588 _ -> id)
589 [ attr "type" "result"
590 , attr "to" to
591 , attr "from" from
592 ]
593 , EventEndElement (mkname namespace "iq")
594 ]
595
596
519xmppInbound :: Server ConnectionKey SockAddr 597xmppInbound :: Server ConnectionKey SockAddr
520 -> XMPPServerParameters 598 -> XMPPServerParameters
521 -> ConnectionKey 599 -> ConnectionKey
@@ -609,19 +687,6 @@ xmppInbound sv xmpp k laddr pingflag src stanzas output donevar = doNestingXML $
609 loop 687 loop
610 688
611 689
612chanContents :: TChan x -> IO [x]
613chanContents ch = do
614 x <- atomically $ do
615 bempty <- isEmptyTChan ch
616 if bempty
617 then return Nothing
618 else fmap Just $ readTChan ch
619 maybe (return [])
620 (\x -> do
621 xs <- chanContents ch
622 return (x:xs))
623 x
624
625while :: IO Bool -> IO a -> IO [a] 690while :: IO Bool -> IO a -> IO [a]
626while cond body = do 691while cond body = do
627 b <- cond 692 b <- cond
@@ -639,6 +704,7 @@ readUntilNothing ch = do
639 return (x:xs)) 704 return (x:xs))
640 x 705 x
641 706
707
642streamFeatures :: Text -> [XML.Event] 708streamFeatures :: Text -> [XML.Event]
643streamFeatures "jabber:client" = 709streamFeatures "jabber:client" =
644 [ EventBeginElement (streamP "features") [] 710 [ EventBeginElement (streamP "features") []
@@ -680,9 +746,6 @@ data XMPPState
680 = PingSlot 746 = PingSlot
681 deriving (Eq,Ord) 747 deriving (Eq,Ord)
682 748
683mkname :: Text -> Text -> XML.Name
684mkname namespace name = (Name name (Just namespace) Nothing)
685
686makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event] 749makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event]
687makePing namespace mid to from = 750makePing namespace mid to from =
688 [ EventBeginElement (mkname namespace "iq") 751 [ EventBeginElement (mkname namespace "iq")
@@ -697,20 +760,6 @@ makePing namespace mid to from =
697 , EventEndElement "{urn:xmpp:ping}ping" 760 , EventEndElement "{urn:xmpp:ping}ping"
698 , EventEndElement $ mkname namespace "iq"] 761 , EventEndElement $ mkname namespace "iq"]
699 762
700makePong :: Text -> Maybe Text -> Text -> Text -> [XML.Event]
701makePong namespace mid to from =
702 -- Note: similar to session reply
703 [ EventBeginElement (mkname namespace "iq")
704 $(case mid of
705 Just c -> (("id",[ContentText c]):)
706 _ -> id)
707 [ attr "type" "result"
708 , attr "to" to
709 , attr "from" from
710 ]
711 , EventEndElement (mkname namespace "iq")
712 ]
713
714iq_bind_reply :: Maybe Text -> Text -> [XML.Event] 763iq_bind_reply :: Maybe Text -> Text -> [XML.Event]
715iq_bind_reply mid jid = 764iq_bind_reply mid jid =
716 [ EventBeginElement "{jabber:client}iq" (consid mid [("type",[ContentText "result"])]) 765 [ EventBeginElement "{jabber:client}iq" (consid mid [("type",[ContentText "result"])])
@@ -944,29 +993,6 @@ clientKey (sock,addr) = do
944 paddr <- getPeerName sock 993 paddr <- getPeerName sock
945 return $ (ClientKey addr,paddr) 994 return $ (ClientKey addr,paddr)
946 995
947stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m ()
948stanzaToConduit stanza = do
949 let xchan = stanzaChan stanza
950 xfin = stanzaClosers stanza
951 rdone = stanzaInterrupt stanza
952 loop = return ()
953 fix $ \inner -> do
954 what <- liftIO . atomically $ foldr1 orElse
955 [readTChan xchan >>= \xml -> return $ do
956 yield xml -- atomically $ Slotted.push slots Nothing xml
957 inner
958 ,do mb <- readTVar xfin
959 cempty <- isEmptyTChan xchan
960 if isNothing mb
961 then if cempty then return loop else retry
962 else do done <- tryReadTMVar rdone
963 check (isJust done)
964 trace "todo: send closers" retry
965 ,do isEmptyTChan xchan >>= check
966 readTMVar rdone
967 return (return ())]
968 what
969
970xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m () 996xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m ()
971xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set) 997xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set)
972 where 998 where
@@ -978,18 +1004,6 @@ xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set)
978 else [] ) 1004 else [] )
979 yield $ EventEndElement "{jabber:iq:roster}item" 1005 yield $ EventEndElement "{jabber:iq:roster}item"
980 1006
981conduitToChan
982 :: Conduit () IO Event
983 -> IO (TChan Event, TVar (Maybe [Event]), TMVar a)
984conduitToChan c = do
985 chan <- atomically newTChan
986 clsrs <- atomically $ newTVar (Just [])
987 quitvar <- atomically $ newEmptyTMVar
988 forkIO $ do
989 c =$= copyToChannel id chan clsrs $$ awaitForever (const $ return ())
990 atomically $ writeTVar clsrs Nothing
991 return (chan,clsrs,quitvar)
992
993sendRoster :: 1007sendRoster ::
994 StanzaWrap a 1008 StanzaWrap a
995 -> XMPPServerParameters 1009 -> XMPPServerParameters
@@ -1190,3 +1204,4 @@ cloneTChan chan = do
1190 xs <- chanContents' chan 1204 xs <- chanContents' chan
1191 return (x:xs) 1205 return (x:xs)
1192#endif 1206#endif
1207