• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tox-rs / tox / 7247588774

18 Dec 2023 11:45AM UTC coverage: 94.85% (-0.03%) from 94.876%
7247588774

Pull #477

github

web-flow
Merge 6a5a935cc into 5ab95a61f
Pull Request #477: Add functions for tox crate version information

0 of 10 new or added lines in 1 file covered. (0.0%)

13 existing lines in 7 files now uncovered.

16816 of 17729 relevant lines covered (94.85%)

1.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.11
/tox_core/src/state_format/old.rs
1
//! Old **Tox State Format (TSF)**. *__Will be deprecated__ when something
2
//! better will become available.*
3

4
use nom::{
5
    branch::alt,
6
    bytes::complete::{tag, take},
7
    combinator::{map, map_parser, rest, success, verify},
8
    error::{make_error, ErrorKind},
9
    multi::{length_data, many0},
10
    number::complete::{be_u16, le_u16, le_u32, le_u64, le_u8},
11
};
12
use rand::{CryptoRng, Rng};
13
use std::default::Default;
14

15
use tox_binary_io::*;
16
use tox_crypto::*;
17
use tox_packet::dht::packed_node::*;
18
use tox_packet::packed_node::*;
19
use tox_packet::toxid::{NoSpam, NOSPAMBYTES};
20

21
const REQUEST_MSG_LEN: usize = 1024;
22

23
/// According to https://zetok.github.io/tox-spec/#sections
24
const SECTION_MAGIC: &[u8; 2] = &[0xce, 0x01];
25

26
/** NoSpam and Keys section of the new state format.
27

28
https://zetok.github.io/tox-spec/#nospam-and-keys-0x01
29
*/
30
#[derive(Clone)]
31
pub struct NospamKeys {
32
    /// Own `NoSpam`.
33
    pub nospam: NoSpam,
34
    /// Own `PublicKey`.
35
    pub pk: PublicKey,
36
    /// Own `SecretKey`.
37
    pub sk: SecretKey,
38
}
39

40
/// Number of bytes of serialized [`NospamKeys`](./struct.NospamKeys.html).
41
pub const NOSPAMKEYSBYTES: usize = NOSPAMBYTES + crypto_box::KEY_SIZE + crypto_box::KEY_SIZE;
42

43
impl NospamKeys {
44
    pub fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
×
45
        let sk = SecretKey::generate(rng);
×
46
        let pk = sk.public_key();
×
47
        NospamKeys {
48
            nospam: rng.gen(),
×
49
            pk,
50
            sk,
51
        }
52
    }
53
}
54

55
/** Provided that there's at least [`NOSPAMKEYSBYTES`]
56
(./constant.NOSPAMKEYSBYTES.html) de-serializing will not fail.
57
*/
58
// NoSpam is defined in toxid.rs
59
impl FromBytes for NospamKeys {
60
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
1✔
61
        let (input, _) = tag([0x01, 0x00])(input)?;
2✔
62
        let (input, _) = tag(SECTION_MAGIC)(input)?;
2✔
63
        let (input, nospam) = NoSpam::from_bytes(input)?;
2✔
64
        let (input, pk) = PublicKey::from_bytes(input)?;
2✔
65
        let (input, sk) = SecretKey::from_bytes(input)?;
2✔
66
        Ok((input, NospamKeys { nospam, pk, sk }))
1✔
67
    }
68
}
69

70
impl ToBytes for NospamKeys {
71
    #[rustfmt::skip]
72
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
1✔
73
        do_gen!(buf,
4✔
74
            gen_le_u16!(0x0001) >>
×
75
            gen_slice!(SECTION_MAGIC) >>
×
76
            gen_slice!(self.nospam.0) >>
1✔
77
            gen_slice!(self.pk.as_ref()) >>
1✔
78
            gen_slice!(self.sk.as_bytes())
1✔
79
        )
80
    }
81
}
82

