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
extern crate crypto_hash;
extern crate lexical_core;
extern crate serde;
extern crate ssb_json_msg_data;
extern crate ssb_multiformats;

use ssb_json_msg_data::LegacyF64;
use ssb_multiformats::{
    multibox::Multibox,
    multihash::Multihash,
    multikey::{Multikey, Multisig},
};

pub mod json;
pub mod verify;

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Content<T> {
    Encrypted(Multibox),
    Plain(T),
}

/// A complete ssb message, signed and all.
///
/// This does not check whether the `content` value is valid.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Message<T> {
    pub previous: Option<Multihash>,
    pub author: Multikey,
    pub sequence: u64,
    pub timestamp: LegacyF64,
    pub content: Content<T>,
    pub swapped: bool,
    pub signature: Option<Multisig>,
}

impl<T> Message<T> {
    /// Return whether the content of this message is encrypted.
    pub fn is_encrypted(&self) -> bool {
        match self.content {
            Content::Encrypted(..) => true,
            Content::Plain(..) => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::{Deserialize, Serialize};
    use ssb_json_msg_data::value::Value;
    use std::fs::read_to_string;

    #[test]
    fn parse() {
        // js-produced signing encoding (methinks)
        let s = read_to_string("./test-data/alice/about-value-from-log.json").unwrap();
        dbg!(&s);

        let (msg, _rest) = json::from_legacy::<Value>(&s.as_bytes()).unwrap();

        eprintln!("msg: {:?}", msg);

        assert!(msg.previous.is_none());

        // TODO: struct SigningEncoding
        // let reencoded = json::to_legacy_vec(&msg, false).unwrap();
        // assert_eq!(&s.as_bytes(), &reencoded.as_slice());
    }

    #[derive(Serialize, Deserialize, Debug)]
    struct Post {
        text: String,
        mentions: Vec<String>, //TODO: not String, MultiWhatever
    }

    #[derive(Serialize, Deserialize, Debug)]
    #[serde(tag = "type")] //
    enum MessageContent {
        #[serde(rename = "post")]
        Post(Post),
    }

    #[test]
    fn round_trip_a_post_message() {
        let s = read_to_string("./test-data/alice/post-valid.json").unwrap();
        dbg!(&s);

        let (msg, _rest) = json::from_legacy::<MessageContent>(&s.as_bytes()).unwrap();
        let sign_json = json::to_legacy_vec(&msg, false).unwrap();
        let (msg, _rest) = json::from_legacy::<MessageContent>(&sign_json).unwrap();

        eprintln!("msg: {:?}", msg);

        match msg.content {
            Content::Plain(MessageContent::Post(msg)) => assert_eq!(msg.text, "Bob?"),
            _ => panic!(),
        }
    }
}