use core::convert::TryFrom;
#[cfg(feature = "alloc")]
use alloc::prelude::v1::*;
use crate::base::{Base, BaseOptions, Body, Header, PrivateOptions};
use crate::crypto;
use crate::error::Error;
use crate::options::Options;
use crate::types::*;
mod info;
pub use info::PageInfo;
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, Clone)]
pub struct Page {
pub id: Id,
pub header: Header,
pub info: PageInfo,
pub body: Body,
pub issued: Option<DateTime>,
pub expiry: Option<DateTime>,
pub public_options: Vec<Options>,
pub private_options: PrivateOptions,
pub previous_sig: Option<Signature>,
pub signature: Option<Signature>,
pub verified: bool,
pub raw: Option<Vec<u8>>,
_extend: (),
}
#[derive(Debug, Clone, PartialEq)]
pub struct PageOptions {
pub issued: Option<DateTime>,
pub expiry: Option<DateTime>,
pub public_options: Vec<Options>,
pub private_options: PrivateOptions,
pub previous_sig: Option<Signature>,
pub signature: Option<Signature>,
pub raw: Option<Vec<u8>>,
}
impl Default for PageOptions {
fn default() -> Self {
Self {
issued: None,
expiry: None,
public_options: vec![],
private_options: PrivateOptions::None,
previous_sig: None,
signature: None,
raw: None,
}
}
}
impl PartialEq for Page {
fn eq(&self, o: &Self) -> bool {
self.id == o.id
&& self.header == o.header
&& self.info == o.info
&& self.body == o.body
&& self.issued == o.issued
&& self.expiry == o.expiry
&& self.previous_sig == o.previous_sig
&& self.public_options == o.public_options
&& self.private_options == o.private_options
&& self.signature == o.signature
}
}
impl Page {
pub fn new(id: Id, header: Header, info: PageInfo, body: Body, options: PageOptions) -> Self {
Page {
id,
header,
info,
body,
issued: options.issued,
expiry: options.expiry,
public_options: options.public_options,
private_options: options.private_options,
previous_sig: options.previous_sig,
signature: options.signature,
raw: options.raw,
verified: false,
_extend: (),
}
}
pub fn id(&self) -> &Id {
&self.id
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn info(&self) -> &PageInfo {
&self.info
}
pub fn body(&self) -> &Body {
&self.body
}
pub fn issued(&self) -> Option<DateTime> {
self.issued
}
pub fn expiry(&self) -> Option<DateTime> {
self.expiry
}
#[cfg(feature = "std")]
pub fn valid(&self) -> bool {
use std::ops::Add;
let (issued, expiry): (Option<std::time::SystemTime>, Option<std::time::SystemTime>) = (
self.issued().map(|v| v.into()),
self.expiry().map(|v| v.into()),
);
match (issued, expiry) {
(_, Some(expiry)) => std::time::SystemTime::now() > expiry,
(Some(issued), None) => {
std::time::SystemTime::now() < issued.add(std::time::Duration::from_secs(3600))
}
_ => true,
}
}
pub fn public_options(&self) -> &[Options] {
&self.public_options
}
pub fn private_options(&self) -> &PrivateOptions {
&self.private_options
}
pub fn signature(&self) -> Option<Signature> {
self.signature.clone()
}
pub fn set_signature(&mut self, sig: Signature) {
self.signature = Some(sig);
}
pub fn raw(&self) -> &Option<Vec<u8>> {
&self.raw
}
pub fn clean(&mut self) {
self.raw = None;
}
}
impl Page {
pub fn decode_pages<V>(buff: &[u8], key_source: V) -> Result<Vec<Page>, Error>
where
V: Fn(&Id) -> Option<PublicKey>,
{
let mut pages = vec![];
let mut i = 0;
let mut last_key: Option<(Id, PublicKey)> = None;
while i < buff.len() {
let (b, n) = Base::parse(
&buff[i..],
|id| {
if let Some(key) = (key_source)(id) {
return Some(key);
};
if let Some(prev) = &last_key {
if &prev.0 == id {
return Some(prev.1.clone());
}
}
None
},
|_id| None,
)?;
i += n;
let page = match Page::try_from(b) {
Ok(p) => p,
Err(e) => {
error!("Error loading page from message: {:?}", e);
continue;
}
};
if let Some(key) = page.info().pub_key() {
last_key = Some((page.id().clone(), key));
}
pages.push(page);
}
Ok(pages)
}
pub fn encode_pages(pages: &[Page], buff: &mut [u8]) -> Result<usize, Error> {
let mut i = 0;
for p in pages {
match (&p.signature, &p.raw) {
(None, None) => {
error!("cannot encode page without associated signature or private key");
continue;
}
_ => (),
};
let mut b = Base::from(p);
let n = b.encode(None, None, &mut buff[i..])?;
i += n;
}
Ok(i)
}
}
impl From<&Page> for Base {
fn from(page: &Page) -> Base {
let sig = page.signature().clone();
let mut default_options = vec![];
if let Some(issued) = page.issued {
default_options.push(Options::issued(issued));
}
if let Some(expiry) = page.expiry {
default_options.push(Options::expiry(expiry));
}
if let Some(prev_sig) = &page.previous_sig {
default_options.push(Options::prev_sig(prev_sig));
}
match &page.info {
PageInfo::Primary(primary) => {
default_options.push(Options::public_key(primary.pub_key.clone()));
}
PageInfo::Secondary(secondary) => {
default_options.push(Options::peer_id(secondary.peer_id.clone()));
}
PageInfo::Data(_data) => {}
}
let mut public_options = page.public_options.clone();
public_options.append(&mut default_options);
let mut b = Base::new(
page.id.clone(),
page.header.clone(),
page.body.clone(),
BaseOptions {
public_options,
private_options: page.private_options.clone(),
..Default::default()
},
);
if let Some(sig) = sig {
b.set_signature(sig);
}
if let Some(raw) = &page.raw {
b.set_raw(raw.clone());
}
b.verified = page.verified;
b
}
}
impl TryFrom<Base> for Page {
type Error = Error;
fn try_from(base: Base) -> Result<Self, Error> {
let header = base.header();
let signature = base.signature();
let flags = header.flags();
let kind = header.kind();
if !kind.is_page() && !kind.is_data() {
return Err(Error::InvalidPageKind);
}
let (mut issued, mut expiry, mut previous_sig, mut peer_id) = (None, None, None, None);
let public_options = base
.public_options()
.iter()
.filter_map(|o| match &o {
Options::Issued(v) => {
issued = Some(v.when);
None
}
Options::Expiry(v) => {
expiry = Some(v.when);
None
}
Options::PrevSig(v) => {
previous_sig = Some(v.sig.clone());
None
}
Options::PeerId(v) => {
peer_id = Some(v.peer_id.clone());
None
}
_ => Some(o),
})
.map(|o| o.clone())
.collect();
let peer_id = base.peer_id.clone();
let _private_options = base.private_options();
let info = if kind.is_page() && !flags.contains(Flags::SECONDARY) {
let public_key: PublicKey = match &base.public_key {
Some(pk) => Ok(pk.clone()),
None => Err(Error::NoPublicKey),
}?;
let hash: Id = crypto::hash(&public_key).unwrap().into();
if &hash != base.id() {
return Err(Error::KeyIdMismatch);
}
PageInfo::primary(public_key)
} else if kind.is_page() && flags.contains(Flags::SECONDARY) {
let peer_id = match peer_id {
Some(id) => Ok(id),
None => Err(Error::NoPeerId),
}?;
PageInfo::secondary(peer_id)
} else if kind.is_data() {
PageInfo::Data(())
} else {
error!(
"Attempted to convert non-page base object ({:?}) to page",
kind
);
return Err(Error::UnexpectedPageType);
};
Ok(Page {
id: base.id().clone(),
header: header.clone(),
info,
body: base.body.clone(),
issued,
expiry,
previous_sig,
public_options,
private_options: base.private_options.clone(),
signature: signature.clone(),
verified: base.verified,
raw: base.raw().clone(),
_extend: (),
})
}
}