83
/** Own name, up to [`NAME_LEN`](./constant.NAME_LEN.html) bytes long.
84
*/
85
#[derive(Clone, Debug, Default, Eq, PartialEq)]
86
pub struct Name(pub Vec<u8>);
87

88
/// Length in bytes of name. ***Will be moved elsewhere.***
89
pub const NAME_LEN: usize = 128;
90

91
/** Produces up to [`NAME_LEN`](./constant.NAME_LEN.html) bytes long `Name`.
92
    Can't fail.
93
*/
94
impl FromBytes for Name {
95
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
96
        let (input, _) = tag([0x04, 0x00])(input)?;
3✔
97
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
98
        let (input, name_bytes) = rest(input)?;
4✔
99
        Ok((input, Name(name_bytes.to_vec())))
2✔
100
    }
101
}
102

103
impl ToBytes for Name {
104
    #[rustfmt::skip]
105
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
106
        do_gen!(buf,
4✔
107
            gen_le_u16!(0x0004) >>
×
108
            gen_slice!(SECTION_MAGIC) >>
×
109
            gen_slice!(self.0.as_slice())
2✔
110
        )
111
    }
112
}
113

114
/** DHT section of the old state format.
115
https://zetok.github.io/tox-spec/#dht-0x02
116
Default is empty, no Nodes.
117
*/
118
#[derive(Clone, Debug, Default, Eq, PartialEq)]
119
pub struct DhtState(pub Vec<PackedNode>);
120

121
/// Special, magical beginning of DHT section in LE.
122
const DHT_MAGICAL: u32 = 0x0159_000d;
123

124
/** Special DHT section type encoded in LE.
125

126
    https://zetok.github.io/tox-spec/#dht-sections
127
*/
128
const DHT_SECTION_TYPE: u16 = 0x0004;
129

130
/** Yet another magical number in DHT section that needs a check.
131

132
https://zetok.github.io/tox-spec/#dht-sections
133
*/
134
const DHT_2ND_MAGICAL: u16 = 0x11ce;
135

136
impl FromBytes for DhtState {
137
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
138
        let (input, _) = tag([0x02, 0x00])(input)?;
4✔
139
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
140
        let (input, _) = verify(le_u32, |value| *value == DHT_MAGICAL)(input)?; // check whether beginning of the section matches DHT magic bytes
8✔
141
        let (input, num_of_bytes) = le_u32(input)?;
4✔
142
        let (input, _) = verify(le_u16, |value| *value == DHT_SECTION_TYPE)(input)?; // check DHT section type
8✔
143
        let (input, _) = verify(le_u16, |value| *value == DHT_2ND_MAGICAL)(input)?; // check whether yet another magic number matches
8✔
144
        let (input, nodes) = map_parser(take(num_of_bytes), many0(PackedNode::from_bytes))(input)?;
5✔
145
        Ok((input, DhtState(nodes)))
2✔
146
    }
147
}
148

149
impl ToBytes for DhtState {
150
    #[rustfmt::skip]
151
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
152
        let start_idx = buf.1;
2✔
153

154
        let (buf, idx) = do_gen!(buf,
2✔
155
            gen_le_u16!(0x0002) >>
×
156
            gen_slice!(SECTION_MAGIC) >>
×
157
            gen_le_u32!(DHT_MAGICAL) >>
×
158
            gen_skip!(4) >>
×
159
            gen_le_u16!(DHT_SECTION_TYPE) >>
×
160
            gen_le_u16!(DHT_2ND_MAGICAL) >>
×
161
            gen_many_ref!(&self.0, |buf, node| PackedNode::to_bytes(node, buf))
4✔
162
        )?;
163

164
        let len = (idx - start_idx - 16) as u32;
4✔
165
        buf[start_idx + 8..start_idx + 12].copy_from_slice(&u32::to_le_bytes(len));
4✔
166
        Ok((buf, idx))
2✔
167
    }
168
}
169

