diff options
Diffstat (limited to 'src/Data/Tox/Msg.hs')
-rw-r--r-- | src/Data/Tox/Msg.hs | 311 |
1 files changed, 0 insertions, 311 deletions
diff --git a/src/Data/Tox/Msg.hs b/src/Data/Tox/Msg.hs deleted file mode 100644 index 66ec6eb1..00000000 --- a/src/Data/Tox/Msg.hs +++ /dev/null | |||
@@ -1,311 +0,0 @@ | |||
1 | {-# LANGUAGE DataKinds #-} | ||
2 | {-# LANGUAGE DefaultSignatures #-} | ||
3 | {-# LANGUAGE FlexibleInstances #-} | ||
4 | {-# LANGUAGE GADTs #-} | ||
5 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} | ||
6 | {-# LANGUAGE KindSignatures #-} | ||
7 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
8 | {-# LANGUAGE PolyKinds #-} | ||
9 | {-# LANGUAGE StandaloneDeriving #-} | ||
10 | {-# LANGUAGE TypeFamilies #-} | ||
11 | module Data.Tox.Msg where | ||
12 | |||
13 | import Crypto.Error | ||
14 | import qualified Crypto.PubKey.Ed25519 as Ed25519 | ||
15 | import Data.ByteArray as BA | ||
16 | import Data.ByteString as B | ||
17 | import Data.Dependent.Sum | ||
18 | import Data.Functor.Contravariant | ||
19 | import Data.Functor.Identity | ||
20 | import Data.GADT.Compare | ||
21 | import Data.GADT.Show | ||
22 | import Data.Monoid | ||
23 | import Data.Serialize | ||
24 | import Data.Text as T | ||
25 | import Data.Text.Encoding as T | ||
26 | import Data.Typeable | ||
27 | import Data.Word | ||
28 | import GHC.TypeLits | ||
29 | |||
30 | import Crypto.Tox | ||
31 | import Data.PacketBuffer (compressSequenceNumbers, decompressSequenceNumbers) | ||
32 | import Network.Tox.NodeId | ||
33 | |||
34 | newtype Unknown = Unknown B.ByteString deriving (Eq,Show) | ||
35 | newtype Padded = Padded B.ByteString deriving (Eq,Show) | ||
36 | |||
37 | -- The 'UserStatus' equivalent in Presence is: | ||
38 | -- | ||
39 | -- data JabberShow = Offline | ||
40 | -- | ExtendedAway | ||
41 | -- | Away -- Tox equiv: Away (1) | ||
42 | -- | DoNotDisturb -- Tox equiv: Busy (2) | ||
43 | -- | Available -- Tox equiv: Online (0) | ||
44 | -- | Chatty | ||
45 | -- deriving (Show,Enum,Ord,Eq,Read) | ||
46 | -- | ||
47 | -- The Enum instance on 'UserStatus' is not arbitrary. It corresponds | ||
48 | -- to on-the-wire id numbers. | ||
49 | data UserStatus = Online | Away | Busy deriving (Show,Read,Eq,Ord,Enum) | ||
50 | |||
51 | instance Serialize UserStatus where | ||
52 | get = do | ||
53 | x <- get :: Get Word8 | ||
54 | return (toEnum8 x) | ||
55 | put x = put (fromEnum8 x) | ||
56 | |||
57 | |||
58 | newtype MissingPackets = MissingPackets [Word32] | ||
59 | deriving (Eq,Show) | ||
60 | |||
61 | data Msg (n :: Nat) t where | ||
62 | Padding :: Msg 0 Padded | ||
63 | PacketRequest :: Msg 1 MissingPackets | ||
64 | KillPacket :: Msg 2 () | ||
65 | ALIVE :: Msg 16 () | ||
66 | SHARE_RELAYS :: Msg 17 Unknown | ||
67 | FRIEND_REQUESTS :: Msg 18 Unknown | ||
68 | ONLINE :: Msg 24 () | ||
69 | OFFLINE :: Msg 25 () | ||
70 | NICKNAME :: Msg 48 Text | ||
71 | STATUSMESSAGE :: Msg 49 Text | ||
72 | USERSTATUS :: Msg 50 UserStatus | ||
73 | TYPING :: Msg 51 Bool | ||
74 | MESSAGE :: Msg 64 Text | ||
75 | ACTION :: Msg 65 Text | ||
76 | MSI :: Msg 69 Unknown | ||
77 | FILE_SENDREQUEST :: Msg 80 Unknown | ||
78 | FILE_CONTROL :: Msg 81 Unknown | ||
79 | FILE_DATA :: Msg 82 Unknown | ||
80 | INVITE_GROUPCHAT :: Msg 95 Invite | ||
81 | INVITE_CONFERENCE :: Msg 96 Unknown | ||
82 | ONLINE_PACKET :: Msg 97 Unknown | ||
83 | DIRECT_CONFERENCE :: Msg 98 Unknown | ||
84 | MESSAGE_CONFERENCE :: Msg 99 Unknown | ||
85 | LOSSY_CONFERENCE :: Msg 199 Unknown | ||
86 | |||
87 | deriving instance Show (Msg n a) | ||
88 | |||
89 | msgbyte :: KnownNat n => Msg n a -> Word8 | ||
90 | msgbyte m = fromIntegral (natVal $ proxy m) | ||
91 | where proxy :: Msg n a -> Proxy n | ||
92 | proxy _ = Proxy | ||
93 | |||
94 | data Pkt a where Pkt :: (KnownNat n, Packet a, KnownMsg n) => Msg n a -> Pkt a | ||
95 | |||
96 | deriving instance (Show (Pkt a)) | ||
97 | |||
98 | type CryptoMessage = DSum Pkt Identity | ||
99 | |||
100 | msgID (Pkt mid :=> Identity _) = M mid | ||
101 | |||
102 | -- TODO | ||
103 | instance GShow Pkt where gshowsPrec = showsPrec | ||
104 | instance ShowTag Pkt Identity where | ||
105 | showTaggedPrec (Pkt _) = showsPrec | ||
106 | |||
107 | instance GEq Pkt where geq (Pkt _) (Pkt _) = eqT | ||
108 | instance EqTag Pkt Identity where eqTagged (Pkt _) (Pkt _) = (==) | ||
109 | |||
110 | someMsgVal :: KnownMsg n => Msg n a -> SomeMsg | ||
111 | someMsgVal m = msgid (proxy m) | ||
112 | where proxy :: Msg n a -> Proxy n | ||
113 | proxy _ = Proxy | ||
114 | |||
115 | class KnownMsg (n::Nat) where msgid :: proxy n -> SomeMsg | ||
116 | |||
117 | instance KnownMsg 0 where msgid _ = M Padding | ||
118 | instance KnownMsg 1 where msgid _ = M PacketRequest | ||
119 | instance KnownMsg 2 where msgid _ = M KillPacket | ||
120 | instance KnownMsg 16 where msgid _ = M ALIVE | ||
121 | instance KnownMsg 17 where msgid _ = M SHARE_RELAYS | ||
122 | instance KnownMsg 18 where msgid _ = M FRIEND_REQUESTS | ||
123 | instance KnownMsg 24 where msgid _ = M ONLINE | ||
124 | instance KnownMsg 25 where msgid _ = M OFFLINE | ||
125 | instance KnownMsg 48 where msgid _ = M NICKNAME | ||
126 | instance KnownMsg 49 where msgid _ = M STATUSMESSAGE | ||
127 | instance KnownMsg 50 where msgid _ = M USERSTATUS | ||
128 | instance KnownMsg 51 where msgid _ = M TYPING | ||
129 | instance KnownMsg 64 where msgid _ = M MESSAGE | ||
130 | instance KnownMsg 65 where msgid _ = M ACTION | ||
131 | instance KnownMsg 69 where msgid _ = M MSI | ||
132 | instance KnownMsg 80 where msgid _ = M FILE_SENDREQUEST | ||
133 | instance KnownMsg 81 where msgid _ = M FILE_CONTROL | ||
134 | instance KnownMsg 82 where msgid _ = M FILE_DATA | ||
135 | instance KnownMsg 95 where msgid _ = M INVITE_GROUPCHAT | ||
136 | instance KnownMsg 96 where msgid _ = M INVITE_CONFERENCE | ||
137 | instance KnownMsg 97 where msgid _ = M ONLINE_PACKET | ||
138 | instance KnownMsg 98 where msgid _ = M DIRECT_CONFERENCE | ||
139 | instance KnownMsg 99 where msgid _ = M MESSAGE_CONFERENCE | ||
140 | |||
141 | msgTag :: Word8 -> Maybe SomeMsg | ||
142 | msgTag 0 = Just $ M Padding | ||
143 | msgTag 1 = Just $ M PacketRequest | ||
144 | msgTag 2 = Just $ M KillPacket | ||
145 | msgTag 16 = Just $ M ALIVE | ||
146 | msgTag 17 = Just $ M SHARE_RELAYS | ||
147 | msgTag 18 = Just $ M FRIEND_REQUESTS | ||
148 | msgTag 24 = Just $ M ONLINE | ||
149 | msgTag 25 = Just $ M OFFLINE | ||
150 | msgTag 48 = Just $ M NICKNAME | ||
151 | msgTag 49 = Just $ M STATUSMESSAGE | ||
152 | msgTag 50 = Just $ M USERSTATUS | ||
153 | msgTag 51 = Just $ M TYPING | ||
154 | msgTag 64 = Just $ M MESSAGE | ||
155 | msgTag 65 = Just $ M ACTION | ||
156 | msgTag 69 = Just $ M MSI | ||
157 | msgTag 80 = Just $ M FILE_SENDREQUEST | ||
158 | msgTag 81 = Just $ M FILE_CONTROL | ||
159 | msgTag 82 = Just $ M FILE_DATA | ||
160 | msgTag 95 = Just $ M INVITE_GROUPCHAT | ||
161 | msgTag 96 = Just $ M INVITE_CONFERENCE | ||
162 | msgTag 97 = Just $ M ONLINE_PACKET | ||
163 | msgTag 98 = Just $ M DIRECT_CONFERENCE | ||
164 | msgTag 99 = Just $ M MESSAGE_CONFERENCE | ||
165 | msgTag _ = Nothing | ||
166 | |||
167 | |||
168 | class (Typeable t, Eq t, Show t, Sized t) => Packet t where | ||
169 | getPacket :: Word32 -> Get t | ||
170 | putPacket :: Word32 -> t -> Put | ||
171 | default getPacket :: Serialize t => Word32 -> Get t | ||
172 | getPacket _ = get | ||
173 | default putPacket :: Serialize t => Word32 -> t -> Put | ||
174 | putPacket _ t = put t | ||
175 | |||
176 | instance Sized UserStatus where size = ConstSize 1 | ||
177 | instance Packet UserStatus | ||
178 | |||
179 | instance Sized () where size = ConstSize 0 | ||
180 | instance Packet () where | ||
181 | getPacket _ = return () | ||
182 | putPacket _ _ = return () | ||
183 | |||
184 | instance Sized MissingPackets where size = VarSize $ \(MissingPackets ws) -> Prelude.length ws | ||
185 | instance Packet MissingPackets where | ||
186 | getPacket seqno = do | ||
187 | bs <- B.unpack <$> (remaining >>= getBytes) | ||
188 | return $ MissingPackets (decompressSequenceNumbers seqno bs) | ||
189 | putPacket seqno (MissingPackets ws) = do | ||
190 | mapM_ putWord8 $ compressSequenceNumbers seqno ws | ||
191 | |||
192 | instance Sized Unknown where size = VarSize $ \(Unknown bs) -> B.length bs | ||
193 | instance Packet Unknown where | ||
194 | getPacket _ = Unknown <$> (remaining >>= getBytes) | ||
195 | putPacket _ (Unknown bs) = putByteString bs | ||
196 | |||
197 | instance Sized Padded where size = VarSize $ \(Padded bs) -> B.length bs | ||
198 | instance Packet Padded where | ||
199 | getPacket _ = Padded <$> (remaining >>= getBytes) | ||
200 | putPacket _ (Padded bs) = putByteString bs | ||
201 | |||
202 | instance Sized Text where size = VarSize (B.length . T.encodeUtf8) | ||
203 | instance Packet Text where | ||
204 | getPacket _ = T.decodeUtf8 <$> (remaining >>= getBytes) | ||
205 | putPacket _ = putByteString . T.encodeUtf8 | ||
206 | |||
207 | instance Sized Bool where size = ConstSize 1 | ||
208 | instance Packet Bool where | ||
209 | getPacket _ = (/= 0) <$> getWord8 | ||
210 | putPacket _ False = putWord8 0 | ||
211 | putPacket _ True = putWord8 1 | ||
212 | |||
213 | data SomeMsg where | ||
214 | M :: (KnownMsg n, KnownNat n, Packet t) => Msg n t -> SomeMsg | ||
215 | |||
216 | instance Eq SomeMsg where | ||
217 | M m == M n = msgbyte m == msgbyte n | ||
218 | |||
219 | instance Show SomeMsg where | ||
220 | show (M m) = show m | ||
221 | |||
222 | toEnum8 :: (Enum a, Integral word8) => word8 -> a | ||
223 | toEnum8 = toEnum . fromIntegral | ||
224 | |||
225 | fromEnum8 :: Enum a => a -> Word8 | ||
226 | fromEnum8 = fromIntegral . fromEnum | ||
227 | |||
228 | data LossyOrLossless = Lossless | Lossy deriving (Eq,Ord,Enum,Show,Bounded) | ||
229 | |||
230 | someLossyness (M m) = lossyness m | ||
231 | |||
232 | lossyness :: KnownNat n => Msg n t -> LossyOrLossless | ||
233 | lossyness m = case msgbyte m of | ||
234 | x | x < 3 -> Lossy | ||
235 | | {-16 <= x,-} x < 192 -> Lossless | ||
236 | | 192 <= x, x < 255 -> Lossy | ||
237 | | otherwise -> Lossless | ||
238 | |||
239 | |||
240 | newtype ChatID = ChatID Ed25519.PublicKey | ||
241 | deriving Eq | ||
242 | |||
243 | instance Sized ChatID where size = ConstSize 32 | ||
244 | |||
245 | instance Serialize ChatID where | ||
246 | get = do | ||
247 | bs <- getBytes 32 | ||
248 | case Ed25519.publicKey bs of | ||
249 | CryptoPassed ed -> return $ ChatID ed | ||
250 | CryptoFailed e -> fail (show e) | ||
251 | put (ChatID ed) = putByteString $ BA.convert ed | ||
252 | |||
253 | instance Read ChatID where | ||
254 | readsPrec _ s | ||
255 | | Right bs <- parseToken32 s | ||
256 | , CryptoPassed ed <- Ed25519.publicKey bs | ||
257 | = [ (ChatID ed, Prelude.drop 43 s) ] | ||
258 | | otherwise = [] | ||
259 | |||
260 | instance Show ChatID where | ||
261 | show (ChatID ed) = showToken32 ed | ||
262 | |||
263 | data InviteType = GroupInvite { groupName :: Text } | ||
264 | | AcceptedInvite | ||
265 | | ConfirmedInvite { inviteNodes :: [NodeInfo] } | ||
266 | deriving (Eq,Show) | ||
267 | |||
268 | instance Sized InviteType where | ||
269 | size = VarSize $ \x -> case x of | ||
270 | GroupInvite name -> B.length (T.encodeUtf8 name) | ||
271 | AcceptedInvite -> 0 | ||
272 | ConfirmedInvite ns -> 0 -- TODO: size of node list. | ||
273 | |||
274 | data Invite = Invite | ||
275 | { inviteChatID :: ChatID | ||
276 | , inviteChatKey :: PublicKey | ||
277 | , invite :: InviteType | ||
278 | } | ||
279 | deriving (Eq,Show) | ||
280 | |||
281 | instance Sized Invite where | ||
282 | size = contramap inviteChatID size | ||
283 | <> contramap (key2id . inviteChatKey) size | ||
284 | <> contramap invite size | ||
285 | |||
286 | instance Serialize Invite where | ||
287 | get = do | ||
288 | group_packet_id <- getWord8 -- expecting 254=GP_FRIEND_INVITE | ||
289 | invite_type <- getWord8 | ||
290 | chatid <- get | ||
291 | chatkey <- getPublicKey | ||
292 | Invite chatid chatkey <$> case invite_type of | ||
293 | 0 -> do bs <- remaining >>= getBytes -- TODO: size can be determined from group shared state. | ||
294 | return $ GroupInvite $ decodeUtf8 bs | ||
295 | 1 -> return AcceptedInvite | ||
296 | 2 -> return $ ConfirmedInvite [] -- TODO: decode nodes | ||
297 | |||
298 | put x = do | ||
299 | putWord8 254 -- GP_FRIEND_INVITE | ||
300 | putWord8 $ case invite x of | ||
301 | GroupInvite {} -> 0 -- GROUP_INVITE | ||
302 | AcceptedInvite -> 1 -- GROUP_INVITE_ACCEPTED | ||
303 | ConfirmedInvite {} -> 2 -- GROUP_INVITE_CONFIRMATION | ||
304 | put $ inviteChatID x | ||
305 | putPublicKey $ inviteChatKey x | ||
306 | case invite x of | ||
307 | GroupInvite name -> putByteString $ encodeUtf8 name | ||
308 | AcceptedInvite -> return () | ||
309 | ConfirmedInvite ns -> return () -- TODO: encode nodes. | ||
310 | |||
311 | instance Packet Invite where | ||