diff options
-rw-r--r-- | Presence/Presence.hs | 5 | ||||
-rw-r--r-- | Presence/XMPPServer.hs | 102 |
2 files changed, 84 insertions, 23 deletions
diff --git a/Presence/Presence.hs b/Presence/Presence.hs index 224d3282..7a784fa1 100644 --- a/Presence/Presence.hs +++ b/Presence/Presence.hs | |||
@@ -170,6 +170,11 @@ presenceHooks state verbosity mclient mpeer = XMPPServerParameters | |||
170 | , xmppPeerInformSubscription = peerInformSubscription state | 170 | , xmppPeerInformSubscription = peerInformSubscription state |
171 | , xmppVerbosity = return verbosity | 171 | , xmppVerbosity = return verbosity |
172 | , xmppGroupChat = Map.singleton "chat" MUC | 172 | , xmppGroupChat = Map.singleton "chat" MUC |
173 | { mucRoomList = return [("testroom",Just "testroom")] | ||
174 | , mucRoomOccupants = \case | ||
175 | "testroom" -> return [("fakeperson",Nothing)] | ||
176 | _ -> return [] | ||
177 | } | ||
173 | , xmppClientBind = mclient | 178 | , xmppClientBind = mclient |
174 | , xmppPeerBind = mpeer | 179 | , xmppPeerBind = mpeer |
175 | } | 180 | } |
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs index bc64ecd8..218e60e0 100644 --- a/Presence/XMPPServer.hs +++ b/Presence/XMPPServer.hs | |||
@@ -86,6 +86,7 @@ import Data.Conduit.ByteString.Builder (builderToByteStringFlush) | |||
86 | import Data.Conduit.Blaze (builderToByteStringFlush) | 86 | import Data.Conduit.Blaze (builderToByteStringFlush) |
87 | #endif | 87 | #endif |
88 | 88 | ||
89 | import Control.Arrow | ||
89 | import Control.Concurrent.STM.Util | 90 | import Control.Concurrent.STM.Util |
90 | import DNSCache (withPort) | 91 | import DNSCache (withPort) |
91 | import qualified Text.XML.Stream.Render as XML hiding (content) | 92 | import qualified Text.XML.Stream.Render as XML hiding (content) |
@@ -120,7 +121,8 @@ newtype Local a = Local a deriving (Eq,Ord,Show) | |||
120 | newtype Remote a = Remote a deriving (Eq,Ord,Show) | 121 | newtype Remote a = Remote a deriving (Eq,Ord,Show) |
121 | 122 | ||
122 | data MUC = MUC | 123 | data MUC = MUC |
123 | { -- todo | 124 | { mucRoomList :: IO [(Text,Maybe Text)] |
125 | , mucRoomOccupants :: Text -> IO [(Text,Maybe Text)] | ||
124 | } | 126 | } |
125 | 127 | ||
126 | data XMPPServerParameters = | 128 | data XMPPServerParameters = |
@@ -655,8 +657,15 @@ makeInfo mid from mto = concat | |||
655 | , EventEndElement "{jabber:client}iq" ] | 657 | , EventEndElement "{jabber:client}iq" ] |
656 | ] | 658 | ] |
657 | 659 | ||
658 | makeMUCInfo :: Maybe Text -> Text -> Maybe Text -> [Event] | 660 | features :: [Text] -> [XML.Event] |
659 | makeMUCInfo mid from mto = concat | 661 | features fs = do |
662 | t <- fs | ||
663 | [ EventBeginElement "{http://jabber.org/protocol/disco#info}feature" | ||
664 | [("var",[ContentText t])], | ||
665 | EventEndElement "{http://jabber.org/protocol/disco#info}feature" ] | ||
666 | |||
667 | makeMUCInfo :: Maybe Text -> Text -> Maybe Text -> [XML.Event] -> [XML.Event] | ||
668 | makeMUCInfo mid from mto fs = concat | ||
660 | [ [ EventBeginElement "{jabber:client}iq" | 669 | [ [ EventBeginElement "{jabber:client}iq" |
661 | $ consid mid $ maybe id (\to -> (("to", [ContentText to]) :)) mto | 670 | $ consid mid $ maybe id (\to -> (("to", [ContentText to]) :)) mto |
662 | [("from", [ContentText from]) | 671 | [("from", [ContentText from]) |
@@ -677,7 +686,7 @@ makeMUCInfo mid from mto = concat | |||
677 | , EventBeginElement "{http://jabber.org/protocol/disco#info}feature" | 686 | , EventBeginElement "{http://jabber.org/protocol/disco#info}feature" |
678 | [("var",[ContentText "http://jabber.org/protocol/muc"])] | 687 | [("var",[ContentText "http://jabber.org/protocol/muc"])] |
679 | , EventEndElement "{http://jabber.org/protocol/disco#info}feature" ] | 688 | , EventEndElement "{http://jabber.org/protocol/disco#info}feature" ] |
680 | , [] | 689 | , fs |
681 | , [ EventEndElement "{http://jabber.org/protocol/disco#info}query" | 690 | , [ EventEndElement "{http://jabber.org/protocol/disco#info}query" |
682 | , EventEndElement "{jabber:client}iq" ] | 691 | , EventEndElement "{jabber:client}iq" ] |
683 | ] | 692 | ] |
@@ -1313,6 +1322,29 @@ monitor sv params xmpp = do | |||
1313 | stanzaTypeString :: StanzaWrap a -> String | 1322 | stanzaTypeString :: StanzaWrap a -> String |
1314 | stanzaTypeString stanza = concat . take 1 . words $ show (stanzaType stanza) | 1323 | stanzaTypeString stanza = concat . take 1 . words $ show (stanzaType stanza) |
1315 | 1324 | ||
1325 | data ServiceMatch a | ||
1326 | = NotMe -- ^ Hostname of another server. | ||
1327 | | UnknownService Text -- ^ Unknown subdomain of this host. | ||
1328 | | Service (Maybe Text) Text a -- ^ A known subdomain of this host. Optionally, a specific room name. | ||
1329 | | TopLevelService -- ^ This server's exact hostname. | ||
1330 | |||
1331 | |||
1332 | lookupService :: Text -> Map.Map Text a -> Text -> (ServiceMatch a) | ||
1333 | lookupService me mucs to = case Text.toLower to of | ||
1334 | nm | nm == Text.toLower me | ||
1335 | -> TopLevelService | ||
1336 | nm | let (a0,b) = Text.break (=='.') nm | ||
1337 | , Text.drop 1 b == Text.toLower me | ||
1338 | , let (a,service) = second (Text.drop 1) $ Text.break (=='@') a0 | ||
1339 | -> if Text.null service -- No '@' means that variable /a/ is the service. | ||
1340 | then case Map.lookup a mucs of | ||
1341 | Just muc -> Service Nothing a muc | ||
1342 | Nothing -> UnknownService a -- ItemNotFound | ||
1343 | else case Map.lookup service mucs of | ||
1344 | Just muc -> Service (Just a) service muc | ||
1345 | Nothing -> UnknownService service | ||
1346 | _ -> NotMe | ||
1347 | |||
1316 | applyStanza :: Server PeerAddress ConnectionData releaseKey Event | 1348 | applyStanza :: Server PeerAddress ConnectionData releaseKey Event |
1317 | -> TMVar () | 1349 | -> TMVar () |
1318 | -> XMPPServerParameters | 1350 | -> XMPPServerParameters |
@@ -1375,29 +1407,53 @@ applyStanza sv quitVar xmpp stanza = case stanzaOrigin stanza of | |||
1375 | enableClientHacks name version replyto | 1407 | enableClientHacks name version replyto |
1376 | RequestInfo -> do | 1408 | RequestInfo -> do |
1377 | me <- xmppTellMyNameToClient xmpp k | 1409 | me <- xmppTellMyNameToClient xmpp k |
1378 | let fail = let query = "{http://jabber.org/protocol/disco#info}info" | 1410 | let unavail = let query = "{http://jabber.org/protocol/disco#info}info" |
1379 | reply = iq_service_unavailable (stanzaId stanza) me query | 1411 | reply = iq_service_unavailable (stanzaId stanza) me query |
1380 | in sendReply quitVar (Error ServiceUnavailable (head reply)) reply replyto | 1412 | in (Error ServiceUnavailable (head reply), reply) |
1381 | case fmap Text.toLower (stanzaTo stanza) of | 1413 | sto = fromMaybe me (stanzaTo stanza) |
1382 | Nothing -> fail | 1414 | let (rtyp,reply) = case lookupService me (xmppGroupChat xmpp) sto of |
1383 | Just nm | nm == Text.toLower me | 1415 | NotMe -> unavail |
1416 | (UnknownService a) -> unavail -- TODO ItemNotFound instead? | ||
1417 | (Service Nothing a muc) | ||
1418 | -> let reply = makeMUCInfo (stanzaId stanza) (a <> "." <> me) (stanzaFrom stanza) [] | ||
1419 | in (Info, reply) | ||
1420 | (Service (Just room) a muc) | ||
1421 | -> let reply = makeMUCInfo (stanzaId stanza) (room <> "@" <> a <> "." <> me) (stanzaFrom stanza) | ||
1422 | $ features | ||
1423 | [ "http://jabber.org/protocol/muc#stable_id" ] | ||
1424 | in (Info, reply) | ||
1425 | TopLevelService | ||
1384 | -> let reply = makeInfo (stanzaId stanza) me (stanzaFrom stanza) | 1426 | -> let reply = makeInfo (stanzaId stanza) me (stanzaFrom stanza) |
1385 | in sendReply quitVar Info reply replyto | 1427 | in (Info, reply) |
1386 | Just nm | let (a,b) = Text.break (=='.') nm | 1428 | sendReply quitVar rtyp reply replyto |
1387 | , Text.drop 1 b == Text.toLower me | ||
1388 | , Just muc <- Map.lookup a (xmppGroupChat xmpp) | ||
1389 | -> let reply = makeMUCInfo (stanzaId stanza) (a <> "." <> me) (stanzaFrom stanza) | ||
1390 | in sendReply quitVar Info reply replyto | ||
1391 | -- TODO ItemNotFound | ||
1392 | _ -> fail | ||
1393 | RequestItems -> do | 1429 | RequestItems -> do |
1394 | -- let query = "{http://jabber.org/protocol/disco#items}query" | 1430 | -- let query = "{http://jabber.org/protocol/disco#items}query" |
1395 | me <- xmppTellMyNameToClient xmpp k | 1431 | me <- xmppTellMyNameToClient xmpp k |
1396 | let items = do | 1432 | let unavail = let query = "{http://jabber.org/protocol/disco#info}info" |
1397 | (name,MUC {}) <- Map.toList $ xmppGroupChat xmpp | 1433 | reply = iq_service_unavailable (stanzaId stanza) me query |
1398 | return (name <> "." <> me, Nothing) | 1434 | in return (Error ServiceUnavailable (head reply), reply) |
1399 | reply = makeItemList (stanzaId stanza) items me (stanzaFrom stanza) | 1435 | sto = fromMaybe me (stanzaTo stanza) |
1400 | sendReply quitVar Items reply replyto | 1436 | (rtyp,reply) <- case lookupService me (xmppGroupChat xmpp) sto of |
1437 | NotMe -> unavail | ||
1438 | (UnknownService a) -> unavail -- TODO ItemNotFound instead? | ||
1439 | (Service Nothing a muc) -> do | ||
1440 | items <- map (\(n,m) -> (n <> "@" <> a <> "." <> me, m)) | ||
1441 | <$> mucRoomList muc | ||
1442 | let reply = makeItemList (stanzaId stanza) items (a <> "." <> me) (stanzaFrom stanza) | ||
1443 | return (Items, reply) | ||
1444 | (Service (Just room) a muc) -> do | ||
1445 | items <- map (\(n,m) -> (room <> "@" <> a <> "." <> me <> "/" <> n, m)) | ||
1446 | <$> mucRoomOccupants muc room | ||
1447 | -- Note: I'm assuming 'mucRoomOccupants' returns an empty list for | ||
1448 | -- private rooms. | ||
1449 | let reply = makeItemList (stanzaId stanza) items (a <> "." <> me) (stanzaFrom stanza) | ||
1450 | return (Items, reply) | ||
1451 | TopLevelService -> do | ||
1452 | let items = do (name,MUC {}) <- Map.toList $ xmppGroupChat xmpp | ||
1453 | return (name <> "." <> me, Nothing) | ||
1454 | reply = makeItemList (stanzaId stanza) items me (stanzaFrom stanza) | ||
1455 | return (Items, reply) | ||
1456 | sendReply quitVar rtyp reply replyto | ||
1401 | UnrecognizedQuery query -> do | 1457 | UnrecognizedQuery query -> do |
1402 | me <- xmppTellMyNameToClient xmpp k | 1458 | me <- xmppTellMyNameToClient xmpp k |
1403 | let reply = iq_service_unavailable (stanzaId stanza) me query | 1459 | let reply = iq_service_unavailable (stanzaId stanza) me query |