170
/** Friend state status. Used by [`FriendState`](./struct.FriendState.html).
171

172
https://zetok.github.io/tox-spec/#friends-0x03
173

174
*/
175
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
176
pub enum FriendStatus {
177
    /// Not a friend. (When this can happen and what does it entail?)
178
    NotFriend = 0,
179
    /// Friend was added.
180
    Added = 1,
181
    /// Friend request was sent to the friend.
182
    FrSent = 2,
183
    /// Friend confirmed.
184
    /// (Something like toxcore knowing that friend accepted FR?)
185
    Confirmed = 3,
186
    /// Friend has come online.
187
    Online = 4,
188
}
189

190
impl FromBytes for FriendStatus {
191
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
192
        let (input, b) = le_u8(input)?;
2✔
193
        match b {
2✔
194
            0 => Ok((input, FriendStatus::NotFriend)),
×
195
            1 => Ok((input, FriendStatus::Added)),
1✔
196
            2 => Ok((input, FriendStatus::FrSent)),
1✔
197
            3 => Ok((input, FriendStatus::Confirmed)),
×
198
            4 => Ok((input, FriendStatus::Online)),
1✔
199
            _ => Err(nom::Err::Error(make_error(input, ErrorKind::Switch))),
×
200
        }
201
    }
202
}
203

204
/** User status. Used for both own & friend statuses.
205

206
https://zetok.github.io/tox-spec/#userstatus
207

208
*/
209
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210
pub enum UserWorkingStatus {
211
    /// User is `Online`.
212
    Online = 0,
213
    /// User is `Away`.
214
    Away = 1,
215
    /// User is `Busy`.
216
    Busy = 2,
217
}
218

219
/// Returns `UserWorkingStatus::Online`.
220
impl Default for UserWorkingStatus {
221
    fn default() -> Self {
UNCOV
222
        UserWorkingStatus::Online
×
223
    }
224
}
225

226
impl FromBytes for UserWorkingStatus {
227
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
228
        let (input, b) = le_u8(input)?;
2✔
229
        match b {
2✔
230
            0 => Ok((input, UserWorkingStatus::Online)),
2✔
231
            1 => Ok((input, UserWorkingStatus::Away)),
×
232
            2 => Ok((input, UserWorkingStatus::Busy)),
×
233
            _ => Err(nom::Err::Error(make_error(input, ErrorKind::Switch))),
×
234
        }
235
    }
236
}
237

238
/// Length in bytes of UserStatus
239
pub const USER_STATUS_LEN: usize = 1;
240

241
/// User status section
242
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
243
pub struct UserStatus(UserWorkingStatus);
244

245
impl FromBytes for UserStatus {
246
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
247
        let (input, _) = tag([0x06, 0x00])(input)?;
3✔
248
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
249
        let (input, user_status) = UserWorkingStatus::from_bytes(input)?;
4✔
250
        Ok((input, UserStatus(user_status)))
2✔
251
    }
252
}
253

254
impl ToBytes for UserStatus {
255
    #[rustfmt::skip]
256
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
257
        do_gen!(buf,
4✔
258
            gen_le_u16!(0x0006) >>
×
259
            gen_slice!(SECTION_MAGIC) >>
×
260
            gen_le_u8!(self.0 as u8)
2✔
261
        )
262
    }
263
}
264

265
/** Status message, up to [`STATUS_MSG_LEN`](./constant.STATUS_MSG_LEN.html)
266
bytes.
267

268
*/
269
#[derive(Clone, Debug, Default, Eq, PartialEq)]
270
pub struct StatusMsg(pub Vec<u8>);
271

272
/// Length in bytes of friend's status message.
273
// FIXME: move somewhere else
274
pub const STATUS_MSG_LEN: usize = 1007;
275

276
impl ToBytes for StatusMsg {
277
    #[rustfmt::skip]
278
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
279
        do_gen!(buf,
4✔
280
            gen_le_u16!(0x0005) >>
×
281
            gen_slice!(SECTION_MAGIC) >>
×
282
            gen_slice!(self.0.as_slice())
2✔
283
        )
284
    }
285
}
286

