diff options
author | Joe Crayne <joe@jerkface.net> | 2018-07-06 05:24:37 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2018-07-06 05:25:36 -0400 |
commit | ea3f4e6543b6dddd94898c945a8ad2c24a46ae77 (patch) | |
tree | 6bff1ac5a1c7ff97cf6fb9387bf1cffe201d6a3f | |
parent | 95d6ae45e07707eb93f083ecf02d8f0df0015496 (diff) |
tox-to-xmpp: presence updates.
-rw-r--r-- | Presence/Presence.hs | 24 | ||||
-rw-r--r-- | ToxToXMPP.hs | 94 |
2 files changed, 100 insertions, 18 deletions
diff --git a/Presence/Presence.hs b/Presence/Presence.hs index 4ca49f78..59926d13 100644 --- a/Presence/Presence.hs +++ b/Presence/Presence.hs | |||
@@ -178,7 +178,7 @@ data LocalPresence = LocalPresence | |||
178 | } | 178 | } |
179 | 179 | ||
180 | data RemotePresence = RemotePresence | 180 | data RemotePresence = RemotePresence |
181 | { resources :: Map Text Stanza | 181 | { resources :: Map ResourceName Stanza |
182 | -- , localSubscribers :: Map Text () | 182 | -- , localSubscribers :: Map Text () |
183 | -- ^ subset of clientsByUser who should be | 183 | -- ^ subset of clientsByUser who should be |
184 | -- notified about this presence. | 184 | -- notified about this presence. |
@@ -764,7 +764,14 @@ informPeerPresence state k stanza = do | |||
764 | -- Presence must indicate full JID with resource... | 764 | -- Presence must indicate full JID with resource... |
765 | dput XJabber $ "xmppInformPeerPresence checking from address..." | 765 | dput XJabber $ "xmppInformPeerPresence checking from address..." |
766 | forM_ (stanzaFrom stanza) $ \from -> do | 766 | forM_ (stanzaFrom stanza) $ \from -> do |
767 | let (muser,h,mresource) = splitJID from | 767 | let (muser0,h,mresource0) = splitJID from |
768 | -- We'll allow the case that user and resource are simultaneously | ||
769 | -- absent. They will be stored in the remotesByPeer map using the | ||
770 | -- empty string. This is to accomodate the tox protocol which didn't | ||
771 | -- anticipate a single peer would have multiple users or front-ends. | ||
772 | (muser,mresource) = case (muser0,mresource0) of | ||
773 | (Nothing,Nothing) -> (Just "", Just "") | ||
774 | _ -> (muser0,mresource0) | ||
768 | dput XJabber $ "xmppInformPeerPresence from = " ++ show from | 775 | dput XJabber $ "xmppInformPeerPresence from = " ++ show from |
769 | -- forM_ mresource $ \resource -> do | 776 | -- forM_ mresource $ \resource -> do |
770 | forM_ muser $ \user -> do | 777 | forM_ muser $ \user -> do |
@@ -806,15 +813,22 @@ informPeerPresence state k stanza = do | |||
806 | con <- liftMaybe $ Map.lookup ck ktc | 813 | con <- liftMaybe $ Map.lookup ck ktc |
807 | return (ck,con,client) | 814 | return (ck,con,client) |
808 | dput XJabber $ "xmppInformPeerPresence (length clients="++show (length clients)++")" | 815 | dput XJabber $ "xmppInformPeerPresence (length clients="++show (length clients)++")" |
816 | (ctyp,cprof) <- atomically $ do | ||
817 | mconn <- Map.lookup k <$> readTVar (pkeyToChan state) | ||
818 | return $ fromMaybe (XMPP,".") $ do | ||
819 | ConnectionData _ ctyp cprof <- auxData <$> mconn | ||
820 | return (ctyp,cprof) | ||
809 | forM_ clients $ \(ck,con,client) -> do | 821 | forM_ clients $ \(ck,con,client) -> do |
810 | -- (TODO: appropriately authorized clients only.) | 822 | -- (TODO: appropriately authorized clients only.) |
811 | -- For now, all "available" clients (available = sent initial presence) | 823 | -- For now, all "available" clients (available = sent initial presence) |
812 | is_avail <- atomically $ clientIsAvailable client | 824 | is_avail <- atomically $ clientIsAvailable client |
813 | when is_avail $ do | 825 | when is_avail $ do |
814 | dput XJabber $ "reversing for client: " ++ show from | 826 | dput XJabber $ "reversing for client: " ++ show from |
815 | froms <- do -- flip (maybe $ return [from]) k . const $ do | 827 | froms <- case ctyp of |
816 | (_,trip) <- multiplyJIDForClient ck from | 828 | Tox | clientProfile client == cprof -> return [from] |
817 | return (map unsplitJID trip) | 829 | _ -> do -- flip (maybe $ return [from]) k . const $ do |
830 | (_,trip) <- multiplyJIDForClient ck from | ||
831 | return (map unsplitJID trip) | ||
818 | 832 | ||
819 | dput XJabber $ "sending to client: " ++ show (stanzaType stanza,froms) | 833 | dput XJabber $ "sending to client: " ++ show (stanzaType stanza,froms) |
820 | forM_ froms $ \from' -> do | 834 | forM_ froms $ \from' -> do |
diff --git a/ToxToXMPP.hs b/ToxToXMPP.hs index 8b2544d7..7208a1d1 100644 --- a/ToxToXMPP.hs +++ b/ToxToXMPP.hs | |||
@@ -5,26 +5,41 @@ | |||
5 | {-# LANGUAGE ViewPatterns #-} | 5 | {-# LANGUAGE ViewPatterns #-} |
6 | module ToxToXMPP where | 6 | module ToxToXMPP where |
7 | 7 | ||
8 | import Control.Monad | ||
8 | import Crypto.Tox | 9 | import Crypto.Tox |
9 | import Data.Conduit as C | 10 | import Data.Conduit as C |
10 | import qualified Data.Conduit.List as CL | 11 | import qualified Data.Conduit.List as CL |
12 | import Data.Function | ||
11 | import Data.Monoid | 13 | import Data.Monoid |
12 | import qualified Data.Text as T | 14 | import qualified Data.Text as T |
13 | ;import Data.Text (Text) | 15 | ;import Data.Text (Text) |
14 | import Data.Word | ||
15 | import Data.Text.Encoding as T | 16 | import Data.Text.Encoding as T |
17 | import Data.Word | ||
16 | import Data.XML.Types as XML | 18 | import Data.XML.Types as XML |
17 | import EventUtil | 19 | import EventUtil |
18 | import Network.Address | 20 | import Network.Address |
19 | import Network.Tox.Crypto.Transport as Tox | 21 | import Network.Tox.Crypto.Transport as Tox hiding (UserStatus (..)) |
20 | import Network.Tox.NodeId | 22 | import Network.Tox.NodeId |
21 | import Util (unsplitJID) | 23 | import Util (unsplitJID) |
22 | import XMPPServer as XMPP | 24 | import XMPPServer as XMPP |
23 | 25 | ||
26 | available :: StanzaType | ||
27 | available = PresenceStatus { presenceShow = Available | ||
28 | , presencePriority = Nothing | ||
29 | , presenceStatus = [] | ||
30 | , presenceWhiteList = [] | ||
31 | } | ||
32 | |||
24 | xmppHostname :: PublicKey -> Text | 33 | xmppHostname :: PublicKey -> Text |
25 | xmppHostname k = T.pack $ show (key2id k) ++ ".tox" | 34 | xmppHostname k = T.pack $ show (key2id k) ++ ".tox" |
26 | 35 | ||
27 | toxToXmpp :: Monad m => SockAddr -> PublicKey -> Text -> Conduit Tox.CryptoMessage m XML.Event | 36 | toxUserStatus :: Word8 -> JabberShow |
37 | toxUserStatus 0 = Available | ||
38 | toxUserStatus 1 = Away | ||
39 | toxUserStatus 2 = DoNotDisturb | ||
40 | toxUserStatus _ = Away -- Default, shouldn't occur. | ||
41 | |||
42 | toxToXmpp :: Monad m => SockAddr -> PublicKey -> Text -> ConduitM Tox.CryptoMessage XML.Event m () | ||
28 | toxToXmpp laddr me theirhost = do | 43 | toxToXmpp laddr me theirhost = do |
29 | CL.sourceList $ XMPP.greet' "jabber:server" theirhost | 44 | CL.sourceList $ XMPP.greet' "jabber:server" theirhost |
30 | let me_u = Nothing | 45 | let me_u = Nothing |
@@ -35,29 +50,82 @@ toxToXmpp laddr me theirhost = do | |||
35 | -- /to/ should match local address of this node. | 50 | -- /to/ should match local address of this node. |
36 | , me_h | 51 | , me_h |
37 | , Nothing)) | 52 | , Nothing)) |
38 | awaitForever $ \case | 53 | let |
54 | statelessMessages = \case | ||
39 | 55 | ||
40 | UpToN { msgID = MESSAGE | 56 | UpToN MESSAGE bs -> |
41 | , msgBytes = bs } | ||
42 | -> do | ||
43 | xmppInstantMessage "jabber:server" im_from im_to [] (T.decodeUtf8 bs) | 57 | xmppInstantMessage "jabber:server" im_from im_to [] (T.decodeUtf8 bs) |
44 | 58 | ||
45 | TwoByte TYPING st -> xmppTyping "jabber:server" im_from im_to st | 59 | TwoByte TYPING st -> xmppTyping "jabber:server" im_from im_to st |
46 | 60 | ||
61 | UpToN NICKNAME bs -> | ||
62 | xmppInstantMessage "jabber:server" im_from im_to | ||
63 | [ attr "style" "font-weight:bold; color:red" ] | ||
64 | ("NICKNAME(todo) " <> T.decodeUtf8 bs) | ||
65 | |||
47 | toxmsg | msgID toxmsg == PacketRequest -> return () | 66 | toxmsg | msgID toxmsg == PacketRequest -> return () |
48 | 67 | ||
49 | toxmsg -> do | 68 | toxmsg -> do |
50 | xmppInstantMessage "jabber:server" | 69 | xmppInstantMessage "jabber:server" im_from im_to |
51 | im_from | ||
52 | im_to -- /to/ should match local address of this node. | ||
53 | [ attr "style" "font-weight:bold; color:red" ] | 70 | [ attr "style" "font-weight:bold; color:red" ] |
54 | (T.pack $ show $ msgID toxmsg) | 71 | (T.pack $ "Unhandled message: " ++ show (msgID toxmsg)) |
72 | |||
73 | flip fix available $ \loop status -> do | ||
74 | let go (TwoByte USERSTATUS st) = do | ||
75 | let status' = status { presenceShow = toxUserStatus st } | ||
76 | xmppPresence "jabber:server" im_from status' | ||
77 | loop status' | ||
78 | |||
79 | go (UpToN STATUSMESSAGE bs) = do | ||
80 | let status' = status { presenceStatus = [("",T.decodeUtf8 bs)] } | ||
81 | xmppPresence "jabber:server" im_from status' | ||
82 | loop status' | ||
83 | |||
84 | go (OneByte ONLINE) = do | ||
85 | xmppPresence "jabber:server" im_from status | ||
86 | loop status | ||
87 | |||
88 | go x = do | ||
89 | statelessMessages x | ||
90 | loop status | ||
91 | await >>= mapM_ go | ||
92 | |||
93 | xmppPresence :: Monad m => Text -> Maybe Text -> StanzaType -> ConduitM i XML.Event m () | ||
94 | xmppPresence namespace mjid p = do | ||
95 | let ns n = n { nameNamespace = Just namespace } | ||
96 | setFrom = maybe id | ||
97 | (\jid -> (attr "from" jid :) ) | ||
98 | mjid | ||
99 | typ Offline = [attr "type" "unavailable"] | ||
100 | typ _ = [] | ||
101 | shw ExtendedAway = ["xa"] | ||
102 | shw Chatty = ["chat"] | ||
103 | shw Away = ["away"] | ||
104 | shw DoNotDisturb = ["dnd"] | ||
105 | shw _ = [] | ||
106 | jabberShow stat = | ||
107 | [ EventBeginElement "{jabber:client}show" [] | ||
108 | , EventContent (ContentText stat) | ||
109 | , EventEndElement "{jabber:client}show" ] | ||
110 | C.yield $ EventBeginElement (ns "presence") (setFrom $ typ $ presenceShow p) | ||
111 | mapM_ C.yield $ shw (presenceShow p) >>= jabberShow | ||
112 | forM_ (presencePriority p) $ \prio -> do | ||
113 | C.yield $ EventBeginElement (ns "priority") [] | ||
114 | C.yield $ EventContent $ ContentText (T.pack $ show prio) | ||
115 | C.yield $ EventEndElement (ns "priority") | ||
116 | forM_ (presenceStatus p) $ \(lang,txt) -> do | ||
117 | let atts | T.null lang = [] | ||
118 | | otherwise = [ ("xml:lang", [ContentText lang]) ] | ||
119 | C.yield $ EventBeginElement (ns "status") atts | ||
120 | C.yield $ EventContent $ ContentText txt | ||
121 | C.yield $ EventEndElement (ns "status") | ||
122 | C.yield $ EventEndElement (ns "presence") | ||
55 | 123 | ||
56 | xmppTyping :: Monad m => Text | 124 | xmppTyping :: Monad m => Text |
57 | -> Maybe Text | 125 | -> Maybe Text |
58 | -> Maybe Text | 126 | -> Maybe Text |
59 | -> Word8 | 127 | -> Word8 |
60 | -> ConduitM i Event m () | 128 | -> ConduitM i XML.Event m () |
61 | xmppTyping namespace mfrom mto x = | 129 | xmppTyping namespace mfrom mto x = |
62 | let ns n = n { nameNamespace = Just namespace } | 130 | let ns n = n { nameNamespace = Just namespace } |
63 | st = case x of | 131 | st = case x of |
@@ -80,7 +148,7 @@ xmppInstantMessage :: Monad m => Text | |||
80 | -> Maybe Text | 148 | -> Maybe Text |
81 | -> [(Name, [Content])] | 149 | -> [(Name, [Content])] |
82 | -> Text | 150 | -> Text |
83 | -> ConduitM i Event m () | 151 | -> ConduitM i XML.Event m () |
84 | xmppInstantMessage namespace mfrom mto style text = do | 152 | xmppInstantMessage namespace mfrom mto style text = do |
85 | let ns n = n { nameNamespace = Just namespace } | 153 | let ns n = n { nameNamespace = Just namespace } |
86 | C.yield $ EventBeginElement (ns "message") | 154 | C.yield $ EventBeginElement (ns "message") |