summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2019-12-23 02:09:05 -0500
committerJoe Crayne <joe@jerkface.net>2020-01-01 23:28:00 -0500
commitaf2d131e01fed76205c2c0c32a2f29bab8cceb84 (patch)
tree1332a0cdac6d3870db39662ac27260fc04c2dc21
parent50e1debec25341ca66456ab14574361a2a994787 (diff)
Create new tox key automatically + disable non-tox operation.
-rw-r--r--dht/Presence/ConfigFiles.hs12
-rw-r--r--dht/Presence/Presence.hs125
-rw-r--r--dht/Presence/XMPPServer.hs65
-rw-r--r--dht/dht-client.cabal2
4 files changed, 121 insertions, 83 deletions
diff --git a/dht/Presence/ConfigFiles.hs b/dht/Presence/ConfigFiles.hs
index d0164e33..e88989f2 100644
--- a/dht/Presence/ConfigFiles.hs
+++ b/dht/Presence/ConfigFiles.hs
@@ -59,7 +59,7 @@ createConfigFile :: ByteString -> FilePath -> IO ()
59createConfigFile tag path = do 59createConfigFile tag path = do
60 let dir = dropFileName path 60 let dir = dropFileName path
61 doesDirectoryExist dir >>= flip unless (do 61 doesDirectoryExist dir >>= flip unless (do
62 createDirectory dir 62 createDirectoryIfMissing True dir
63 ) 63 )
64 withFile path WriteMode $ \h -> do 64 withFile path WriteMode $ \h -> do
65 L.hPutStrLn h tag 65 L.hPutStrLn h tag
@@ -79,6 +79,16 @@ addItem item tag path =
79 in doit 79 in doit
80 80
81 81
82getProfiles :: User -> IO [Profile]
83getProfiles user = do
84 home <- homeDirectory <$> getUserEntryForName (unpack user)
85 let cfg = home </> configDir
86 fs <- listDirectory cfg
87 ds <- filterM (doesDirectoryExist . (cfg </>)) fs
88 return ds
89 `catchIOError` \e -> do
90 return []
91
82-- | Modify a presence configuration file. This function will iterate over all 92-- | Modify a presence configuration file. This function will iterate over all
83-- items in the file and invoke a test function. If the function returns 93-- items in the file and invoke a test function. If the function returns
84-- Nothing, that item is removed from the file. Otherwise, the function may 94-- Nothing, that item is removed from the file. Otherwise, the function may
diff --git a/dht/Presence/Presence.hs b/dht/Presence/Presence.hs
index 926ee3c2..b8c9f923 100644
--- a/dht/Presence/Presence.hs
+++ b/dht/Presence/Presence.hs
@@ -17,7 +17,7 @@ import Control.Concurrent.STM
17import Control.Monad.Trans 17import Control.Monad.Trans
18import Network.Socket ( SockAddr(..) ) 18import Network.Socket ( SockAddr(..) )
19import Data.Char 19import Data.Char
20import Data.List (nub, (\\), intersect, groupBy, sort, sortBy ) 20import Data.List (nub, (\\), intersect, groupBy, sort, sortBy, isSuffixOf )
21import Data.Ord (comparing ) 21import Data.Ord (comparing )
22import Data.Monoid ((<>)) 22import Data.Monoid ((<>))
23import qualified Data.Text as Text 23import qualified Data.Text as Text
@@ -33,7 +33,7 @@ import qualified ConfigFiles
33import Data.Maybe 33import Data.Maybe
34import Data.Bits 34import Data.Bits
35import Data.Int (Int8) 35import Data.Int (Int8)
36import Data.XML.Types (Event) 36import Data.XML.Types as XML (Event, Name)
37import System.Posix.Types (UserID,CPid) 37import System.Posix.Types (UserID,CPid)
38import Control.Applicative 38import Control.Applicative
39import Crypto.PubKey.Curve25519 (SecretKey,toPublic) 39import Crypto.PubKey.Curve25519 (SecretKey,toPublic)
@@ -51,7 +51,7 @@ import Util
51import qualified Connection 51import qualified Connection
52 ;import Connection (PeerAddress (..), resolvePeer, reverseAddress) 52 ;import Connection (PeerAddress (..), resolvePeer, reverseAddress)
53import Network.Tox.NodeId (key2id,parseNoSpamId,nospam64,NoSpamId(..),ToxProgress,ToxContact(..)) 53import Network.Tox.NodeId (key2id,parseNoSpamId,nospam64,NoSpamId(..),ToxProgress,ToxContact(..))
54import Crypto.Tox (decodeSecret) 54import Crypto.Tox (decodeSecret,encodeSecret, generateSecretKey)
55import DPut 55import DPut
56import DebugTag 56import DebugTag
57import Codec.AsciiKey256 57import Codec.AsciiKey256
@@ -242,67 +242,97 @@ identifyTTY' ttypids uid inode = ttypid
242 ttypid = fmap textify $ identifyTTY ttypids' uid inode 242 ttypid = fmap textify $ identifyTTY ttypids' uid inode
243 textify (tty,pid) = (fmap lazyByteStringToText tty, pid) 243 textify (tty,pid) = (fmap lazyByteStringToText tty, pid)
244 244
245chooseResourceName :: PresenceState stat 245
246 -> ClientAddress -> Remote SockAddr -> Maybe Text -> Maybe Text -> IO Text 246generateToxProfile :: Text -> IO ConfigFiles.Profile
247chooseResourceName state k (Remote addr) clientsNameForMe desired = do 247generateToxProfile user0 = do
248 muid <- getLocalPeerCred' addr 248 secret <- generateSecretKey
249 (mtty,pid) <- getTTYandPID muid 249 let pubkey = show $ key2id $ toPublic secret
250 user <- getJabberUserForId muid 250 Just s = L.fromStrict <$> encodeSecret secret
251 status <- atomically $ newTVar Nothing 251 profile = pubkey ++ ".tox"
252 flgs <- atomically $ newTVar 0 252 user = L.fromChunks [Text.encodeUtf8 user0]
253 profile <- fmap (fromMaybe ".") 253 ConfigFiles.configPath user profile ConfigFiles.secretsFile >>= ConfigFiles.addItem s "<? secret ?>"
254 $ forM ((,) <$> clientsNameForMe <*> toxManager state) $ \(wanted_profile0,toxman) -> 254 dput XMisc $ "Generated new Tox key " ++ profile
255 case stripSuffix ".tox" wanted_profile0 of 255 return profile
256 Just "*" -> do 256
257 dput XMisc $ "TODO: Match single tox key profile or generate first." 257
258 -- TODO: Match single tox key profile or generate first. 258autoSelectToxProfile :: Text -> IO (Maybe ConfigFiles.Profile)
259 _todo 259autoSelectToxProfile user = do
260 ps <- filter (isSuffixOf ".tox") <$> ConfigFiles.getProfiles (L.fromChunks [Text.encodeUtf8 user])
261 case ps of
262 [profile] -> return $ Just profile
263 [] -> Just <$> generateToxProfile user
264 _ -> return Nothing
265
266chooseProfile :: Text -> Bool -> ClientAddress -> Maybe (Text, ToxManager ClientAddress) -> IO (Either Text ConfigFiles.Profile)
267chooseProfile user allowNonTox k wanted_profile0 = do
268 let doAuto = do
269 p <- autoSelectToxProfile user
270 case p of Nothing -> return $ Left "Tox user-id is ambiguous."
271 Just pr -> chooseProfile user allowNonTox k
272 (Just (Text.pack pr, snd $ fromJust wanted_profile0))
273 case stripSuffix ".tox" =<< fmap fst wanted_profile0 of
274 Just "auto" -> doAuto
260 Just pub -> do 275 Just pub -> do
261 cdir <- ConfigFiles.configPath (L.fromChunks [Text.encodeUtf8 user]) "." "" 276 cdir <- ConfigFiles.configPath (L.fromChunks [Text.encodeUtf8 user]) "." ""
262#if !MIN_VERSION_directory(1,2,5)
263 let listDirectory path = filter (`notElem` [".",".."]) <$> getDirectoryContents path
264#endif
265 cfs <- map Text.pack <$> listDirectory cdir `catchIOError` (\e -> return []) 277 cfs <- map Text.pack <$> listDirectory cdir `catchIOError` (\e -> return [])
266 let profiles = filter (\f -> Text.toLower f == Text.toLower wanted_profile0) cfs 278 let Just (wanted_profile1,toxman) = wanted_profile0
279 profiles = filter (\f -> Text.toLower f == Text.toLower wanted_profile1) cfs
267 -- dput XMisc $ "Toxmpp profile " ++ show (user,wanted_profile0,profiles,cfs) 280 -- dput XMisc $ "Toxmpp profile " ++ show (user,wanted_profile0,profiles,cfs)
268 let wanted_profile = head $ profiles ++ [wanted_profile0] 281 let wanted_profile = head $ profiles ++ [wanted_profile1]
269 secs <- configText ConfigFiles.getSecrets user wanted_profile 282 secs <- configText ConfigFiles.getSecrets user wanted_profile
270 case secs of 283 case secs of
271 sec:_ | Just s <- decodeSecret (Text.encodeUtf8 sec) 284 sec:_ | Just s <- decodeSecret (Text.encodeUtf8 sec)
272 , map toLower (show $ key2id $ toPublic s) == map toLower (Text.unpack pub) 285 , map toLower (show $ key2id $ toPublic s) == map toLower (Text.unpack pub)
273 -> do activateAccount toxman k wanted_profile s 286 -> do activateAccount toxman k wanted_profile s
274 dput XMisc $ "loaded tox secret " ++ show sec 287 dput XMisc $ "loaded tox secret " ++ show sec
275 return wanted_profile 288 return $ Right $ Text.unpack wanted_profile
276 _ -> do 289 _ -> do
277 -- XXX: We should probably fail to connect when an 290 -- XXX: We should probably fail to connect when an
278 -- invalid Tox profile is used. For now, we'll 291 -- invalid Tox profile is used. For now, we'll
279 -- fall back to the Unix account login. 292 -- fall back to the Unix account login.
280 dput XMisc "failed to find tox secret" 293 dput XMisc "failed to find tox secret"
281 return "." 294 return $ Left $ "Missing secret key for " <> pub
282 _ -> return "." 295 Nothing | allowNonTox -> return $ Right "."
283 let client = ClientState { clientResource = maybe "fallback" id mtty 296 | otherwise -> doAuto
284 , clientUser = user
285 , clientProfile = profile
286 , clientPid = pid
287 , clientStatus = status
288 , clientFlags = flgs }
289
290 do -- forward-lookup of the buddies so that it is cached for reversing.
291 buds <- configText ConfigFiles.getBuddies (clientUser client) (clientProfile client)
292 forM_ buds $ \bud -> do
293 let (_,h,_) = splitJID bud
294 forkLabeled "XMPP.buddies.resolvePeer" $ do
295 void $ resolvePeer (manager state $ clientProfile client) h
296 297
297 atomically $ do 298chooseResourceName :: PresenceState stat
298 modifyTVar' (clients state) $ Map.insert k client 299 -> ClientAddress -> Remote SockAddr -> Maybe Text -> Maybe Text -> IO (Either Text Text)
299 let add mb = Just $ maybe (pcSingletonNetworkClient k client) 300chooseResourceName state k (Remote addr) clientsNameForMe desired = do
300 (pcInsertNetworkClient k client) 301 muid <- getLocalPeerCred' addr
301 mb 302 (mtty,pid) <- getTTYandPID muid
302 modifyTVar' (clientsByUser state) $ Map.alter add (clientUser client) 303 user <- getJabberUserForId muid
303 modifyTVar' (clientsByProfile state) $ Map.alter add (clientProfile client) 304 status <- atomically $ newTVar Nothing
305 flgs <- atomically $ newTVar 0
306 let mprofspec = (,) <$> clientsNameForMe <*> toxManager state
307 eprofile <- chooseProfile user False k mprofspec
308 case eprofile of
309
310 Right profile -> do
311 let client = ClientState { clientResource = maybe "fallback" id mtty
312 , clientUser = user
313 , clientProfile = Text.pack profile
314 , clientPid = pid
315 , clientStatus = status
316 , clientFlags = flgs }
317
318 do -- forward-lookup of the buddies so that it is cached for reversing.
319 buds <- configText ConfigFiles.getBuddies (clientUser client) (clientProfile client)
320 forM_ buds $ \bud -> do
321 let (_,h,_) = splitJID bud
322 forkLabeled "XMPP.buddies.resolvePeer" $ do
323 void $ resolvePeer (manager state $ clientProfile client) h
324
325 atomically $ do
326 modifyTVar' (clients state) $ Map.insert k client
327 let add mb = Just $ maybe (pcSingletonNetworkClient k client)
328 (pcInsertNetworkClient k client)
329 mb
330 modifyTVar' (clientsByUser state) $ Map.alter add (clientUser client)
331 modifyTVar' (clientsByProfile state) $ Map.alter add (clientProfile client)
332
333 Right <$> localJID (clientUser client) (clientProfile client) (clientResource client)
304 334
305 localJID (clientUser client) (clientProfile client) (clientResource client) 335 Left e -> return $ Left e
306 336
307 where 337 where
308 getTTYandPID muid = do 338 getTTYandPID muid = do
@@ -1294,6 +1324,7 @@ peerSubscriptionRequest state fail k stanza chan = do
1294 , stanzaTo = Just $ unsplitJID totup } 1324 , stanzaTo = Just $ unsplitJID totup }
1295 chan 1325 chan
1296 1326
1327myMakeRosterUpdate :: Text -> Text -> Text -> [(XML.Name, Text)] -> IO Stanza
1297myMakeRosterUpdate prf tojid contact as 1328myMakeRosterUpdate prf tojid contact as
1298 | ".tox" `Text.isSuffixOf` prf 1329 | ".tox" `Text.isSuffixOf` prf
1299 , (Just u,h,r) <- splitJID contact 1330 , (Just u,h,r) <- splitJID contact
diff --git a/dht/Presence/XMPPServer.hs b/dht/Presence/XMPPServer.hs
index 2f2a1b4b..65d882bd 100644
--- a/dht/Presence/XMPPServer.hs
+++ b/dht/Presence/XMPPServer.hs
@@ -124,9 +124,11 @@ data XMPPServerParameters =
124 -- the name the client referred to this server by. The second Maybe is the 124 -- the name the client referred to this server by. The second Maybe is the
125 -- client's preferred resource name. 125 -- client's preferred resource name.
126 -- 126 --
127 -- Note: The returned domain will be discarded and replaced with the result of 127 -- The returned domain will be discarded and replaced with the result of
128 -- 'xmppTellMyNameToClient'. 128 -- 'xmppTellMyNameToClient'.
129 xmppChooseResourceName :: ClientAddress -> Remote SockAddr -> Maybe Text -> Maybe Text -> IO Text 129 --
130 -- A Left result causes an error stanza to be sent instead.
131 xmppChooseResourceName :: ClientAddress -> Remote SockAddr -> Maybe Text -> Maybe Text -> IO (Either Text Text)
130 , -- | This should indicate the server's hostname that all client's see. 132 , -- | This should indicate the server's hostname that all client's see.
131 xmppTellMyNameToClient :: ClientAddress -> IO Text 133 xmppTellMyNameToClient :: ClientAddress -> IO Text
132 , xmppTellMyNameToPeer :: Local SockAddr -> IO Text 134 , xmppTellMyNameToPeer :: Local SockAddr -> IO Text
@@ -1436,6 +1438,17 @@ lookupService me mucs to = case Text.toLower to of
1436 Nothing -> UnknownService service 1438 Nothing -> UnknownService service
1437 _ -> NotMe 1439 _ -> NotMe
1438 1440
1441requestVersion :: Text -> Text -> ConduitT i XML.Event IO ()
1442requestVersion rsc hostname = do
1443 yield $ EventBeginElement "{jabber:client}iq"
1444 [ attr "to" rsc
1445 , attr "from" hostname
1446 , attr "type" "get"
1447 , attr "id" "version"]
1448 yield $ EventBeginElement "{jabber:iq:version}query" []
1449 yield $ EventEndElement "{jabber:iq:version}query"
1450 yield $ EventEndElement "{jabber:client}iq"
1451
1439applyStanza :: Server PeerAddress ConnectionData releaseKey Event 1452applyStanza :: Server PeerAddress ConnectionData releaseKey Event
1440 -> TVar (Map.Map ClientAddress (Map.Map (Text,Text) (TChan Stanza,JoinedRoom ClientAddress))) 1453 -> TVar (Map.Map ClientAddress (Map.Map (Text,Text) (TChan Stanza,JoinedRoom ClientAddress)))
1441 -> TMVar () 1454 -> TMVar ()
@@ -1450,38 +1463,22 @@ applyStanza sv joined_rooms quitVar xmpp stanza = do
1450 case stanzaType stanza of 1463 case stanzaType stanza of
1451 RequestResource clientsNameForMe wanted -> do 1464 RequestResource clientsNameForMe wanted -> do
1452 sockaddr <- socketFromKey sv k 1465 sockaddr <- socketFromKey sv k
1453 rsc0 <- xmppChooseResourceName xmpp k sockaddr clientsNameForMe wanted 1466 xmppChooseResourceName xmpp k sockaddr clientsNameForMe wanted >>= \case
1454 hostname <- xmppTellMyNameToClient xmpp k 1467 Right rsc0 -> do
1455 let rsc = unsplitJID (n,hostname,r) where (n,_,r) = splitJID rsc0 1468 hostname <- xmppTellMyNameToClient xmpp k
1456 let reply = iq_bind_reply (stanzaId stanza) rsc 1469 let rsc = unsplitJID (n,hostname,r) where (n,_,r) = splitJID rsc0
1457 -- sendReply quitVar SetResource reply replyto 1470 let reply = iq_bind_reply (stanzaId stanza) rsc
1458 let requestVersion :: ConduitT i XML.Event IO () 1471 sendReply quitVar SetResource reply replyto
1459 requestVersion = do 1472 conduitToStanza (UnrecognizedQuery "{jabber:iq:version}query")
1460 yield $ EventBeginElement "{jabber:client}iq" 1473 Nothing -- id
1461 [ attr "to" rsc 1474 (Just hostname) -- from
1462 , attr "from" hostname 1475 (Just rsc) -- to
1463 , attr "type" "get" 1476 (requestVersion rsc hostname)
1464 , attr "id" "version"] 1477 >>= ioWriteChan replyto
1465 yield $ EventBeginElement "{jabber:iq:version}query" [] 1478 Left err -> do
1466 yield $ EventEndElement "{jabber:iq:version}query" 1479 hostname <- xmppTellMyNameToClient xmpp k
1467 yield $ EventEndElement "{jabber:client}iq" 1480 reply <- makeErrorStanza' stanza NotAllowed []
1468 {- 1481 sendReply quitVar (Error NotAuthorized (head reply)) reply replyto
1469 -- XXX Debug chat:
1470 yield $ EventBeginElement "{jabber:client}message"
1471 [ attr "from" $ eventContent (Just [ContentText rsc])
1472 , attr "type" "normal" ] -- "blackbird" ]
1473 yield $ EventBeginElement "{jabber:client}body" []
1474 yield $ EventContent $ ContentText ("hello?")
1475 yield $ EventEndElement "{jabber:client}body"
1476 yield $ EventEndElement "{jabber:client}message"
1477 -}
1478 sendReply quitVar SetResource reply replyto
1479 conduitToStanza (UnrecognizedQuery "{jabber:iq:version}query")
1480 Nothing -- id
1481 (Just hostname) -- from
1482 (Just rsc) -- to
1483 requestVersion
1484 >>= ioWriteChan replyto
1485 SessionRequest -> do 1482 SessionRequest -> do
1486 me <- xmppTellMyNameToClient xmpp k 1483 me <- xmppTellMyNameToClient xmpp k
1487 let reply = iq_session_reply (stanzaId stanza) me 1484 let reply = iq_session_reply (stanzaId stanza) me
diff --git a/dht/dht-client.cabal b/dht/dht-client.cabal
index 5b75d8a8..64d48f53 100644
--- a/dht/dht-client.cabal
+++ b/dht/dht-client.cabal
@@ -66,7 +66,7 @@ custom-setup
66 setup-depends: 66 setup-depends:
67 base >= 4.5, 67 base >= 4.5,
68 Cabal >= 1.14, 68 Cabal >= 1.14,
69 directory 69 directory >= 1.2.5
70 70
71library 71library
72 default-language: Haskell2010 72 default-language: Haskell2010