287
impl FromBytes for StatusMsg {
288
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
289
        let (input, _) = tag([0x05, 0x00])(input)?;
3✔
290
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
291
        let (input, status_msg_bytes) = rest(input)?;
4✔
292
        Ok((input, StatusMsg(status_msg_bytes.to_vec())))
2✔
293
    }
294
}
295

296
/// Contains list in `TcpUdpPackedNode` format.
297
#[derive(Clone, Debug, Default, Eq, PartialEq)]
298
pub struct TcpRelays(pub Vec<TcpUdpPackedNode>);
299

300
impl FromBytes for TcpRelays {
301
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
302
        let (input, _) = tag([0x0a, 0x00])(input)?;
3✔
303
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
304
        let (input, nodes) = many0(TcpUdpPackedNode::from_bytes)(input)?;
4✔
305
        Ok((input, TcpRelays(nodes)))
2✔
306
    }
307
}
308

309
impl ToBytes for TcpRelays {
310
    #[rustfmt::skip]
311
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
312
        do_gen!(buf,
2✔
313
            gen_le_u16!(0x000a) >>
×
314
            gen_slice!(SECTION_MAGIC) >>
×
315
            gen_many_ref!(&self.0, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
4✔
316
        )
317
    }
318
}
319

320
/// Contains list in `PackedNode` format.
321
#[derive(Clone, Debug, Default, Eq, PartialEq)]
322
pub struct PathNodes(pub Vec<TcpUdpPackedNode>);
323

324
impl FromBytes for PathNodes {
325
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
326
        let (input, _) = tag([0x0b, 0x00])(input)?;
3✔
327
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
328
        let (input, nodes) = many0(TcpUdpPackedNode::from_bytes)(input)?;
4✔
329
        Ok((input, PathNodes(nodes)))
2✔
330
    }
331
}
332

333
impl ToBytes for PathNodes {
334
    #[rustfmt::skip]
335
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
336
        do_gen!(buf,
2✔
337
            gen_le_u16!(0x000b) >>
×
338
            gen_slice!(SECTION_MAGIC) >>
×
339
            gen_many_ref!(&self.0, |buf, node| TcpUdpPackedNode::to_bytes(node, buf))
4✔
340
        )
341
    }
342
}
343

344
/** Friend state format for a single friend, compatible with what C toxcore
345
does with on `GCC x86{,_x64}` platform.
346

347
Data that is supposed to be strings (friend request message, friend name,
348
friend status message) might, or might not even be a valid UTF-8. **Anything
349
using that data should validate whether it's actually correct UTF-8!**
350

351
*feel free to add compatibility to what broken C toxcore does on other
352
platforms*
353

354
https://zetok.github.io/tox-spec/#friends-0x03
355
*/
356
#[derive(Clone, Debug, Eq, PartialEq)]
357
pub struct FriendState {
358
    friend_status: FriendStatus,
359
    pk: PublicKey,
360
    /// Friend request message that is being sent to friend.
361
    fr_msg: Vec<u8>,
362
    /// Friend's name.
363
    name: Name,
364
    status_msg: StatusMsg,
365
    user_status: UserWorkingStatus,
366
    nospam: NoSpam,
367
    /// Time when friend was last seen online.
368
    last_seen: u64,
369
}
370

371
/// Number of bytes of serialized [`FriendState`](./struct.FriendState.html).
372
pub const FRIENDSTATEBYTES: usize = 1      // "Status"
373
    + crypto_box::KEY_SIZE
374
    /* Friend request message      */ + REQUEST_MSG_LEN
375
    /* padding1                    */ + 1
376
    /* actual size of FR message   */ + 2
377
    /* Name;                       */ + NAME_LEN
378
    /* actual size of Name         */ + 2
379
    /* Status msg;                 */ + STATUS_MSG_LEN
380
    /* padding2                    */ + 1
381
    /* actual size of status msg   */ + 2
382
    /* UserStatus                  */ + 1
383
    /* padding3                    */ + 3
384
    /* only used for sending FR    */ + NOSPAMBYTES
