summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ToxManager.hs154
-rw-r--r--dht-client.cabal1
-rw-r--r--examples/dhtd.hs115
3 files changed, 156 insertions, 114 deletions
diff --git a/ToxManager.hs b/ToxManager.hs
new file mode 100644
index 00000000..81def17f
--- /dev/null
+++ b/ToxManager.hs
@@ -0,0 +1,154 @@
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE NamedFieldPuns #-}
4module ToxManager where
5
6import Announcer
7import Connection
8-- import Control.Concurrent
9import Control.Concurrent.STM
10import Control.Monad
11import Crypto.Tox
12import Data.HashMap.Strict as HashMap
13import Data.Maybe
14import qualified Data.Set as Set
15import qualified Data.Text as T
16import Data.Time.Clock.POSIX
17import Network.Address
18import Network.Kademlia.Routing as R
19import Network.Kademlia.Search
20import qualified Network.Tox as Tox
21import Network.Tox.ContactInfo as Tox
22import qualified Network.Tox.Crypto.Handlers as Tox
23-- import qualified Network.Tox.DHT.Handlers as Tox
24import qualified Network.Tox.DHT.Transport as Tox
25import qualified Network.Tox.Onion.Handlers as Tox
26import qualified Network.Tox.Onion.Transport as Tox
27import Presence
28import System.IO
29import Text.Read
30import ToxToXMPP
31import XMPPServer (ConnectionKey)
32
33#ifdef THREAD_DEBUG
34import Control.Concurrent.Lifted.Instrument
35#else
36import Control.Concurrent.Lifted
37import GHC.Conc (labelThread)
38#endif
39
40toxAnnounceInterval :: POSIXTime
41toxAnnounceInterval = 15
42
43toxAnnounceSendData :: Tox.Tox -> PublicKey
44 -> Nonce32
45 -> Maybe Tox.NodeInfo
46 -> IO (Maybe (Tox.Rendezvous, Tox.AnnounceResponse))
47toxAnnounceSendData tox pubkey token = \case
48 Just ni -> Tox.putRendezvous (Tox.onionTimeout tox)
49 (Tox.toxCryptoKeys tox)
50 (Tox.toxOnion tox)
51 (pubkey :: PublicKey)
52 (token :: Nonce32)
53 ni
54 Nothing -> return Nothing
55
56
57toxQSearch :: Tox.Tox -> Search Tox.NodeId (IP, PortNumber) Nonce32 Tox.NodeInfo Tox.Rendezvous
58toxQSearch tox = Tox.toxidSearch (Tox.onionTimeout tox) (Tox.toxCryptoKeys tox) (Tox.toxOnion tox)
59
60-- |
61--
62-- These hooks will be invoked in order to connect to *.tox hosts in a user's
63-- XMPP roster.
64toxman :: Announcer -> [(String,TVar (BucketList Tox.NodeInfo))] -> Tox.Tox -> PresenceState -> ToxManager ConnectionKey
65toxman announcer toxbkts tox presence = ToxManager
66 { activateAccount = \k pubname seckey -> do
67 hPutStrLn stderr $ "toxman ACTIVATE " ++ show (Tox.key2id $ toPublic seckey)
68 let ContactInfo{ accounts } = Tox.toxContactInfo tox
69 pub = toPublic seckey
70 pubid = Tox.key2id pub
71 (acnt,newlyActive) <- atomically $ do
72 macnt <- HashMap.lookup pubid <$> readTVar accounts
73 acnt <- maybe (newAccount seckey) return macnt
74 rs <- readTVar $ clientRefs acnt
75 writeTVar (clientRefs acnt) $! Set.insert k rs
76 modifyTVar accounts (HashMap.insert pubid acnt)
77 if not (Set.null rs)
78 then return (acnt,[])
79 else do
80 fmap ((,) acnt) $ forM toxbkts $ \(nm,bkts) -> do
81 akey <- packAnnounceKey announcer (nm ++ "id:" ++ show pubid)
82 return (akey,bkts)
83 forM_ newlyActive $ \(akey,bkts) -> do
84 -- Schedule recurring announce.
85 --
86 schedule announcer
87 akey
88 (AnnounceMethod (toxQSearch tox)
89 (Right $ toxAnnounceSendData tox)
90 bkts
91 pubid
92 toxAnnounceInterval)
93 pub
94
95 forkAccountWatcher acnt tox presence
96 return ()
97
98 , deactivateAccount = \k pubname -> do
99 hPutStrLn stderr $ "toxman DECTIVATE (todo) 1 " ++ show pubname
100 let ContactInfo{ accounts } = Tox.toxContactInfo tox
101 mpubid = readMaybe $ T.unpack $ T.take 43 pubname
102 bStopped <- fmap (fromMaybe Nothing) $ atomically $ do
103 forM mpubid $ \pubid -> do
104 refs <- do
105 macnt <- HashMap.lookup pubid <$> readTVar accounts
106 rs <- fromMaybe Set.empty <$> mapM (readTVar . clientRefs) macnt
107 forM_ macnt $ \acnt -> do
108 modifyTVar' (clientRefs acnt) $ Set.delete k
109 return rs
110 if (Set.null $ refs Set.\\ Set.singleton k) then do
111 -- TODO
112 -- If this is the last reference to a non-connected contact:
113 -- Stop the recurring search for that contact
114 --
115 -- Stop recurring announce.
116 fmap Just $ forM toxbkts $ \(nm,bkts) -> do
117 akey <- packAnnounceKey announcer (nm ++ "id:" ++ show pubid)
118 return (akey,bkts)
119 else return Nothing
120 forM_ bStopped $ \kbkts -> do
121 hPutStrLn stderr $ "toxman DECTIVATE (todo) 3 " ++ show pubname
122 let Just pubid = mpubid
123 pub = Tox.id2key pubid
124 forM_ kbkts $ \(akey,bkts) -> do
125 cancel announcer
126 akey
127 (AnnounceMethod (toxQSearch tox)
128 (Right $ toxAnnounceSendData tox)
129 bkts
130 pubid
131 toxAnnounceInterval)
132 pub
133
134 , setToxConnectionPolicy = \me them p -> do
135 let m = do meid <- readMaybe $ T.unpack $ T.take 43 me
136 themid <- readMaybe $ T.unpack $ T.take 43 them
137 return $ Tox.Key meid themid
138 hPutStrLn stderr $ "toxman ConnectionPolicy " ++ show (me,them,p,fmap (const ()) m)
139 forM_ m $ \k -> do
140 setPolicy (Tox.toxMgr tox) k p
141 case p of
142 TryingToConnect -> do
143 let db@ContactInfo{ accounts } = Tox.toxContactInfo tox
144 sequence_ $ do
145 let Tox.Key meid themid = k
146 Just $ atomically $ do
147 accs <- readTVar accounts
148 case HashMap.lookup meid accs of
149 Nothing -> return () -- Unknown account.
150 Just acc -> setContactPolicy (Tox.id2key themid) TryingToConnect acc
151 -- If unscheduled and unconnected, schedule recurring search for this contact.
152 _ -> return () -- Remove contact.
153 }
154
diff --git a/dht-client.cabal b/dht-client.cabal
index 0ac652ce..9dc5ceb9 100644
--- a/dht-client.cabal
+++ b/dht-client.cabal
@@ -141,6 +141,7 @@ library
141 PingMachine 141 PingMachine
142 Connection 142 Connection
143 ToxToXMPP 143 ToxToXMPP
144 ToxManager
144 145
145 build-depends: base 146 build-depends: base
146 , containers 147 , containers
diff --git a/examples/dhtd.hs b/examples/dhtd.hs
index 56d9544d..28e9f261 100644
--- a/examples/dhtd.hs
+++ b/examples/dhtd.hs
@@ -67,6 +67,7 @@ import System.Posix.Signals
67 67
68 68
69import Announcer 69import Announcer
70import ToxManager
70import Crypto.Tox -- (zeros32,SecretKey,PublicKey, generateSecretKey, toPublic, encodeSecret, decodeSecret, userKeys) 71import Crypto.Tox -- (zeros32,SecretKey,PublicKey, generateSecretKey, toPublic, encodeSecret, decodeSecret, userKeys)
71import Network.UPNP as UPNP 72import Network.UPNP as UPNP
72import Network.Address hiding (NodeId, NodeInfo(..)) 73import Network.Address hiding (NodeId, NodeInfo(..))
@@ -1300,120 +1301,6 @@ newXmmpSink session@(Tox.NCrypto { ncOutgoingQueue = outGoingQVar, ncPacketQueue
1300 liftIO $ sendit session flush_cyptomessage 1301 liftIO $ sendit session flush_cyptomessage
1301 1302
1302 1303
1303toxAnnounceSendData :: Tox.Tox -> PublicKey
1304 -> Nonce32
1305 -> Maybe Tox.NodeInfo
1306 -> IO (Maybe (Tox.Rendezvous, Tox.AnnounceResponse))
1307toxAnnounceSendData tox pubkey token = \case
1308 Just ni -> Tox.putRendezvous (Tox.onionTimeout tox)
1309 (Tox.toxCryptoKeys tox)
1310 (Tox.toxOnion tox)
1311 (pubkey :: PublicKey)
1312 (token :: Nonce32)
1313 ni
1314 Nothing -> return Nothing
1315
1316toxQSearch :: Tox.Tox -> Search Tox.NodeId (IP, PortNumber) Nonce32 Tox.NodeInfo Tox.Rendezvous
1317toxQSearch tox = Tox.toxidSearch (Tox.onionTimeout tox) (Tox.toxCryptoKeys tox) (Tox.toxOnion tox)
1318
1319toxAnnounceInterval :: POSIXTime
1320toxAnnounceInterval = 15
1321
1322-- |
1323--
1324-- These hooks will be invoked in order to connect to *.tox hosts in a user's
1325-- XMPP roster.
1326toxman :: Announcer -> [(String,TVar (BucketList Tox.NodeInfo))] -> Tox.Tox -> PresenceState -> ToxManager ConnectionKey
1327toxman announcer toxbkts tox presence = ToxManager
1328 { activateAccount = \k pubname seckey -> do
1329 hPutStrLn stderr $ "toxman ACTIVATE " ++ show (Tox.key2id $ toPublic seckey)
1330 let ContactInfo{ accounts } = Tox.toxContactInfo tox
1331 pub = toPublic seckey
1332 pubid = Tox.key2id pub
1333 (acnt,newlyActive) <- atomically $ do
1334 macnt <- HashMap.lookup pubid <$> readTVar accounts
1335 acnt <- maybe (newAccount seckey) return macnt
1336 rs <- readTVar $ clientRefs acnt
1337 writeTVar (clientRefs acnt) $! Set.insert k rs
1338 modifyTVar accounts (HashMap.insert pubid acnt)
1339 if not (Set.null rs)
1340 then return (acnt,[])
1341 else do
1342 fmap ((,) acnt) $ forM toxbkts $ \(nm,bkts) -> do
1343 akey <- packAnnounceKey announcer (nm ++ "id:" ++ show pubid)
1344 return (akey,bkts)
1345 forM_ newlyActive $ \(akey,bkts) -> do
1346 -- Schedule recurring announce.
1347 --
1348 schedule announcer
1349 akey
1350 (AnnounceMethod (toxQSearch tox)
1351 (Right $ toxAnnounceSendData tox)
1352 bkts
1353 pubid
1354 toxAnnounceInterval)
1355 pub
1356
1357 forkAccountWatcher acnt tox presence
1358 return ()
1359
1360 , deactivateAccount = \k pubname -> do
1361 hPutStrLn stderr $ "toxman DECTIVATE (todo) 1 " ++ show pubname
1362 let ContactInfo{ accounts } = Tox.toxContactInfo tox
1363 mpubid = readMaybe $ T.unpack $ T.take 43 pubname
1364 bStopped <- fmap (fromMaybe Nothing) $ atomically $ do
1365 forM mpubid $ \pubid -> do
1366 refs <- do
1367 macnt <- HashMap.lookup pubid <$> readTVar accounts
1368 rs <- fromMaybe Set.empty <$> mapM (readTVar . clientRefs) macnt
1369 forM_ macnt $ \acnt -> do
1370 modifyTVar' (clientRefs acnt) $ Set.delete k
1371 return rs
1372 if (Set.null $ refs Set.\\ Set.singleton k) then do
1373 -- TODO
1374 -- If this is the last reference to a non-connected contact:
1375 -- Stop the recurring search for that contact
1376 --
1377 -- Stop recurring announce.
1378 fmap Just $ forM toxbkts $ \(nm,bkts) -> do
1379 akey <- packAnnounceKey announcer (nm ++ "id:" ++ show pubid)
1380 return (akey,bkts)
1381 else return Nothing
1382 forM_ bStopped $ \kbkts -> do
1383 hPutStrLn stderr $ "toxman DECTIVATE (todo) 3 " ++ show pubname
1384 let Just pubid = mpubid
1385 pub = Tox.id2key pubid
1386 forM_ kbkts $ \(akey,bkts) -> do
1387 cancel announcer
1388 akey
1389 (AnnounceMethod (toxQSearch tox)
1390 (Right $ toxAnnounceSendData tox)
1391 bkts
1392 pubid
1393 toxAnnounceInterval)
1394 pub
1395
1396 , setToxConnectionPolicy = \me them p -> do
1397 let m = do meid <- readMaybe $ T.unpack $ T.take 43 me
1398 themid <- readMaybe $ T.unpack $ T.take 43 them
1399 return $ Tox.Key meid themid
1400 hPutStrLn stderr $ "toxman ConnectionPolicy " ++ show (me,them,p,fmap (const ()) m)
1401 forM_ m $ \k -> do
1402 setPolicy (Tox.toxMgr tox) k p
1403 case p of
1404 TryingToConnect -> do
1405 let db@ContactInfo{ accounts } = Tox.toxContactInfo tox
1406 sequence_ $ do
1407 let Tox.Key meid themid = k
1408 Just $ atomically $ do
1409 accs <- readTVar accounts
1410 case HashMap.lookup meid accs of
1411 Nothing -> return () -- Unknown account.
1412 Just acc -> setContactPolicy (Tox.id2key themid) TryingToConnect acc
1413 -- If unscheduled and unconnected, schedule recurring search for this contact.
1414 _ -> return () -- Remove contact.
1415 }
1416
1417-- | Called upon a new Tox friend-connection session with a remote peer in 1304-- | Called upon a new Tox friend-connection session with a remote peer in
1418-- order to set up translating conduits that simulate a remote XMPP server. 1305-- order to set up translating conduits that simulate a remote XMPP server.
1419announceToxJabberPeer :: PublicKey -- ^ Remote tox node's long-term user key. 1306announceToxJabberPeer :: PublicKey -- ^ Remote tox node's long-term user key.