summaryrefslogtreecommitdiff
path: root/examples/atox.hs
blob: 5e6aace61606ba926b0757497b6f73932858186c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}

import Control.Monad.IO.Class
import Control.Concurrent
import Control.Concurrent.STM
import qualified Data.ByteString.Char8 as B
import Data.ByteString (ByteString)
import Data.Char
import qualified Data.Conduit as Conduit
import Data.Conduit ((.|))
import qualified Data.Conduit.Binary as Conduit
import Data.Conduit.Cereal
import Data.Function
import qualified Data.Map.Strict as Map
import Data.Monoid
import qualified Data.Serialize as S
import Data.Serialize (Get(..), Put(..))
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8,decodeUtf8)
import Network.Tox.Crypto.Transport
import Network.Tox.Crypto.Handlers
import Network.Tox.NodeId
import System.Console.ANSI
import qualified System.Console.Terminal.Size as Term
import System.Environment
import System.Exit
import System.FilePath
import System.IO
import System.IO.Unsafe (unsafePerformIO)
import qualified System.Posix.Env.ByteString as B
import System.Posix.IO.ByteString
import System.Posix.Types
import Text.Read

-- Some Global State --

{-# NOINLINE sMe #-}
sMe :: TVar NodeId
sMe = unsafePerformIO $ newTVarIO zero
    where Right zero = (S.decode $ B.replicate 32 '\NUL')

{-# NOINLINE sThem #-}
sThem :: TVar NodeId
sThem = unsafePerformIO $ newTVarIO zero
    where Right zero = (S.decode $ B.replicate 32 '\NUL')

{-# NOINLINE sMap #-}
sMap :: TVar (Map.Map Key ViewSnapshot)
sMap = unsafePerformIO $ newTVarIO (Map.empty)

-----------------------


puts :: MonadIO m => ByteString -> m ()
puts = liftIO . B.putStrLn

packUtf8 :: String -> ByteString
packUtf8 = encodeUtf8 . T.pack

pshow :: Show a => a -> ByteString
pshow = packUtf8 . show

usage = do
     hPutStrLn stderr "Usage: atox <INPUT-FILE-DESCRIPTOR> <OUTPUT-FILE-DESCRIPTOR>"
     exitFailure

processArgs usage doit [readNum,writeNum] | Just i <- readMaybe readNum
                                          , Just o <- readMaybe writeNum = doit i o
processArgs usage _ _ = usage

main = getArgs >>= processArgs usage doit

pattern IPC = Padding

-- | Interprocess command
data SetCmd = SetME
            | SetTHEM
            | SetView
    deriving (Eq,Bounded,Ord,Enum,Show)

forkToxInputThread myRead = forkIO $ do
    let myconduit = Conduit.sourceHandle myRead  .| conduitGet2 (S.get :: Get CryptoMessage) -- :: ConduitT i CryptoMessage IO ()
    Conduit.runConduit (myconduit .| Conduit.awaitForever handle)
 where
    handle (UpToN IPC (B.uncons -> Just (ord -> toEnum -> i,arg))) = updateState i arg
    handle msg = puts (pshow msg)

    updateState SetME arg = case S.decode arg of
                                Left str -> puts (packUtf8 str)
                                Right x  -> liftIO . atomically . writeTVar sMe $ x
    updateState SetTHEM arg = case S.decode arg of
                                Left str -> puts (packUtf8 str)
                                Right x  -> liftIO . atomically . writeTVar sThem $ x
    updateState SetView arg = case S.decode arg of
                                Left str -> puts (packUtf8 str)
                                Right view -> liftIO . atomically $ do
                                    me <- readTVar sMe
                                    them <- readTVar sThem
                                    let key = Key me them
                                    modifyTVar' sMap (Map.insert key view)

doit :: Fd -> Fd -> IO ()
doit myReadFd myWriteFd = do
    myRead <- fdToHandle myReadFd
    myWrite <- fdToHandle myWriteFd
    forkToxInputThread myRead
    terminalInputLoop myWrite

hSend :: MonadIO m => Handle -> CryptoMessage -> m ()
hSend h msg = liftIO $ B.hPutStrLn h (S.encode msg)

terminalInputLoop myWriteH = fix $ \loop -> do
    line <- B.getLine
    if "/" `B.isPrefixOf` line then let (B.drop 1 -> cmd, B.drop 1 -> arg) = B.break (==' ') line
                                        in slashCommand myWriteH (B.drop 1 line) arg
                               else hSend myWriteH (UpToN MESSAGE line)
    loop
    
    
slashCommand :: MonadIO m => Handle -> ByteString -> ByteString -> m ()
slashCommand h "quit" _ = do    
   hSend h (OneByte OFFLINE)
   hSend h (OneByte KillPacket)
   puts "Exiting..."
   liftIO $ exitSuccess

slashCommand h "nick" (B.words -> take 1 -> [nick]) = hSend h (UpToN NICKNAME nick)

slashCommand h "away" msg = do
    hSend h (TwoByte USERSTATUS (fromEnum8 Away))
    hSend h (UpToN STATUSMESSAGE msg)

slashCommand h "back" msg = do
    hSend h (TwoByte USERSTATUS (fromEnum8 Online))
    hSend h (UpToN STATUSMESSAGE msg)

slashCommand h cmd _ = do
    puts $ "UNKNOWN COMMAND: " <> cmd