385
    /* last time seen              */ + 8;
386

387
impl FromBytes for FriendState {
388
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
389
        let (input, friend_status) = FriendStatus::from_bytes(input)?;
2✔
390
        let (input, pk) = PublicKey::from_bytes(input)?;
4✔
391
        let (input, fr_msg_bytes) = take(REQUEST_MSG_LEN)(input)?;
4✔
392
        let (input, _) = take(1usize)(input)?;
4✔
393
        let (input, fr_msg_len) = be_u16(input)?;
4✔
394
        let (input, _) = verify(success(fr_msg_len), |len| *len <= REQUEST_MSG_LEN as u16)(input)?;
8✔
395
        let fr_msg = fr_msg_bytes[..fr_msg_len as usize].to_vec();
2✔
396
        let (input, name_bytes) = take(NAME_LEN)(input)?;
4✔
397
        let (input, name_len) = be_u16(input)?;
4✔
398
        let (input, _) = verify(success(name_len), |len| *len <= NAME_LEN as u16)(input)?;
8✔
399
        let name = Name(name_bytes[..name_len as usize].to_vec());
4✔
400
        let (input, status_msg_bytes) = take(STATUS_MSG_LEN)(input)?;
4✔
401
        let (input, _) = take(1usize)(input)?;
4✔
402
        let (input, status_msg_len) = be_u16(input)?;
4✔
403
        let (input, _) = verify(success(status_msg_len), |len| *len <= STATUS_MSG_LEN as u16)(input)?;
8✔
404
        let status_msg = StatusMsg(status_msg_bytes[..status_msg_len as usize].to_vec());
4✔
405
        let (input, user_status) = UserWorkingStatus::from_bytes(input)?;
4✔
406
        let (input, _) = take(3usize)(input)?;
4✔
407
        let (input, nospam) = NoSpam::from_bytes(input)?;
4✔
408
        let (input, last_seen) = le_u64(input)?;
4✔
409
        Ok((
2✔
410
            input,
411
            FriendState {
2✔
412
                friend_status,
413
                pk,
414
                fr_msg,
2✔
415
                name,
2✔
416
                status_msg,
2✔
417
                user_status,
418
                nospam,
419
                last_seen,
420
            },
421
        ))
422
    }
423
}
424

425
impl ToBytes for FriendState {
426
    #[allow(clippy::cognitive_complexity)]
427
    #[rustfmt::skip]
428
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
429
        let mut fr_msg_pad = self.fr_msg.clone();
2✔
430
        let mut name_pad = self.name.0.clone();
2✔
431
        let mut status_msg_pad = self.status_msg.0.clone();
2✔
432
        fr_msg_pad.resize(REQUEST_MSG_LEN, 0);
2✔
433
        name_pad.resize(NAME_LEN, 0);
2✔
434
        status_msg_pad.resize(STATUS_MSG_LEN, 0);
2✔
435

436
        do_gen!(buf,
10✔
437
            gen_le_u8!(self.friend_status as u8) >>
2✔
438
            gen_slice!(self.pk.as_ref()) >>
2✔
439
            gen_slice!(fr_msg_pad.as_slice()) >>
2✔
440
            gen_le_u8!(0) >>
×
441
            gen_be_u16!(self.fr_msg.len() as u16) >>
2✔
442
            gen_slice!(name_pad.as_slice()) >>
2✔
443
            gen_be_u16!(self.name.0.len() as u16) >>
2✔
444
            gen_slice!(status_msg_pad.as_slice()) >>
2✔
445
            gen_le_u8!(0) >>
×
446
            gen_be_u16!(self.status_msg.0.len() as u16) >>
2✔
447
            gen_le_u8!(self.user_status as u8) >>
2✔
448
            gen_le_u8!(0) >>
×
449
            gen_le_u16!(0) >>
×
450
            gen_slice!(self.nospam.0) >>
2✔
451
            gen_le_u64!(self.last_seen)
2✔
452
        )
453
    }
454
}
455

