diff options
author | joe <joe@jerkface.net> | 2014-02-16 16:02:17 -0500 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-02-16 16:02:17 -0500 |
commit | 585e8aca57f0fdb09dd6cf42cb67af23448cdc6a (patch) | |
tree | fc62c1c72934394b1d38ecefe5cd43ee9ed3f0a8 /Presence/XMPPServer.hs | |
parent | 02a5b4dd3ac448e1e025234ce42e78b731de6fe9 (diff) |
send roster
Diffstat (limited to 'Presence/XMPPServer.hs')
-rw-r--r-- | Presence/XMPPServer.hs | 96 |
1 files changed, 89 insertions, 7 deletions
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index 52933440..d3df48d0 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -6,8 +6,9 @@ module XMPPServer | |||
6 | , Stanza(..) | 6 | , Stanza(..) |
7 | , StanzaType(..) | 7 | , StanzaType(..) |
8 | , StanzaOrigin(..) | 8 | , StanzaOrigin(..) |
9 | , dupStanza | 9 | , cloneStanza |
10 | ) where | 10 | ) where |
11 | import Debug.Trace | ||
11 | import Control.Monad.Trans.Resource (runResourceT) | 12 | import Control.Monad.Trans.Resource (runResourceT) |
12 | import Control.Monad.Trans (lift) | 13 | import Control.Monad.Trans (lift) |
13 | import Control.Monad.IO.Class (MonadIO, liftIO) | 14 | import Control.Monad.IO.Class (MonadIO, liftIO) |
@@ -33,13 +34,16 @@ import Data.Conduit.Blaze (builderToByteStringFlush) | |||
33 | import qualified Text.XML.Stream.Render as XML | 34 | import qualified Text.XML.Stream.Render as XML |
34 | import qualified Text.XML.Stream.Parse as XML | 35 | import qualified Text.XML.Stream.Parse as XML |
35 | import Data.XML.Types as XML | 36 | import Data.XML.Types as XML |
36 | import Data.Maybe (catMaybes,fromJust,isNothing) | 37 | import Data.Maybe (catMaybes,fromJust,isJust,isNothing) |
37 | import Data.Monoid ( (<>) ) | 38 | import Data.Monoid ( (<>) ) |
38 | import Data.Text (Text) | 39 | import Data.Text (Text) |
39 | import qualified Data.Text as Text (pack,unpack) | 40 | import qualified Data.Text as Text (pack,unpack) |
40 | import Data.Char (toUpper) | 41 | import Data.Char (toUpper) |
41 | import Data.Map (Map) | 42 | import Data.Map (Map) |
42 | import qualified Data.Map as Map | 43 | import qualified Data.Map as Map |
44 | import Data.Set (Set, (\\) ) | ||
45 | import qualified Data.Set as Set | ||
46 | import qualified System.Random | ||
43 | 47 | ||
44 | import qualified Control.Concurrent.STM.UpdateStream as Slotted | 48 | import qualified Control.Concurrent.STM.UpdateStream as Slotted |
45 | import ControlMaybe | 49 | import ControlMaybe |
@@ -85,6 +89,8 @@ data StanzaType | |||
85 | | SetResource | 89 | | SetResource |
86 | | SessionRequest | 90 | | SessionRequest |
87 | | UnrecognizedQuery Name | 91 | | UnrecognizedQuery Name |
92 | | RequestRoster | ||
93 | | Roster | ||
88 | | Error | 94 | | Error |
89 | | PresenceStatus { presenceShow :: JabberShow | 95 | | PresenceStatus { presenceShow :: JabberShow |
90 | , presencePriority :: Maybe Int8 | 96 | , presencePriority :: Maybe Int8 |
@@ -117,6 +123,12 @@ data XMPPServerParameters = | |||
117 | { xmppChooseResourceName :: ConnectionKey -> Socket -> Maybe Text -> IO Text | 123 | { xmppChooseResourceName :: ConnectionKey -> Socket -> Maybe Text -> IO Text |
118 | , xmppNewConnection :: ConnectionKey -> TChan Stanza -> IO () | 124 | , xmppNewConnection :: ConnectionKey -> TChan Stanza -> IO () |
119 | , xmppEOF :: ConnectionKey -> IO () | 125 | , xmppEOF :: ConnectionKey -> IO () |
126 | , xmppRosterBuddies :: ConnectionKey -> IO [Text] | ||
127 | , xmppRosterSubscribers :: ConnectionKey -> IO [Text] | ||
128 | , xmppRosterSolicited :: ConnectionKey -> IO [Text] | ||
129 | , xmppRosterOthers :: ConnectionKey -> IO [Text] | ||
130 | , xmppLookupClientJID :: ConnectionKey -> IO Text | ||
131 | , xmppLookupPeerName :: ConnectionKey -> IO Text | ||
120 | } | 132 | } |
121 | 133 | ||
122 | -- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error | 134 | -- TODO: http://xmpp.org/rfcs/rfc6120.html#rules-remote-error |
@@ -167,8 +179,8 @@ type FlagCommand = STM Bool | |||
167 | type ReadCommand = IO (Maybe ByteString) | 179 | type ReadCommand = IO (Maybe ByteString) |
168 | type WriteCommand = ByteString -> IO Bool | 180 | type WriteCommand = ByteString -> IO Bool |
169 | 181 | ||
170 | dupStanza stanza = do | 182 | cloneStanza stanza = do |
171 | dupped <- dupTChan (stanzaChan stanza) | 183 | dupped <- cloneTChan (stanzaChan stanza) |
172 | return stanza { stanzaChan = dupped } | 184 | return stanza { stanzaChan = dupped } |
173 | 185 | ||
174 | copyToChannel f chan closer_stack = awaitForever copy | 186 | copyToChannel f chan closer_stack = awaitForever copy |
@@ -223,6 +235,7 @@ grokStanzaIQGet stanza = do | |||
223 | flip (maybe $ return Nothing) mtag $ \tag -> do | 235 | flip (maybe $ return Nothing) mtag $ \tag -> do |
224 | case tagName tag of | 236 | case tagName tag of |
225 | "{urn:xmpp:ping}ping" -> return $ Just Ping | 237 | "{urn:xmpp:ping}ping" -> return $ Just Ping |
238 | "{jabber:iq:roster}query" -> return $ Just RequestRoster | ||
226 | name -> return . Just $ UnrecognizedQuery name | 239 | name -> return . Just $ UnrecognizedQuery name |
227 | 240 | ||
228 | grokStanzaIQResult :: Monad m => XML.Event -> NestingXML o m (Maybe StanzaType) | 241 | grokStanzaIQResult :: Monad m => XML.Event -> NestingXML o m (Maybe StanzaType) |
@@ -644,7 +657,7 @@ forkConnection sv k pingflag src snk stanzas = do | |||
644 | fix $ \loop -> do | 657 | fix $ \loop -> do |
645 | what <- atomically $ foldr1 orElse | 658 | what <- atomically $ foldr1 orElse |
646 | [readTChan output >>= \stanza -> return $ do | 659 | [readTChan output >>= \stanza -> return $ do |
647 | dup <- atomically $ dupStanza stanza | 660 | dup <- atomically $ cloneStanza stanza |
648 | stanzaToConduit dup $$ prettyPrint $ case k of | 661 | stanzaToConduit dup $$ prettyPrint $ case k of |
649 | ClientKey {} -> "C<-" <> bshow (stanzaType dup) <> " " | 662 | ClientKey {} -> "C<-" <> bshow (stanzaType dup) <> " " |
650 | PeerKey {} -> "P<-" <> bshow (stanzaType dup) <> " " | 663 | PeerKey {} -> "P<-" <> bshow (stanzaType dup) <> " " |
@@ -712,12 +725,74 @@ stanzaToConduit stanza = do | |||
712 | cempty <- isEmptyTChan xchan | 725 | cempty <- isEmptyTChan xchan |
713 | if isNothing mb | 726 | if isNothing mb |
714 | then if cempty then return loop else retry | 727 | then if cempty then return loop else retry |
715 | else retry -- todo: send closers | 728 | else do done <- tryReadTMVar rdone |
729 | check (isJust done) | ||
730 | trace "todo: send closers" retry | ||
716 | ,do isEmptyTChan xchan >>= check | 731 | ,do isEmptyTChan xchan >>= check |
717 | readTMVar rdone | 732 | readTMVar rdone |
718 | return (return ())] | 733 | return (return ())] |
719 | what | 734 | what |
720 | 735 | ||
736 | xmlifyRosterItems :: Monad m => Set Text -> Text -> Set Text -> ConduitM i Event m () | ||
737 | xmlifyRosterItems solicited stype set = mapM_ item (Set.toList set) | ||
738 | where | ||
739 | item jid = do yield $ EventBeginElement "item" | ||
740 | ([ attr "jid" jid | ||
741 | , attr "subscription" stype | ||
742 | ]++if Set.member jid solicited | ||
743 | then [attr "ask" "subscribe"] | ||
744 | else [] ) | ||
745 | yield $ EventEndElement "item" | ||
746 | |||
747 | sendRoster query xmpp replyto = do | ||
748 | let k = case stanzaOrigin query of | ||
749 | NetworkOrigin k _ -> Just k | ||
750 | LocalPeer -> Nothing -- local peer requested roster? | ||
751 | flip (maybe $ return ()) k $ \k -> do | ||
752 | jid <- case k of | ||
753 | ClientKey {} -> xmppLookupClientJID xmpp k | ||
754 | PeerKey {} -> xmppLookupPeerName xmpp k | ||
755 | let getlist f = do | ||
756 | bs <- f xmpp k | ||
757 | -- js <- mapM parseHostNameJID bs | ||
758 | return (Set.fromList bs) -- js) | ||
759 | buddies <- getlist xmppRosterBuddies | ||
760 | subscribers <- getlist xmppRosterSubscribers | ||
761 | solicited <- getlist xmppRosterSolicited | ||
762 | subnone0 <- getlist xmppRosterOthers | ||
763 | let subnone = subnone0 \\ (Set.union buddies subscribers) | ||
764 | let subto = buddies \\ subscribers | ||
765 | let subfrom = subscribers \\ buddies | ||
766 | let subboth = Set.intersection buddies subscribers | ||
767 | let roster = do | ||
768 | yield $ EventBeginElement "{jabber:client}iq" | ||
769 | (consid (stanzaId query) | ||
770 | [ attr "to" jid | ||
771 | , attr "type" "result" ]) | ||
772 | yield $ EventBeginElement "{jabber:iq:roster}query" [] -- todo: ver? | ||
773 | xmlifyRosterItems solicited "to" subto | ||
774 | xmlifyRosterItems solicited "from" subfrom | ||
775 | xmlifyRosterItems solicited "both" subboth | ||
776 | xmlifyRosterItems solicited "none" subnone | ||
777 | yield $ EventEndElement "{jabber:iq:roster}query" | ||
778 | yield $ EventEndElement "{jabber:client}iq" | ||
779 | chan <- atomically newTChan | ||
780 | clsrs <- atomically $ newTVar (Just []) | ||
781 | quitvar <- atomically $ newEmptyTMVar | ||
782 | forkIO $ do | ||
783 | roster =$= copyToChannel id chan clsrs $$ awaitForever (const $ return ()) | ||
784 | atomically $ writeTVar clsrs Nothing | ||
785 | ioWriteChan replyto | ||
786 | Stanza { stanzaType = Roster | ||
787 | , stanzaId = (stanzaId query) | ||
788 | , stanzaTo = Just jid | ||
789 | , stanzaFrom = Nothing | ||
790 | , stanzaChan = chan | ||
791 | , stanzaClosers = clsrs | ||
792 | , stanzaInterrupt = quitvar | ||
793 | , stanzaOrigin = LocalPeer | ||
794 | } | ||
795 | |||
721 | socketFromKey :: Server k -> k -> IO Socket | 796 | socketFromKey :: Server k -> k -> IO Socket |
722 | socketFromKey sv k = do | 797 | socketFromKey sv k = do |
723 | return todo | 798 | return todo |
@@ -758,6 +833,8 @@ monitor sv params xmpp = do | |||
758 | SessionRequest -> do | 833 | SessionRequest -> do |
759 | let reply = iq_session_reply (stanzaId stanza) "localhost" | 834 | let reply = iq_session_reply (stanzaId stanza) "localhost" |
760 | sendReply quitVar Pong reply replyto | 835 | sendReply quitVar Pong reply replyto |
836 | RequestRoster -> | ||
837 | sendRoster stanza xmpp replyto | ||
761 | UnrecognizedQuery query -> do | 838 | UnrecognizedQuery query -> do |
762 | let reply = iq_service_unavailable (stanzaId stanza) "localhost" query | 839 | let reply = iq_service_unavailable (stanzaId stanza) "localhost" query |
763 | sendReply quitVar Error reply replyto | 840 | sendReply quitVar Error reply replyto |
@@ -784,8 +861,13 @@ xmppServer :: ( MonadResource m | |||
784 | ) => XMPPServerParameters -> m (Server ConnectionKey,ConnectionParameters ConnectionKey) | 861 | ) => XMPPServerParameters -> m (Server ConnectionKey,ConnectionParameters ConnectionKey) |
785 | xmppServer xmpp = do | 862 | xmppServer xmpp = do |
786 | sv <- server | 863 | sv <- server |
864 | -- some fuzz helps avoid simultaneity | ||
865 | pingfuzz <- liftIO $ do | ||
866 | gen <- System.Random.getStdGen | ||
867 | let (r,gen') = System.Random.next gen | ||
868 | return $ r `mod` 2000 -- maximum 2 seconds of fuzz | ||
787 | let peer_params = (connectionDefaults peerKey) | 869 | let peer_params = (connectionDefaults peerKey) |
788 | { pingInterval = 15000 | 870 | { pingInterval = 15000 + pingfuzz |
789 | , timeout = 2000 | 871 | , timeout = 2000 |
790 | , duplex = False } | 872 | , duplex = False } |
791 | client_params = (connectionDefaults clientKey) | 873 | client_params = (connectionDefaults clientKey) |