diff options
Diffstat (limited to 'Presence/XMPPServer.hs')
-rw-r--r-- | Presence/XMPPServer.hs | 157 |
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 #-} | ||
3 | module XMPPServer | 4 | module 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 | |||
21 | import Debug.Trace | 23 | import Debug.Trace |
22 | import Control.Monad.Trans.Resource (runResourceT) | 24 | import Control.Monad.Trans.Resource (runResourceT) |
23 | import Control.Monad.Trans (lift) | 25 | import Control.Monad.Trans (lift) |
@@ -28,7 +30,6 @@ import Control.Concurrent (forkIO) | |||
28 | import Control.Concurrent.STM | 30 | import Control.Concurrent.STM |
29 | -- import Control.Concurrent.STM.TChan | 31 | -- import Control.Concurrent.STM.TChan |
30 | import Network.Socket | 32 | import Network.Socket |
31 | import XMPPTypes (withPort) | ||
32 | import Text.Printf | 33 | import Text.Printf |
33 | import System.Posix.Signals | 34 | import System.Posix.Signals |
34 | import Data.ByteString (ByteString) | 35 | import Data.ByteString (ByteString) |
@@ -55,17 +56,26 @@ import Data.Set (Set, (\\) ) | |||
55 | import qualified Data.Set as Set | 56 | import qualified Data.Set as Set |
56 | import qualified System.Random | 57 | import qualified System.Random |
57 | import qualified Network.BSD as BSD | 58 | import qualified Network.BSD as BSD |
59 | import Data.Void (Void) | ||
58 | 60 | ||
59 | import GetHostByAddr (getHostByAddr) | 61 | import GetHostByAddr (getHostByAddr) |
60 | import qualified Control.Concurrent.STM.UpdateStream as Slotted | 62 | import qualified Control.Concurrent.STM.UpdateStream as Slotted |
61 | import ControlMaybe | ||
62 | import Nesting | 63 | import Nesting |
63 | import EventUtil | ||
64 | import Server | 64 | import Server |
65 | import EventUtil | ||
66 | import ControlMaybe | ||
65 | 67 | ||
68 | withPort :: SockAddr -> Int -> SockAddr | ||
69 | withPort (SockAddrInet _ a) port = SockAddrInet (toEnum port) a | ||
70 | withPort (SockAddrInet6 _ a b c) port = SockAddrInet6 (toEnum port) a b c | ||
71 | |||
72 | |||
73 | peerport :: PortNumber | ||
66 | peerport = 5269 | 74 | peerport = 5269 |
75 | clientport :: PortNumber | ||
67 | clientport = 5222 | 76 | clientport = 5222 |
68 | 77 | ||
78 | my_uuid :: Text | ||
69 | my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" | 79 | my_uuid = "154ae29f-98f2-4af4-826d-a40c8a188574" |
70 | 80 | ||
71 | data ConnectionKey | 81 | data 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 | ||
266 | prettyPrint :: ByteString -> ConduitM Event Void IO () | ||
255 | prettyPrint prefix = | 267 | prettyPrint 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 | ||
294 | conduitToChan | ||
295 | :: Conduit () IO Event | ||
296 | -> IO (TChan Event, TVar (Maybe [Event]), TMVar a) | ||
297 | conduitToChan 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 | |||
306 | ioWriteChan :: MonadIO m => TChan a -> a -> m () | ||
307 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | ||
308 | |||
309 | stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m () | ||
310 | stanzaToConduit 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 | ||
283 | sendModifiedStanzaToPeer :: Stanza -> TChan Stanza -> IO () | 333 | sendModifiedStanzaToPeer :: Stanza -> TChan Stanza -> IO () |
284 | sendModifiedStanzaToPeer stanza chan = do | 334 | sendModifiedStanzaToPeer stanza chan = do |
@@ -374,9 +424,19 @@ C->Unrecognized xmlns="jabber:client"> | |||
374 | C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> | 424 | C->Unrecognized <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/> |
375 | C->Unrecognized </iq> | 425 | C->Unrecognized </iq> |
376 | -} | 426 | -} |
427 | chanContents :: TChan x -> IO [x] | ||
428 | chanContents 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 | ||
378 | ioWriteChan :: MonadIO m => TChan a -> a -> m () | ||
379 | ioWriteChan c v = liftIO . atomically $ writeTChan c v | ||
380 | 440 | ||
381 | parsePresenceStatus | 441 | parsePresenceStatus |
382 | :: ( MonadThrow m | 442 | :: ( MonadThrow m |
@@ -499,7 +559,7 @@ grokMessage ns stanzaTag = do | |||
499 | 559 | ||
500 | grokStanza | 560 | grokStanza |
501 | :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType) | 561 | :: Text -> XML.Event -> NestingXML o IO (Maybe StanzaType) |
502 | ggrokStanza "jabber:server" stanzaTag = | 562 | grokStanza "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 | ||
579 | mkname :: Text -> Text -> XML.Name | ||
580 | mkname namespace name = (Name name (Just namespace) Nothing) | ||
581 | |||
582 | makePong :: Text -> Maybe Text -> Text -> Text -> [XML.Event] | ||
583 | makePong 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 | |||
519 | xmppInbound :: Server ConnectionKey SockAddr | 597 | xmppInbound :: 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 | ||
612 | chanContents :: TChan x -> IO [x] | ||
613 | chanContents 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 | |||
625 | while :: IO Bool -> IO a -> IO [a] | 690 | while :: IO Bool -> IO a -> IO [a] |
626 | while cond body = do | 691 | while 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 | |||
642 | streamFeatures :: Text -> [XML.Event] | 708 | streamFeatures :: Text -> [XML.Event] |
643 | streamFeatures "jabber:client" = | 709 | streamFeatures "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 | ||
683 | mkname :: Text -> Text -> XML.Name | ||
684 | mkname namespace name = (Name name (Just namespace) Nothing) | ||
685 | |||
686 | makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event] | 749 | makePing :: Text -> Maybe Text -> Text -> Text -> [XML.Event] |
687 | makePing namespace mid to from = | 750 | makePing 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 | ||
700 | makePong :: Text -> Maybe Text -> Text -> Text -> [XML.Event] | ||
701 | makePong 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 | |||
714 | iq_bind_reply :: Maybe Text -> Text -> [XML.Event] | 763 | iq_bind_reply :: Maybe Text -> Text -> [XML.Event] |
715 | iq_bind_reply mid jid = | 764 | iq_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 | ||
947 | stanzaToConduit :: MonadIO m => Stanza -> ConduitM i Event m () | ||
948 | stanzaToConduit 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 | |||
970 | xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m () | 996 | xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m () |
971 | xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set) | 997 | xmlifyRosterItems 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 | ||
981 | conduitToChan | ||
982 | :: Conduit () IO Event | ||
983 | -> IO (TChan Event, TVar (Maybe [Event]), TMVar a) | ||
984 | conduitToChan 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 | |||
993 | sendRoster :: | 1007 | sendRoster :: |
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 | |||