456
/** Wrapper struct for `Vec<FriendState>` to ease working with friend lists.
457
*/
458
#[derive(Clone, Debug, Default, Eq, PartialEq)]
459
pub struct Friends(pub Vec<FriendState>);
460

461
impl FromBytes for Friends {
462
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
463
        let (input, _) = tag([0x03, 0x00])(input)?;
3✔
464
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
465
        let (input, friends) = many0(map_parser(take(FRIENDSTATEBYTES), FriendState::from_bytes))(input)?;
4✔
466
        Ok((input, Friends(friends)))
2✔
467
    }
468
}
469

470
impl ToBytes for Friends {
471
    #[rustfmt::skip]
472
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
473
        do_gen!(buf,
2✔
474
            gen_le_u16!(0x0003) >>
×
475
            gen_slice!(SECTION_MAGIC) >>
×
476
            gen_many_ref!(&self.0, |buf, friend| FriendState::to_bytes(friend, buf))
4✔
477
        )
478
    }
479
}
480

481
/// End of the state format data.
482
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
483
pub struct Eof;
484

485
impl FromBytes for Eof {
486
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
2✔
487
        let (input, _) = tag([0xff, 0x00])(input)?;
3✔
488
        let (input, _) = tag(SECTION_MAGIC)(input)?;
4✔
489
        Ok((input, Eof))
2✔
490
    }
491
}
492

493
impl ToBytes for Eof {
494
    #[rustfmt::skip]
495
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
2✔
496
        do_gen!(buf,
2✔
497
            gen_le_u16!(0x00ff) >>
×
498
            gen_slice!(SECTION_MAGIC)
×
499
        )
500
    }
501
}
502

503
/** Sections of state format.
504

505
https://zetok.github.io/tox-spec/#sections
506
*/
507
#[derive(Clone)]
508
pub enum Section {
509
    /** Section for [`NoSpam`](../../toxid/struct.NoSpam.html), public and
510
    secret keys.
511

512
    https://zetok.github.io/tox-spec/#nospam-and-keys-0x01
513
    */
514
    NospamKeys(NospamKeys),
515
    /** Section for DHT-related data – [`DhtState`](./struct.DhtState.html).
516

517
    https://zetok.github.io/tox-spec/#dht-0x02
518
    */
519
    DhtState(DhtState),
520
    /** Section for friends data. Contains list of [`Friends`]
521
    (./struct.Friends.html).
522

523
    https://zetok.github.io/tox-spec/#friends-0x03
524
    */
525
    Friends(Friends),
526
    /** Section for own [`StatusMsg`](./struct.StatusMsg.html).
527

528
    https://zetok.github.io/tox-spec/#status-message-0x05
529
    */
530
    Name(Name),
531
    /** Section for own [`StatusMsg`](./struct.StatusMsg.html).
532

533
    https://zetok.github.io/tox-spec/#status-message-0x05
534
    */
535
    StatusMsg(StatusMsg),
536
    /** Section for own [`UserStatus`](./enum.UserStatus.html).
537

538
    https://zetok.github.io/tox-spec/#status-0x06
539
    */
540
    UserStatus(UserStatus),
541
    /** Section for a list of [`TcpRelays`](./struct.TcpRelays.html).
542

543
    https://zetok.github.io/tox-spec/#tcp-relays-0x0a
544
    */
545
    TcpRelays(TcpRelays),
546
    /** Section for a list of [`PathNodes`](./struct.PathNodes.html) for onion
547
    routing.
548

549
    https://zetok.github.io/tox-spec/#path-nodes-0x0b
550
    */
551
    PathNodes(PathNodes),
552
    /// End of file. https://zetok.github.io/tox-spec/#eof-0xff
553
    Eof(Eof),
554
}
555

