diff options
Diffstat (limited to 'src/Data/Tox')
-rw-r--r-- | src/Data/Tox/Message.hs | 3 | ||||
-rw-r--r-- | src/Data/Tox/Msg.hs | 231 |
2 files changed, 233 insertions, 1 deletions
diff --git a/src/Data/Tox/Message.hs b/src/Data/Tox/Message.hs index b77307a2..9f1ce339 100644 --- a/src/Data/Tox/Message.hs +++ b/src/Data/Tox/Message.hs | |||
@@ -31,7 +31,8 @@ pattern MSI = MessageID 69 | |||
31 | pattern FILE_SENDREQUEST = MessageID 80 -- 1+1+4+8+32+max255 = up to 301 | 31 | pattern FILE_SENDREQUEST = MessageID 80 -- 1+1+4+8+32+max255 = up to 301 |
32 | pattern FILE_CONTROL = MessageID 81 -- 8 bytes if seek, otherwise 4 | 32 | pattern FILE_CONTROL = MessageID 81 -- 8 bytes if seek, otherwise 4 |
33 | pattern FILE_DATA = MessageID 82 -- up to 1373 | 33 | pattern FILE_DATA = MessageID 82 -- up to 1373 |
34 | pattern INVITE_GROUPCHAT = MessageID 96 -- 0x60 | 34 | pattern INVITE_GROUPCHAT = MessageID 95 |
35 | pattern INVITE_GROUPCHAT0 = MessageID 96 -- 0x60 | ||
35 | -- TODO: rename to INVITE_CONFERENCE 96 | 36 | -- TODO: rename to INVITE_CONFERENCE 96 |
36 | pattern ONLINE_PACKET = MessageID 97 -- 0x61 | 37 | pattern ONLINE_PACKET = MessageID 97 -- 0x61 |
37 | pattern DIRECT_GROUPCHAT = MessageID 98 -- 0x62 | 38 | pattern DIRECT_GROUPCHAT = MessageID 98 -- 0x62 |
diff --git a/src/Data/Tox/Msg.hs b/src/Data/Tox/Msg.hs new file mode 100644 index 00000000..84fffb12 --- /dev/null +++ b/src/Data/Tox/Msg.hs | |||
@@ -0,0 +1,231 @@ | |||
1 | {-# LANGUAGE DataKinds #-} | ||
2 | {-# LANGUAGE DefaultSignatures #-} | ||
3 | {-# LANGUAGE FlexibleInstances #-} | ||
4 | {-# LANGUAGE GADTs #-} | ||
5 | {-# LANGUAGE KindSignatures #-} | ||
6 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
7 | {-# LANGUAGE PolyKinds #-} | ||
8 | {-# LANGUAGE TypeFamilies #-} | ||
9 | {-# LANGUAGE StandaloneDeriving #-} | ||
10 | module Data.Tox.Msg where | ||
11 | |||
12 | import Data.ByteString as B | ||
13 | import Data.Dependent.Sum | ||
14 | import Data.GADT.Compare | ||
15 | import Data.GADT.Show | ||
16 | import Data.Functor.Identity | ||
17 | import Data.Serialize | ||
18 | import Data.Text as T | ||
19 | import Data.Text.Encoding as T | ||
20 | import Data.Typeable | ||
21 | import Data.Word | ||
22 | import GHC.TypeLits | ||
23 | |||
24 | import Crypto.Tox | ||
25 | import Data.PacketBuffer (compressSequenceNumbers, decompressSequenceNumbers) | ||
26 | |||
27 | newtype Unknown = Unknown B.ByteString deriving (Eq,Show) | ||
28 | newtype Padded = Padded B.ByteString deriving (Eq,Show) | ||
29 | |||
30 | -- The 'UserStatus' equivalent in Presence is: | ||
31 | -- | ||
32 | -- data JabberShow = Offline | ||
33 | -- | ExtendedAway | ||
34 | -- | Away -- Tox equiv: Away (1) | ||
35 | -- | DoNotDisturb -- Tox equiv: Busy (2) | ||
36 | -- | Available -- Tox equiv: Online (0) | ||
37 | -- | Chatty | ||
38 | -- deriving (Show,Enum,Ord,Eq,Read) | ||
39 | -- | ||
40 | -- The Enum instance on 'UserStatus' is not arbitrary. It corresponds | ||
41 | -- to on-the-wire id numbers. | ||
42 | data UserStatus = Online | Away | Busy deriving (Show,Read,Eq,Ord,Enum) | ||
43 | |||
44 | instance Serialize UserStatus where | ||
45 | get = do | ||
46 | x <- get :: Get Word8 | ||
47 | return (toEnum8 x) | ||
48 | put x = put (fromEnum8 x) | ||
49 | |||
50 | |||
51 | newtype MissingPackets = MissingPackets [Word32] | ||
52 | deriving (Eq,Show) | ||
53 | |||
54 | data Msg (n :: Nat) t where | ||
55 | Padding :: Msg 0 Padded | ||
56 | PacketRequest :: Msg 1 MissingPackets | ||
57 | KillPacket :: Msg 2 () | ||
58 | ALIVE :: Msg 16 () | ||
59 | SHARE_RELAYS :: Msg 17 Unknown | ||
60 | FRIEND_REQUESTS :: Msg 18 Unknown | ||
61 | ONLINE :: Msg 24 () | ||
62 | OFFLINE :: Msg 25 () | ||
63 | NICKNAME :: Msg 48 Text | ||
64 | STATUSMESSAGE :: Msg 49 Text | ||
65 | USERSTATUS :: Msg 50 UserStatus | ||
66 | TYPING :: Msg 51 Bool | ||
67 | MESSAGE :: Msg 64 Text | ||
68 | ACTION :: Msg 65 Text | ||
69 | MSI :: Msg 69 Unknown | ||
70 | FILE_SENDREQUEST :: Msg 80 Unknown | ||
71 | FILE_CONTROL :: Msg 81 Unknown | ||
72 | FILE_DATA :: Msg 82 Unknown | ||
73 | INVITE_GROUPCHAT :: Msg 95 Unknown | ||
74 | INVITE_CONFERENCE :: Msg 96 Unknown | ||
75 | ONLINE_PACKET :: Msg 97 Unknown | ||
76 | DIRECT_CONFERENCE :: Msg 98 Unknown | ||
77 | MESSAGE_CONFERENCE :: Msg 99 Unknown | ||
78 | LOSSY_CONFERENCE :: Msg 199 Unknown | ||
79 | |||
80 | deriving instance Show (Msg n a) | ||
81 | |||
82 | msgbyte :: KnownNat n => Msg n a -> Word8 | ||
83 | msgbyte m = fromIntegral (natVal $ proxy m) | ||
84 | where proxy :: Msg n a -> Proxy n | ||
85 | proxy _ = Proxy | ||
86 | |||
87 | data Pkt a where Pkt :: (KnownNat n, Packet a, KnownMsg n) => Msg n a -> Pkt a | ||
88 | |||
89 | deriving instance (Show (Pkt a)) | ||
90 | |||
91 | type CryptoMessage = DSum Pkt Identity | ||
92 | |||
93 | msgID (Pkt mid :=> Identity _) = M mid | ||
94 | |||
95 | -- TODO | ||
96 | instance GShow Pkt where gshowsPrec = showsPrec | ||
97 | instance ShowTag Pkt Identity where | ||
98 | showTaggedPrec (Pkt _) = showsPrec | ||
99 | |||
100 | instance GEq Pkt where geq (Pkt _) (Pkt _) = eqT | ||
101 | instance EqTag Pkt Identity where eqTagged (Pkt _) (Pkt _) = (==) | ||
102 | |||
103 | someMsgVal :: KnownMsg n => Msg n a -> SomeMsg | ||
104 | someMsgVal m = msgid (proxy m) | ||
105 | where proxy :: Msg n a -> Proxy n | ||
106 | proxy _ = Proxy | ||
107 | |||
108 | class KnownMsg (n::Nat) where msgid :: proxy n -> SomeMsg | ||
109 | |||
110 | instance KnownMsg 0 where msgid _ = M Padding | ||
111 | instance KnownMsg 1 where msgid _ = M PacketRequest | ||
112 | instance KnownMsg 2 where msgid _ = M KillPacket | ||
113 | instance KnownMsg 16 where msgid _ = M ALIVE | ||
114 | instance KnownMsg 17 where msgid _ = M SHARE_RELAYS | ||
115 | instance KnownMsg 18 where msgid _ = M FRIEND_REQUESTS | ||
116 | instance KnownMsg 24 where msgid _ = M ONLINE | ||
117 | instance KnownMsg 25 where msgid _ = M OFFLINE | ||
118 | instance KnownMsg 48 where msgid _ = M NICKNAME | ||
119 | instance KnownMsg 49 where msgid _ = M STATUSMESSAGE | ||
120 | instance KnownMsg 50 where msgid _ = M USERSTATUS | ||
121 | instance KnownMsg 51 where msgid _ = M TYPING | ||
122 | instance KnownMsg 64 where msgid _ = M MESSAGE | ||
123 | instance KnownMsg 65 where msgid _ = M ACTION | ||
124 | instance KnownMsg 69 where msgid _ = M MSI | ||
125 | instance KnownMsg 80 where msgid _ = M FILE_SENDREQUEST | ||
126 | instance KnownMsg 81 where msgid _ = M FILE_CONTROL | ||
127 | instance KnownMsg 82 where msgid _ = M FILE_DATA | ||
128 | instance KnownMsg 95 where msgid _ = M INVITE_GROUPCHAT | ||
129 | instance KnownMsg 96 where msgid _ = M INVITE_CONFERENCE | ||
130 | instance KnownMsg 97 where msgid _ = M ONLINE_PACKET | ||
131 | instance KnownMsg 98 where msgid _ = M DIRECT_CONFERENCE | ||
132 | instance KnownMsg 99 where msgid _ = M MESSAGE_CONFERENCE | ||
133 | |||
134 | msgTag :: Word8 -> Maybe SomeMsg | ||
135 | msgTag 0 = Just $ M Padding | ||
136 | msgTag 1 = Just $ M PacketRequest | ||
137 | msgTag 2 = Just $ M KillPacket | ||
138 | msgTag 16 = Just $ M ALIVE | ||
139 | msgTag 17 = Just $ M SHARE_RELAYS | ||
140 | msgTag 18 = Just $ M FRIEND_REQUESTS | ||
141 | msgTag 24 = Just $ M ONLINE | ||
142 | msgTag 25 = Just $ M OFFLINE | ||
143 | msgTag 48 = Just $ M NICKNAME | ||
144 | msgTag 49 = Just $ M STATUSMESSAGE | ||
145 | msgTag 50 = Just $ M USERSTATUS | ||
146 | msgTag 51 = Just $ M TYPING | ||
147 | msgTag 64 = Just $ M MESSAGE | ||
148 | msgTag 65 = Just $ M ACTION | ||
149 | msgTag 69 = Just $ M MSI | ||
150 | msgTag 80 = Just $ M FILE_SENDREQUEST | ||
151 | msgTag 81 = Just $ M FILE_CONTROL | ||
152 | msgTag 82 = Just $ M FILE_DATA | ||
153 | msgTag 95 = Just $ M INVITE_GROUPCHAT | ||
154 | msgTag 96 = Just $ M INVITE_CONFERENCE | ||
155 | msgTag 97 = Just $ M ONLINE_PACKET | ||
156 | msgTag 98 = Just $ M DIRECT_CONFERENCE | ||
157 | msgTag 99 = Just $ M MESSAGE_CONFERENCE | ||
158 | msgTag _ = Nothing | ||
159 | |||
160 | |||
161 | class (Typeable t, Eq t, Show t, Sized t) => Packet t where | ||
162 | getPacket :: Word32 -> Get t | ||
163 | putPacket :: Word32 -> t -> Put | ||
164 | default getPacket :: Serialize t => Word32 -> Get t | ||
165 | getPacket _ = get | ||
166 | default putPacket :: Serialize t => Word32 -> t -> Put | ||
167 | putPacket _ t = put t | ||
168 | |||
169 | instance Sized UserStatus where size = ConstSize 1 | ||
170 | instance Packet UserStatus | ||
171 | |||
172 | instance Sized () where size = ConstSize 0 | ||
173 | instance Packet () where | ||
174 | getPacket _ = return () | ||
175 | putPacket _ _ = return () | ||
176 | |||
177 | instance Sized MissingPackets where size = VarSize $ \(MissingPackets ws) -> Prelude.length ws | ||
178 | instance Packet MissingPackets where | ||
179 | getPacket seqno = do | ||
180 | bs <- B.unpack <$> (remaining >>= getBytes) | ||
181 | return $ MissingPackets (decompressSequenceNumbers seqno bs) | ||
182 | putPacket seqno (MissingPackets ws) = do | ||
183 | mapM_ putWord8 $ compressSequenceNumbers seqno ws | ||
184 | |||
185 | instance Sized Unknown where size = VarSize $ \(Unknown bs) -> B.length bs | ||
186 | instance Packet Unknown where | ||
187 | getPacket _ = Unknown <$> (remaining >>= getBytes) | ||
188 | putPacket _ (Unknown bs) = putByteString bs | ||
189 | |||
190 | instance Sized Padded where size = VarSize $ \(Padded bs) -> B.length bs | ||
191 | instance Packet Padded where | ||
192 | getPacket _ = Padded <$> (remaining >>= getBytes) | ||
193 | putPacket _ (Padded bs) = putByteString bs | ||
194 | |||
195 | instance Sized Text where size = VarSize (B.length . T.encodeUtf8) | ||
196 | instance Packet Text where | ||
197 | getPacket _ = T.decodeUtf8 <$> (remaining >>= getBytes) | ||
198 | putPacket _ = putByteString . T.encodeUtf8 | ||
199 | |||
200 | instance Sized Bool where size = ConstSize 1 | ||
201 | instance Packet Bool where | ||
202 | getPacket _ = (/= 0) <$> getWord8 | ||
203 | putPacket _ False = putWord8 0 | ||
204 | putPacket _ True = putWord8 1 | ||
205 | |||
206 | data SomeMsg where | ||
207 | M :: (KnownMsg n, KnownNat n, Packet t) => Msg n t -> SomeMsg | ||
208 | |||
209 | instance Eq SomeMsg where | ||
210 | M m == M n = msgbyte m == msgbyte n | ||
211 | |||
212 | instance Show SomeMsg where | ||
213 | show (M m) = show m | ||
214 | |||
215 | toEnum8 :: (Enum a, Integral word8) => word8 -> a | ||
216 | toEnum8 = toEnum . fromIntegral | ||
217 | |||
218 | fromEnum8 :: Enum a => a -> Word8 | ||
219 | fromEnum8 = fromIntegral . fromEnum | ||
220 | |||
221 | data LossyOrLossless = Lossless | Lossy deriving (Eq,Ord,Enum,Show,Bounded) | ||
222 | |||
223 | someLossyness (M m) = lossyness m | ||
224 | |||
225 | lossyness :: KnownNat n => Msg n t -> LossyOrLossless | ||
226 | lossyness m = case msgbyte m of | ||
227 | x | x < 3 -> Lossy | ||
228 | | {-16 <= x,-} x < 192 -> Lossless | ||
229 | | 192 <= x, x < 255 -> Lossy | ||
230 | | otherwise -> Lossless | ||
231 | |||