summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Presence/Presence.hs48
-rw-r--r--Presence/XMPPServer.hs1
-rw-r--r--ToxManager.hs14
-rw-r--r--ToxToXMPP.hs70
-rw-r--r--XMPPToTox.hs67
-rw-r--r--examples/dhtd.hs14
-rw-r--r--examples/test-xmpp.hs41
7 files changed, 213 insertions, 42 deletions
diff --git a/Presence/Presence.hs b/Presence/Presence.hs
index e8a69066..53de4e93 100644
--- a/Presence/Presence.hs
+++ b/Presence/Presence.hs
@@ -85,6 +85,11 @@ data ToxManager k = ToxManager
85 -- their public key (in hostname format) and the Policy to set for this 85 -- their public key (in hostname format) and the Policy to set for this
86 -- link. 86 -- link.
87 , setToxConnectionPolicy :: Text -> Text -> Connection.Policy -> IO () 87 , setToxConnectionPolicy :: Text -> Text -> Connection.Policy -> IO ()
88 -- | Given a remote Tox key, return the address of a connected peer.
89 --
90 -- The arguments are our public key (in base64 format) followed by
91 -- their public key (in base64 format).
92 , resolveToxPeer :: Text -> Text -> IO (Maybe PeerAddress)
88 } 93 }
89 94
90data PresenceState = forall status. PresenceState 95data PresenceState = forall status. PresenceState
@@ -577,10 +582,25 @@ deliverMessage state fail msg =
577 case stanzaOrigin msg of 582 case stanzaOrigin msg of
578 ClientOrigin senderk _ -> do 583 ClientOrigin senderk _ -> do
579 -- Case 1. Client -> Peer 584 -- Case 1. Client -> Peer
580 mto <- fmap join $ mapM rewriteJIDForPeer (stanzaTo msg) 585 mto <- join $ atomically $ do
581 fromMaybe fail {- reverse lookup failure -} $ mto <&> \(to',k) -> do 586 mclient <- Map.lookup senderk <$> readTVar (clients state)
587 return
588 $ fromMaybe -- Resolve XMPP peer.
589 (fmap join $ mapM rewriteJIDForPeer (stanzaTo msg))
590 $ do (mu,h,rsc) <- splitJID <$> stanzaTo msg
591 u <- mu
592 client <- mclient
593 (toxman,me,them) <- weAreTox state client h
594 return -- Resolve Tox peer.
595 $ do maddr <- resolveToxPeer toxman me them
596 return $ fmap (u,) maddr
597 fromMaybe (do dput XJabber $ "Unable to resolve "++show (stanzaTo msg)
598 fail {- reverse lookup failure -})
599 $ mto <&> \(to',k) -> do
582 chans <- atomically $ readTVar (pkeyToChan state) 600 chans <- atomically $ readTVar (pkeyToChan state)
583 fromMaybe fail $ (Map.lookup k chans) <&> \conn -> do 601 fromMaybe (do dput XJabber $ "Peer unavailable: "++ show k
602 fail)
603 $ (Map.lookup k chans) <&> \conn -> do
584 -- original 'from' address is discarded. 604 -- original 'from' address is discarded.
585 from' <- forClient state senderk (return Nothing) 605 from' <- forClient state senderk (return Nothing)
586 $ return . Just . clientJID conn 606 $ return . Just . clientJID conn
@@ -592,12 +612,18 @@ deliverMessage state fail msg =
592 pc <- readTVar (pkeyToChan state) 612 pc <- readTVar (pkeyToChan state)
593 cc <- readTVar (ckeyToChan state) 613 cc <- readTVar (ckeyToChan state)
594 return (pc,cc) 614 return (pc,cc)
595 fromMaybe fail $ (Map.lookup senderk pchans) 615 fromMaybe (do dput XJabber $ "Unknown peer " ++ show senderk
616 fail)
617 $ Map.lookup senderk pchans
596 <&> \(Conn { connChan = sender_chan 618 <&> \(Conn { connChan = sender_chan
597 , auxData = ConnectionData (Left laddr) ctyp cprof }) -> do 619 , auxData = ConnectionData (Left laddr) ctyp cprof }) -> do
598 fromMaybe fail $ (stanzaTo msg) <&> \to -> do 620 fromMaybe (do dput XJabber $ "Message missing \"to\" attribute."
621 fail)
622 $ (stanzaTo msg) <&> \to -> do
599 (mine,(n,h,r)) <- rewriteJIDForClient laddr to [] 623 (mine,(n,h,r)) <- rewriteJIDForClient laddr to []
600 if not mine then fail else do 624 if not mine then do dput XJabber $ "Address mis-match " ++ show (laddr,to)
625 fail
626 else do
601 let to' = unsplitJID (n,h,r) 627 let to' = unsplitJID (n,h,r)
602 let (cmapVar,ckey) = case ctyp of 628 let (cmapVar,ckey) = case ctyp of
603 Tox -> (clientsByProfile state , Just cprof ) 629 Tox -> (clientsByProfile state , Just cprof )
@@ -1024,14 +1050,18 @@ clientSubscriptionRequest state fail k stanza chan = do
1024 , stanzaFrom = Just from }) 1050 , stanzaFrom = Just from })
1025 (connChan con) 1051 (connChan con)
1026 let policySetter = fromMaybe (Connection.setPolicy conns h) $ do 1052 let policySetter = fromMaybe (Connection.setPolicy conns h) $ do
1027 toxman <- toxManager state 1053 (toxman,_,_) <- weAreTox state client h
1028 (me , ".tox") <- Just $ Text.splitAt 43 (clientProfile client)
1029 (them, ".tox") <- Just $ Text.splitAt 43 h
1030 Just $ setToxConnectionPolicy toxman (clientProfile client) h 1054 Just $ setToxConnectionPolicy toxman (clientProfile client) h
1031 -- Add peer if we are not already associated ... 1055 -- Add peer if we are not already associated ...
1032 policySetter Connection.TryingToConnect 1056 policySetter Connection.TryingToConnect
1033 atomically $ putTMVar svVar (sv,conns) 1057 atomically $ putTMVar svVar (sv,conns)
1034 1058
1059weAreTox :: PresenceState -> ClientState -> Text -> Maybe (ToxManager ClientAddress,Text{- me -},Text{- them -})
1060weAreTox state client h = do
1061 toxman <- toxManager state
1062 (me , ".tox") <- Just $ Text.splitAt 43 (clientProfile client)
1063 (them, ".tox") <- Just $ Text.splitAt 43 h
1064 return (toxman,me,them)
1035 1065
1036resolvedFromRoster 1066resolvedFromRoster
1037 :: (ConfigFiles.User -> ConfigFiles.Profile -> IO [L.ByteString]) 1067 :: (ConfigFiles.User -> ConfigFiles.Profile -> IO [L.ByteString])
diff --git a/Presence/XMPPServer.hs b/Presence/XMPPServer.hs
index 79335efc..ae861a61 100644
--- a/Presence/XMPPServer.hs
+++ b/Presence/XMPPServer.hs
@@ -41,6 +41,7 @@ module XMPPServer
41 , flushPassThrough 41 , flushPassThrough
42 , greet' 42 , greet'
43 , (<&>) 43 , (<&>)
44 , grokStanza
44 ) where 45 ) where
45 46
46import ConnectionKey 47import ConnectionKey
diff --git a/ToxManager.hs b/ToxManager.hs
index d16a5d2e..6d0149cf 100644
--- a/ToxManager.hs
+++ b/ToxManager.hs
@@ -26,7 +26,7 @@ import qualified Network.Tox.Onion.Transport as Tox
26import Presence 26import Presence
27import Text.Read 27import Text.Read
28import ToxToXMPP 28import ToxToXMPP
29import XMPPServer (ClientAddress) 29import XMPPServer
30import DPut 30import DPut
31 31
32 32
@@ -140,5 +140,17 @@ toxman announcer toxbkts tox presence = ToxManager
140 Just acc -> setContactPolicy (Tox.id2key themid) TryingToConnect acc 140 Just acc -> setContactPolicy (Tox.id2key themid) TryingToConnect acc
141 -- If unscheduled and unconnected, schedule recurring search for this contact. 141 -- If unscheduled and unconnected, schedule recurring search for this contact.
142 _ -> return () -- Remove contact. 142 _ -> return () -- Remove contact.
143 , resolveToxPeer = \me them -> do
144 let lookupContact accs
145 = do meid <- readMaybe $ T.unpack me
146 themid <- readMaybe $ T.unpack them
147 acc <- HashMap.lookup meid accs
148 return $ HashMap.lookup themid <$> readTVar (contacts acc)
149 atomically $ do
150 accs <- let ContactInfo{ accounts } = Tox.toxContactInfo tox
151 in readTVar accounts
152 mc <- join <$> sequence (lookupContact accs)
153 maddr <- join <$> mapM (readTVar . contactLastSeenAddr) mc
154 return $ addrToPeerKey . Remote . Tox.nodeAddr . snd <$> maddr
143 } 155 }
144 156
diff --git a/ToxToXMPP.hs b/ToxToXMPP.hs
index b75ada6a..9979526a 100644
--- a/ToxToXMPP.hs
+++ b/ToxToXMPP.hs
@@ -15,14 +15,19 @@ module ToxToXMPP
15 , interweave 15 , interweave
16 ) where 16 ) where
17 17
18import Data.Conduit as C 18import Control.Applicative
19import qualified Data.Conduit.List as CL 19import Data.Conduit as C
20import Data.XML.Types as XML 20import qualified Data.Conduit.List as CL
21import Data.Monoid
22import Data.Text.Encoding as T
23import Data.XML.Types as XML
21import EventUtil 24import EventUtil
22import Network.Tox.Crypto.Transport as Tox 25import Network.Tox.Crypto.Transport as Tox
23import Network.Tox.Handshake (HandshakeParams (..)) 26import Network.Tox.Handshake (HandshakeParams (..))
24import Util (unsplitJID) 27import qualified Text.XML.Stream.Parse as XML
25import XMPPServer as XMPP 28import Util (unsplitJID)
29import XMPPServer as XMPP
30
26 31
27import Announcer 32import Announcer
28import Announcer.Tox 33import Announcer.Tox
@@ -71,31 +76,39 @@ import GHC.Conc (labelThread)
71#endif 76#endif
72import DPut 77import DPut
73import Nesting 78import Nesting
74 79import XMPPToTox
75xmppToTox :: Conduit XML.Event IO Tox.CryptoMessage
76xmppToTox = doNestingXML $ do
77 eventBeginDocument <- await
78 streamTag <- await
79 fix $ \loop -> do
80 e <- nextElement
81 -- dput DPut.XMan $ "xmppToTox: " ++ show e
82 --
83 -- (yield e >> awaitForever yield) $$ prettyPrint "xmpp->Tox"
84 -- prettyPrint
85 loop
86 80
87toxToXmpp :: Monad m => SockAddr -> PublicKey -> Text -> Conduit Tox.CryptoMessage m XML.Event 81toxToXmpp :: Monad m => SockAddr -> PublicKey -> Text -> Conduit Tox.CryptoMessage m XML.Event
88toxToXmpp laddr me theirhost = do 82toxToXmpp laddr me theirhost = do
89 CL.sourceList $ XMPP.greet' "jabber:server" theirhost 83 CL.sourceList $ XMPP.greet' "jabber:server" theirhost
90 let me_u = T.pack $ show (key2id me) 84 let me_u = T.pack $ show (key2id me)
91 awaitForever $ \toxmsg -> do 85 awaitForever $ \case
92 xmppInstantMessage "jabber:server" 86
93 (Just theirhost) -- /from/ 87 UpToN { msgID = MESSAGE
94 (Just $ unsplitJID (Just me_u,T.pack (show laddr),Nothing)) -- /to/ should match local address of this node. 88 , msgBytes = bs }
95 (T.pack $ show $ msgID toxmsg) 89 -> do
96 90 xmppInstantMessage "jabber:server"
97xmppInstantMessage :: Monad m => Text -> Maybe Text -> Maybe Text -> Text -> ConduitM i Event m () 91 (Just $ "root@" <> theirhost) -- /from/
98xmppInstantMessage namespace mfrom mto text = do 92 (Just $ unsplitJID (Just me_u,T.pack (show laddr),Nothing)) -- /to/ should match local address of this node.
93 []
94 (T.decodeUtf8 bs)
95
96 toxmsg | msgID toxmsg == PacketRequest -> return ()
97
98 toxmsg -> do
99 xmppInstantMessage "jabber:server"
100 (Just theirhost) -- /from/
101 (Just $ unsplitJID (Just me_u,T.pack (show laddr),Nothing)) -- /to/ should match local address of this node.
102 [ attr "style" "font-weight:bold; color:red" ]
103 (T.pack $ show $ msgID toxmsg)
104
105xmppInstantMessage :: Monad m => Text
106 -> Maybe Text
107 -> Maybe Text
108 -> [(Name, [Content])]
109 -> Text
110 -> ConduitM i Event m ()
111xmppInstantMessage namespace mfrom mto style text = do
99 let ns n = n { nameNamespace = Just namespace } 112 let ns n = n { nameNamespace = Just namespace }
100 C.yield $ EventBeginElement (ns "message") 113 C.yield $ EventBeginElement (ns "message")
101 ( maybe id (\t->(attr "from" t:)) mfrom 114 ( maybe id (\t->(attr "from" t:)) mfrom
@@ -106,8 +119,7 @@ xmppInstantMessage namespace mfrom mto text = do
106 C.yield $ EventEndElement (ns "body") 119 C.yield $ EventEndElement (ns "body")
107 C.yield $ EventBeginElement "{http://jabber.org/protocol/xhtml-im}html" [] 120 C.yield $ EventBeginElement "{http://jabber.org/protocol/xhtml-im}html" []
108 C.yield $ EventBeginElement "{http://www.w3.org/1999/xhtml}body" [] 121 C.yield $ EventBeginElement "{http://www.w3.org/1999/xhtml}body" []
109 C.yield $ EventBeginElement "{http://www.w3.org/1999/xhtml}p" 122 C.yield $ EventBeginElement "{http://www.w3.org/1999/xhtml}p" style
110 [ attr "style" "font-weight:bold; color:red" ]
111 C.yield $ EventContent $ ContentText text 123 C.yield $ EventContent $ ContentText text
112 C.yield $ EventEndElement "{http://www.w3.org/1999/xhtml}p" 124 C.yield $ EventEndElement "{http://www.w3.org/1999/xhtml}p"
113 C.yield $ EventEndElement "{http://www.w3.org/1999/xhtml}body" 125 C.yield $ EventEndElement "{http://www.w3.org/1999/xhtml}body"
diff --git a/XMPPToTox.hs b/XMPPToTox.hs
new file mode 100644
index 00000000..7ca4330e
--- /dev/null
+++ b/XMPPToTox.hs
@@ -0,0 +1,67 @@
1{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE NoMonomorphismRestriction #-}
3module XMPPToTox
4 ( module XMPPToTox
5 , CryptoMessage(..)
6 , MessageID(..)
7 ) where
8
9import Control.Applicative
10import Control.Monad
11import Control.Monad.Catch
12import Data.Conduit
13import Data.Function
14import Data.Monoid
15import Data.Text (Text)
16import Data.Text.Encoding as T
17import Data.XML.Types as XML
18import Network.Tox.Crypto.Transport (CryptoMessage (..), MessageID (..))
19import Text.XML.Stream.Parse as XML
20
21-- Debugging. Not real Tox message.
22funnyMessage :: MonadThrow m => Text -> ConduitM i CryptoMessage m ()
23funnyMessage txt = yield $ UpToN Padding (T.encodeUtf8 txt)
24
25sendMsg :: MonadThrow m => Text -> ConduitM i CryptoMessage m ()
26sendMsg txt = yield $ UpToN MESSAGE (T.encodeUtf8 txt)
27
28
29eom :: MonadThrow m => ConduitM Event o m ()
30eom = many_ ignoreAnyTreeContent
31
32msgToTox :: MonadThrow m =>
33 ConduitM Event CryptoMessage m (Maybe ())
34msgToTox = tag' "{jabber:server}message"
35 (requireAttr "type" >>= \case
36 "chat" -> ignoreAttrs
37 _ -> empty)
38 $ \_ -> many_ $ choose
39 [ tagIgnoreAttrs "{jabber:server}body"
40 $ do content >>= sendMsg
41 eom
42 , tagIgnoreAttrs "{http://jabber.org/protocol/chatstates}composing"
43 $ do yield $ TwoByte TYPING 1
44 eom
45 , tagIgnoreAttrs "{http://jabber.org/protocol/chatstates}paused"
46 $ do yield $ TwoByte TYPING 0
47 eom
48 , ignoreAnyTreeContent
49 ]
50
51unknownToTox :: MonadThrow m =>
52 ConduitM Event CryptoMessage m (Maybe ())
53unknownToTox = tag anyName (\n -> ignoreAttrs >> return n) $ \n -> do
54 funnyMessage $ nameLocalName n
55 eom
56
57
58xmppToTox :: MonadThrow m => Conduit XML.Event m CryptoMessage
59xmppToTox = do
60 eventBeginDocument <- await
61 streamTag <- await
62 fix $ \loop -> do
63 got <- choose
64 [ msgToTox
65 , unknownToTox
66 ]
67 forM_ got $ \_ -> loop
diff --git a/examples/dhtd.hs b/examples/dhtd.hs
index fefec650..e099334e 100644
--- a/examples/dhtd.hs
+++ b/examples/dhtd.hs
@@ -1982,14 +1982,22 @@ main = do
1982 xmppSink = newXmmpSink netcrypto 1982 xmppSink = newXmmpSink netcrypto
1983 forM_ msv $ \sv -> do 1983 forM_ msv $ \sv -> do
1984 let Tox.HaveDHTKey saddr = Tox.ncSockAddr netcrypto 1984 let Tox.HaveDHTKey saddr = Tox.ncSockAddr netcrypto
1985 announceToxJabberPeer (Tox.ncMyPublicKey netcrypto) (Tox.ncTheirPublicKey netcrypto) (xmppEventChannel sv) addrTox saddr pingflag xmppSrc xmppSink 1985 Tox.HaveDHTKey dkey = Tox.ncTheirDHTKey netcrypto
1986 nid = Tox.key2id dkey
1987 them = Tox.ncTheirPublicKey netcrypto
1988 me = Tox.ncMyPublicKey netcrypto
1989 announceToxJabberPeer me them (xmppEventChannel sv) addrTox saddr pingflag xmppSrc xmppSink
1986 forM_ mbtox $ \tox -> do 1990 forM_ mbtox $ \tox -> do
1987 let ContactInfo{accounts} = Tox.toxContactInfo tox 1991 let ContactInfo{accounts} = Tox.toxContactInfo tox
1988 mbacc <- HashMap.lookup (Tox.key2id $ Tox.ncMyPublicKey netcrypto) 1992 mbacc <- HashMap.lookup (Tox.key2id me)
1989 <$> atomically (readTVar accounts) 1993 <$> atomically (readTVar accounts)
1994 -- TODO: Add account if it doesn't exist?
1990 forM_ mbacc $ \acnt -> do 1995 forM_ mbacc $ \acnt -> do
1991 now <- getPOSIXTime 1996 now <- getPOSIXTime
1992 atomically $ setEstablished now (Tox.ncTheirPublicKey netcrypto) acnt 1997 forM_ (either (const Nothing) Just $ Tox.nodeInfo nid saddr)
1998 $ \ni -> do
1999 atomically $ do setEstablished now them acnt
2000 setContactAddr now them ni acnt
1993 atomically $ do 2001 atomically $ do
1994 supply <- readTVar (Tox.listenerIDSupply netCryptoSessionsState) 2002 supply <- readTVar (Tox.listenerIDSupply netCryptoSessionsState)
1995 let (listenerId,supply') = freshId supply 2003 let (listenerId,supply') = freshId supply
diff --git a/examples/test-xmpp.hs b/examples/test-xmpp.hs
new file mode 100644
index 00000000..a8e20c3c
--- /dev/null
+++ b/examples/test-xmpp.hs
@@ -0,0 +1,41 @@
1
2import Control.Monad.IO.Class
3import Control.Monad.Trans.Resource
4-- import Control.Monad.Trans.Class
5import Data.Conduit
6import Data.Conduit.List as CL
7-- import Data.XML.Types
8import System.Environment
9import Text.XML.Stream.Parse
10
11import XMPPToTox
12
13{-
14parse :: ConduitM Event o (ResourceT IO) ()
15parse = do
16 return ()
17-}
18
19showTox :: CryptoMessage -> ResourceT IO ()
20showTox = liftIO . print
21
22main :: IO ()
23main = do
24 args <- getArgs
25 let xmlfile = args !! 0
26
27 -- runConduit :: Monad m => ConduitM () Void m r -> m r
28
29 -- test-xmpp.hs:19:51: warning: [-Wdeprecations]
30 -- In the use of ‘$$’
31 -- (imported from Data.Conduit, but defined in conduit-1.3.0.3:Data.Conduit.Internal.Conduit):
32 -- Deprecated: "Use runConduit and .|"
33 --
34 -- runResourceT $ parseFile def xmlfile =$= parse $$ return ()
35
36 runResourceT $ runConduit $ do
37 parseFile def xmlfile
38 .| xmppToTox
39 -- CL.mapM_ :: Monad m => (a -> m ()) -> ConduitT a o m ()
40 .| CL.mapM_ showTox
41