556
impl FromBytes for Section {
557
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
1✔
558
        alt((
559
            map(NospamKeys::from_bytes, Section::NospamKeys),
560
            map(DhtState::from_bytes, Section::DhtState),
561
            map(Friends::from_bytes, Section::Friends),
562
            map(Name::from_bytes, Section::Name),
563
            map(StatusMsg::from_bytes, Section::StatusMsg),
564
            map(UserStatus::from_bytes, Section::UserStatus),
565
            map(TcpRelays::from_bytes, Section::TcpRelays),
566
            map(PathNodes::from_bytes, Section::PathNodes),
567
            map(Eof::from_bytes, Section::Eof),
568
        ))(input)
569
    }
570
}
571

572
impl ToBytes for Section {
573
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
1✔
574
        let (buf, start_idx) = buf;
1✔
575

576
        if buf.len() < start_idx + 4 {
1✔
577
            return Err(GenError::BufferTooSmall(start_idx + 4));
×
578
        }
579

580
        let buf = (buf, start_idx + 4);
2✔
581
        let (buf, idx) = match *self {
2✔
582
            Section::NospamKeys(ref p) => p.to_bytes(buf),
1✔
583
            Section::DhtState(ref p) => p.to_bytes(buf),
1✔
584
            Section::Friends(ref p) => p.to_bytes(buf),
1✔
585
            Section::Name(ref p) => p.to_bytes(buf),
1✔
586
            Section::StatusMsg(ref p) => p.to_bytes(buf),
1✔
587
            Section::UserStatus(ref p) => p.to_bytes(buf),
1✔
588
            Section::TcpRelays(ref p) => p.to_bytes(buf),
1✔
589
            Section::PathNodes(ref p) => p.to_bytes(buf),
1✔
590
            Section::Eof(ref p) => p.to_bytes(buf),
1✔
591
        }?;
592

593
        let len = (idx - start_idx - 8) as u32;
2✔
594
        buf[start_idx..start_idx + 4].copy_from_slice(&u32::to_le_bytes(len));
2✔
595
        Ok((buf, idx))
1✔
596
    }
597
}
598

599
/// State Format magic bytes.
600
const STATE_MAGIC: &[u8; 4] = &[0x1f, 0x1b, 0xed, 0x15];
601

602
/** Tox State sections. Use to manage `.tox` save files.
603

604
https://zetok.github.io/tox-spec/#state-format
605
*/
606
#[derive(Clone)]
607
pub struct State {
608
    sections: Vec<Section>,
609
}
610

611
impl FromBytes for State {
612
    fn from_bytes(input: &[u8]) -> IResult<&[u8], Self> {
1✔
613
        let (input, _) = tag(&[0; 4][..])(input)?;
1✔
614
        let (input, _) = tag(STATE_MAGIC)(input)?;
2✔
615
        let (input, sections) = many0(map_parser(length_data(map(le_u32, |len| len + 4)), Section::from_bytes))(input)?;
4✔
616
        Ok((
1✔
617
            input,
618
            State {
1✔
619
                sections: sections.to_vec(),
2✔
620
            },
621
        ))
622
    }
623
}
624

625
impl ToBytes for State {
626
    #[rustfmt::skip]
627
    fn to_bytes<'a>(&self, buf: (&'a mut [u8], usize)) -> Result<(&'a mut [u8], usize), GenError> {
1✔
628
        do_gen!(buf,
1✔
629
            gen_slice!([0; 4]) >>
1✔
630
            gen_slice!(STATE_MAGIC) >>
×
631
            gen_many_ref!(&self.sections, |buf, section| Section::to_bytes(section, buf))
2✔
632
        )
633
    }
634
}
635

