summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Presence/ControlMaybe.hs8
-rw-r--r--Presence/EventUtil.hs17
-rw-r--r--Presence/Nesting.hs4
-rw-r--r--Presence/XMPPServer.hs157
4 files changed, 114 insertions, 72 deletions
diff --git a/Presence/ControlMaybe.hs b/Presence/ControlMaybe.hs
index 69a38f71..659dab74 100644
--- a/Presence/ControlMaybe.hs
+++ b/Presence/ControlMaybe.hs
@@ -4,9 +4,12 @@ module ControlMaybe where
4-- import GHC.IO.Exception (IOException(..)) 4-- import GHC.IO.Exception (IOException(..))
5import Control.Exception as Exception (IOException(..),catch) 5import Control.Exception as Exception (IOException(..),catch)
6 6
7
8withJust :: Monad m => Maybe x -> (x -> m ()) -> m ()
7withJust (Just x) f = f x 9withJust (Just x) f = f x
8withJust Nothing f = return () 10withJust Nothing f = return ()
9 11
12whenJust :: Monad m => m (Maybe x) -> (x -> m ()) -> m ()
10whenJust acn f = do 13whenJust acn f = do
11 x <- acn 14 x <- acn
12 withJust x f 15 withJust x f
@@ -18,6 +21,9 @@ catchIO_ a h = Exception.catch a (\(_ :: IOException) -> h)
18catchIO :: IO a -> (IOException -> IO a) -> IO a 21catchIO :: IO a -> (IOException -> IO a) -> IO a
19catchIO body handler = Exception.catch body handler 22catchIO body handler = Exception.catch body handler
20 23
24handleIO_ :: IO a -> IO a -> IO a
21handleIO_ = flip catchIO_ 25handleIO_ = flip catchIO_
22handleIO = flip catchIO
23 26
27
28handleIO :: (IOException -> IO a) -> IO a -> IO a
29handleIO = flip catchIO
diff --git a/Presence/EventUtil.hs b/Presence/EventUtil.hs
index 02a37472..908e09e0 100644
--- a/Presence/EventUtil.hs
+++ b/Presence/EventUtil.hs
@@ -4,49 +4,62 @@ module EventUtil where
4import Control.Monad 4import Control.Monad
5import Data.XML.Types as XML 5import Data.XML.Types as XML
6import qualified Data.List as List 6import qualified Data.List as List
7import Data.Text (Text)
7 8
8-- getStreamName (EventBeginElement name _) = name 9-- getStreamName (EventBeginElement name _) = name
9 10
11isEventBeginElement :: Event -> Bool
10isEventBeginElement (EventBeginElement {}) = True 12isEventBeginElement (EventBeginElement {}) = True
11isEventBeginElement _ = False 13isEventBeginElement _ = False
12 14
15isEventEndElement :: Event -> Bool
13isEventEndElement (EventEndElement {}) = True 16isEventEndElement (EventEndElement {}) = True
14isEventEndElement _ = False 17isEventEndElement _ = False
15 18
16-- Note: This function ignores name space qualification 19-- Note: This function ignores name space qualification
20elementAttrs ::
21 MonadPlus m =>
22 Text -> Event -> m [(Name, [Content])]
17elementAttrs expected (EventBeginElement name attrs) 23elementAttrs expected (EventBeginElement name attrs)
18 | nameLocalName name==expected 24 | nameLocalName name==expected
19 = return attrs 25 = return attrs
20elementAttrs _ _ = mzero 26elementAttrs _ _ = mzero
21 27
28streamP :: Text -> Name
22streamP name = Name name (Just "http://etherx.jabber.org/streams") (Just "stream") 29streamP name = Name name (Just "http://etherx.jabber.org/streams") (Just "stream")
23 30
31attr :: Name -> Text -> (Name,[Content])
24attr name value = (name,[ContentText value]) 32attr name value = (name,[ContentText value])
25 33
34isServerIQOf :: Event -> Text -> Bool
26isServerIQOf (EventBeginElement name attrs) testType 35isServerIQOf (EventBeginElement name attrs) testType
27 | name=="{jabber:server}iq" 36 | name=="{jabber:server}iq"
28 && matchAttrib "type" testType attrs 37 && matchAttrib "type" testType attrs
29 = True 38 = True
30isServerIQOf _ _ = False 39isServerIQOf _ _ = False
31 40
41isClientIQOf :: Event -> Text -> Bool
32isClientIQOf (EventBeginElement name attrs) testType 42isClientIQOf (EventBeginElement name attrs) testType
33 | name=="{jabber:client}iq" 43 | name=="{jabber:client}iq"
34 && matchAttrib "type" testType attrs 44 && matchAttrib "type" testType attrs
35 = True 45 = True
36isClientIQOf _ _ = False 46isClientIQOf _ _ = False
37 47
48matchAttrib :: Name -> Text -> [(Name, [Content])] -> Bool
38matchAttrib name value attrs = 49matchAttrib name value attrs =
39 case List.find ( (==name) . fst) attrs of 50 case List.find ( (==name) . fst) attrs of
40 Just (_,[ContentText x]) | x==value -> True 51 Just (_,[ContentText x]) | x==value -> True
41 Just (_,[ContentEntity x]) | x==value -> True 52 Just (_,[ContentEntity x]) | x==value -> True
42 _ -> False 53 _ -> False
43 54
55lookupAttrib :: Name -> [(Name, [Content])] -> Maybe Text
44lookupAttrib name attrs = 56lookupAttrib name attrs =
45 case List.find ( (==name) . fst) attrs of 57 case List.find ( (==name) . fst) attrs of
46 Just (_,[ContentText x]) -> Just x 58 Just (_,[ContentText x]) -> Just x
47 Just (_,[ContentEntity x]) -> Just x 59 Just (_,[ContentEntity x]) -> Just x
48 _ -> Nothing 60 _ -> Nothing
49 61
62tagAttrs :: Event -> [(Name, [Content])]
50tagAttrs (EventBeginElement _ xs) = xs 63tagAttrs (EventBeginElement _ xs) = xs
51tagAttrs _ = [] 64tagAttrs _ = []
52 65
@@ -59,8 +72,12 @@ iqTypeError = "error"
59-} 72-}
60 73
61 74
75tagName :: Event -> Name
62tagName (EventBeginElement n _) = n 76tagName (EventBeginElement n _) = n
63tagName _ = "" 77tagName _ = ""
64 78
79closerFor :: Event -> Event
65closerFor (EventBeginElement n _) = EventEndElement n 80closerFor (EventBeginElement n _) = EventEndElement n
66closerFor _ = error "closerFor: unsupported event" 81closerFor _ = error "closerFor: unsupported event"
82
83
diff --git a/Presence/Nesting.hs b/Presence/Nesting.hs
index 850cb8c0..dd0e4113 100644
--- a/Presence/Nesting.hs
+++ b/Presence/Nesting.hs
@@ -52,6 +52,7 @@ trackNesting = awaitForever doit
52 yield xml 52 yield xml
53 53
54 54
55lookupLang :: [(Name, [Content])] -> Maybe S.Text
55lookupLang attrs = 56lookupLang attrs =
56 case List.find ( (=="xml:lang") . fst) attrs of 57 case List.find ( (=="xml:lang") . fst) attrs of
57 Just (_,[ContentText x]) -> Just x 58 Just (_,[ContentText x]) -> Just x
@@ -67,6 +68,9 @@ awaitCloser lvl = do
67 xml <- await 68 xml <- await
68 maybe (return ()) (const loop) xml 69 maybe (return ()) (const loop) xml
69 70
71withXML ::
72 Monad m =>
73 (i -> ConduitM i o m ()) -> ConduitM i o m ()
70withXML f = await >>= maybe (return ()) f 74withXML f = await >>= maybe (return ()) f
71 75
72nextElement :: Monad m => NestingXML o m (Maybe Event) 76nextElement :: Monad m => NestingXML o m (Maybe Event)
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