636
#[cfg(test)]
637
mod tests {
638
    use super::*;
639

640
    use rand::thread_rng;
641
    use tox_packet::ip_port::*;
642

643
    encode_decode_test!(
644
        dht_state_encode_decode,
645
        DhtState(vec![
3✔
646
            PackedNode {
1✔
647
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
3✔
648
                saddr: "1.2.3.4:1234".parse().unwrap(),
1✔
649
            },
650
            PackedNode {
1✔
651
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
2✔
652
                saddr: "1.2.3.5:1235".parse().unwrap(),
1✔
653
            },
654
        ])
655
    );
656

657
    encode_decode_test!(
658
        friends_encode_decode,
659
        Friends(vec![
3✔
660
            FriendState {
1✔
661
                friend_status: FriendStatus::Added,
1✔
662
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
3✔
663
                fr_msg: b"test msg".to_vec(),
1✔
664
                name: Name(b"test name".to_vec()),
2✔
665
                status_msg: StatusMsg(b"test status msg".to_vec()),
2✔
666
                user_status: UserWorkingStatus::Online,
1✔
667
                nospam: NoSpam([7; NOSPAMBYTES]),
1✔
668
                last_seen: 1234,
669
            },
670
            FriendState {
1✔
671
                friend_status: FriendStatus::Added,
1✔
672
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
3✔
673
                fr_msg: b"test msg2".to_vec(),
1✔
674
                name: Name(b"test name2".to_vec()),
2✔
675
                status_msg: StatusMsg(b"test status msg2".to_vec()),
2✔
676
                user_status: UserWorkingStatus::Online,
1✔
677
                nospam: NoSpam([8; NOSPAMBYTES]),
1✔
678
                last_seen: 1235,
679
            },
680
        ])
681
    );
682

683
    encode_decode_test!(
684
        friend_state_encode_decode,
685
        FriendState {
1✔
686
            friend_status: FriendStatus::Added,
1✔
687
            pk: SecretKey::generate(&mut thread_rng()).public_key(),
2✔
688
            fr_msg: b"test msg".to_vec(),
1✔
689
            name: Name(b"test name".to_vec()),
2✔
690
            status_msg: StatusMsg(b"test status msg".to_vec()),
2✔
691
            user_status: UserWorkingStatus::Online,
1✔
692
            nospam: NoSpam([7; NOSPAMBYTES]),
1✔
693
            last_seen: 1234,
694
        }
695
    );
696

697
    encode_decode_test!(name_encode_decode, Name(vec![0, 1, 2, 3, 4]));
1✔
698

699
    encode_decode_test!(status_msg_encode_decode, StatusMsg(vec![0, 1, 2, 3, 4, 5]));
1✔
700

701
    encode_decode_test!(eof_encode_decode, Eof);
702

703
    encode_decode_test!(user_status_encode_decode, UserStatus(UserWorkingStatus::Online));
1✔
704

705
    encode_decode_test!(
706
        tcp_relays_encode_decode,
707
        TcpRelays(vec![
3✔
708
            TcpUdpPackedNode {
1✔
709
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
3✔
710
                ip_port: IpPort {
1✔
711
                    protocol: ProtocolType::Tcp,
1✔
712
                    ip_addr: "1.2.3.4".parse().unwrap(),
1✔
713
                    port: 1234,
714
                },
715
            },
716
            TcpUdpPackedNode {
1✔
717
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
2✔
718
                ip_port: IpPort {
1✔
719
                    protocol: ProtocolType::Udp,
1✔
720
                    ip_addr: "1.2.3.5".parse().unwrap(),
1✔
721
                    port: 12345,
722
                },
723
            },
724
        ])
725
    );
726

727
    encode_decode_test!(
728
        path_nodes_encode_decode,
729
        PathNodes(vec![
3✔
730
            TcpUdpPackedNode {
1✔
731
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
3✔
732
                ip_port: IpPort {
1✔
733
                    protocol: ProtocolType::Tcp,
1✔
734
                    ip_addr: "1.2.3.4".parse().unwrap(),
1✔
735
                    port: 1234,
736
                },
737
            },
738
            TcpUdpPackedNode {
1✔
739
                pk: SecretKey::generate(&mut thread_rng()).public_key(),
2✔
740
                ip_port: IpPort {
1✔
741
                    protocol: ProtocolType::Udp,
1✔
742
                    ip_addr: "1.2.3.5".parse().unwrap(),
1✔
743
                    port: 12345,
744
                },
745
            },
746
        ])
747
    );
748
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc