#![deny(unsafe_code)]
#![warn(missing_docs)]
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
fmt, io,
os::raw::c_int,
};
use log::debug;
use koibumi_core::{
address::{Address, Error as AddressError, ParseError as ParseAddressError},
crypto::{PrivateKey, PrivateKeyError},
encoding::{self, Encoding},
identity::{Features, Private as PrivateIdentity},
io::{SizedReadFromExact, WriteTo},
message::{self, InvHash},
object,
pow::{NonceTrialsPerByte, PayloadLengthExtraBytes},
time::Time,
};
const SQLITE_CONSTRAINT_PRIMARYKEY: c_int = 1555;
const SUBSCRIBERS: &str = "(Subscribers)";
#[derive(Debug)]
pub enum Error {
RusqliteError(rusqlite::Error),
TagMismatch,
AddressError(AddressError),
ParseAddressError(ParseAddressError),
TryIntoMsgError(object::TryIntoMsgError),
TryIntoBroadcastError(object::TryIntoBroadcastError),
DecryptError(object::DecryptError),
TryIntoMessageError(TryIntoMessageError),
AlreadyExists,
NotExists,
InvalidIdentity,
TryIntoPrivateKeysError(TryIntoPrivateKeysError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RusqliteError(err) => err.fmt(f),
Self::TagMismatch => "tag mismatch".fmt(f),
Self::AddressError(err) => err.fmt(f),
Self::ParseAddressError(err) => err.fmt(f),
Self::TryIntoMsgError(err) => err.fmt(f),
Self::TryIntoBroadcastError(err) => err.fmt(f),
Self::DecryptError(err) => err.fmt(f),
Self::TryIntoMessageError(err) => err.fmt(f),
Self::AlreadyExists => "already exists".fmt(f),
Self::NotExists => "not exists".fmt(f),
Self::InvalidIdentity => "invalid identity".fmt(f),
Self::TryIntoPrivateKeysError(err) => err.fmt(f),
}
}
}
impl std::error::Error for Error {}
impl From<rusqlite::Error> for Error {
fn from(err: rusqlite::Error) -> Self {
Self::RusqliteError(err)
}
}
impl From<AddressError> for Error {
fn from(err: AddressError) -> Self {
Self::AddressError(err)
}
}
impl From<ParseAddressError> for Error {
fn from(err: ParseAddressError) -> Self {
Self::ParseAddressError(err)
}
}
impl From<object::TryIntoMsgError> for Error {
fn from(err: object::TryIntoMsgError) -> Self {
Self::TryIntoMsgError(err)
}
}
impl From<object::TryIntoBroadcastError> for Error {
fn from(err: object::TryIntoBroadcastError) -> Self {
Self::TryIntoBroadcastError(err)
}
}
impl From<object::DecryptError> for Error {
fn from(err: object::DecryptError) -> Self {
Self::DecryptError(err)
}
}
impl From<TryIntoMessageError> for Error {
fn from(err: TryIntoMessageError) -> Self {
Self::TryIntoMessageError(err)
}
}
impl From<TryIntoPrivateKeysError> for Error {
fn from(err: TryIntoPrivateKeysError) -> Self {
Self::TryIntoPrivateKeysError(err)
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Contact {
address: Address,
}
impl Contact {
pub fn new(address: Address) -> Self {
Self { address }
}
pub fn address(&self) -> &Address {
&self.address
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct User {
id: Vec<u8>,
subscriptions: Vec<Address>,
private_identities: Vec<PrivateIdentity>,
contacts: Vec<Contact>,
aliases: HashMap<String, String>,
}
impl User {
pub fn id(&self) -> &[u8] {
&self.id
}
pub fn subscriptions(&self) -> &[Address] {
&self.subscriptions
}
pub fn subscriptions_mut(&mut self) -> &mut Vec<Address> {
&mut self.subscriptions
}
pub fn private_identities(&self) -> &[PrivateIdentity] {
&self.private_identities
}
pub fn private_identities_mut(&mut self) -> &mut Vec<PrivateIdentity> {
&mut self.private_identities
}
pub fn private_identity_by_address(&self, address: &Address) -> Option<&PrivateIdentity> {
self.private_identities
.iter()
.find(|i| &i.address() == address)
}
pub fn contacts(&self) -> &[Contact] {
&self.contacts
}
pub fn contacts_mut(&mut self) -> &mut Vec<Contact> {
&mut self.contacts
}
pub fn aliases(&self) -> &HashMap<String, String> {
&self.aliases
}
pub fn aliases_mut(&mut self) -> &mut HashMap<String, String> {
&mut self.aliases
}
pub fn alias<'a>(&'a self, address: &'a str) -> &'a str {
match self.aliases.get(address) {
Some(alias) => alias,
None => address,
}
}
pub fn rich_alias(&self, address: &str) -> String {
match self.aliases.get(address) {
Some(alias) => format!("{} ({})", alias, address),
None => address.to_string(),
}
}
}
impl From<User> for koibumi_node_sync::User {
fn from(user: User) -> Self {
Self::new(user.id, user.subscriptions, user.private_identities)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Message {
user_id: Vec<u8>,
object: message::Object,
time: Time,
to: Option<Address>,
from: Address,
encoding: Encoding,
content: Vec<u8>,
subject: String,
read: bool,
}
impl Message {
pub fn time(&self) -> Time {
self.time
}
pub fn to_address(&self) -> Option<&Address> {
self.to.as_ref()
}
pub fn from_address(&self) -> &Address {
&self.from
}
pub fn encoding(&self) -> Encoding {
self.encoding
}
pub fn content(&self) -> &[u8] {
&self.content
}
}
#[derive(Debug)]
pub enum TryIntoMessageError {
IoError(io::Error),
ParseAddressError(ParseAddressError),
InvalidEncoding(encoding::InvalidEncoding),
}
impl fmt::Display for TryIntoMessageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IoError(err) => err.fmt(f),
Self::ParseAddressError(err) => err.fmt(f),
Self::InvalidEncoding(err) => err.fmt(f),
}
}
}
impl std::error::Error for TryIntoMessageError {}
impl From<io::Error> for TryIntoMessageError {
fn from(err: io::Error) -> Self {
Self::IoError(err)
}
}
impl From<ParseAddressError> for TryIntoMessageError {
fn from(err: ParseAddressError) -> Self {
Self::ParseAddressError(err)
}
}
impl From<encoding::InvalidEncoding> for TryIntoMessageError {
fn from(err: encoding::InvalidEncoding) -> Self {
Self::InvalidEncoding(err)
}
}
impl TryFrom<MessageRow> for Message {
type Error = TryIntoMessageError;
fn try_from(value: MessageRow) -> Result<Self, <Self as TryFrom<MessageRow>>::Error> {
let object = message::Object::sized_read_from_exact(value.object)?;
let to = if value.to_address == SUBSCRIBERS {
None
} else {
Some(value.to_address.parse::<Address>()?)
};
let from = value.from_address.parse::<Address>()?;
let encoding: Encoding = (value.encoding as u64).try_into()?;
Ok(Self {
user_id: value.user,
object,
time: (value.time as u64).into(),
to,
from,
encoding,
content: value.content,
subject: value.subject,
read: value.read != 0,
})
}
}
struct MessageRow {
user: Vec<u8>,
object: Vec<u8>,
time: i64,
to_address: String,
from_address: String,
encoding: i64,
content: Vec<u8>,
subject: String,
read: i8,
}
impl From<Message> for MessageRow {
fn from(value: Message) -> Self {
let mut object = Vec::new();
value.object.write_to(&mut object).unwrap();
let time = if value.time.as_secs() > i64::MAX as u64 {
i64::MAX
} else {
value.time.as_secs() as i64
};
let to_address = if let Some(address) = value.to {
address.to_string()
} else {
SUBSCRIBERS.to_string()
};
Self {
user: value.user_id,
object,
time,
to_address,
from_address: value.from.to_string(),
encoding: value.encoding as i64,
content: value.content,
subject: value.subject,
read: value.read as i8,
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
struct PrivateKeys {
user_id: Vec<u8>,
enabled: bool,
private_identity: PrivateIdentity,
}
struct PrivateKeysRow {
user: Vec<u8>,
address: String,
enabled: bool,
features: i64,
nonce_trials_per_byte: i64,
payload_length_extra_bytes: i64,
signing_key: Vec<u8>,
encryption_key: Vec<u8>,
chan: bool,
}
#[derive(Debug)]
enum TryIntoPrivateKeysRowError {
OutOfRange,
}
impl fmt::Display for TryIntoPrivateKeysRowError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OutOfRange => "out of range".fmt(f),
}
}
}
impl std::error::Error for TryIntoPrivateKeysRowError {}
impl TryFrom<PrivateKeys> for PrivateKeysRow {
type Error = TryIntoPrivateKeysRowError;
fn try_from(value: PrivateKeys) -> Result<Self, <Self as TryFrom<PrivateKeys>>::Error> {
let nonce_trials_per_byte = value.private_identity.nonce_trials_per_byte().as_u64();
if nonce_trials_per_byte > i64::MAX as u64 {
return Err(TryIntoPrivateKeysRowError::OutOfRange);
}
let payload_length_extra_bytes =
value.private_identity.payload_length_extra_bytes().as_u64();
if payload_length_extra_bytes > i64::MAX as u64 {
return Err(TryIntoPrivateKeysRowError::OutOfRange);
}
Ok(Self {
user: value.user_id,
address: value.private_identity.address().to_string(),
enabled: value.enabled,
features: value.private_identity.features().bits() as i64,
nonce_trials_per_byte: nonce_trials_per_byte as i64,
payload_length_extra_bytes: payload_length_extra_bytes as i64,
signing_key: value
.private_identity
.private_signing_key()
.as_ref()
.to_vec(),
encryption_key: value
.private_identity
.private_encryption_key()
.as_ref()
.to_vec(),
chan: value.private_identity.chan(),
})
}
}
#[derive(Debug)]
pub enum TryIntoPrivateKeysError {
OutOfRange,
ParseAddressError(ParseAddressError),
TryFromSliceError(std::array::TryFromSliceError),
PrivateKeyError(PrivateKeyError),
AddressError(AddressError),
}
impl fmt::Display for TryIntoPrivateKeysError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OutOfRange => "out of range".fmt(f),
Self::ParseAddressError(err) => err.fmt(f),
Self::TryFromSliceError(err) => err.fmt(f),
Self::PrivateKeyError(err) => err.fmt(f),
Self::AddressError(err) => err.fmt(f),
}
}
}
impl std::error::Error for TryIntoPrivateKeysError {}
impl From<ParseAddressError> for TryIntoPrivateKeysError {
fn from(err: ParseAddressError) -> Self {
Self::ParseAddressError(err)
}
}
impl From<std::array::TryFromSliceError> for TryIntoPrivateKeysError {
fn from(err: std::array::TryFromSliceError) -> Self {
Self::TryFromSliceError(err)
}
}
impl From<PrivateKeyError> for TryIntoPrivateKeysError {
fn from(err: PrivateKeyError) -> Self {
Self::PrivateKeyError(err)
}
}
impl From<AddressError> for TryIntoPrivateKeysError {
fn from(err: AddressError) -> Self {
Self::AddressError(err)
}
}
impl TryFrom<PrivateKeysRow> for PrivateKeys {
type Error = TryIntoPrivateKeysError;
fn try_from(value: PrivateKeysRow) -> Result<Self, <Self as TryFrom<PrivateKeysRow>>::Error> {
let address = value.address.parse::<Address>()?;
if value.features < 0 || value.features > u32::MAX as i64 {
return Err(TryIntoPrivateKeysError::OutOfRange);
}
let features = Features::from_bits_direct(value.features as u32);
if value.nonce_trials_per_byte < 0 {
return Err(TryIntoPrivateKeysError::OutOfRange);
}
let nonce_trials_per_byte = NonceTrialsPerByte::from(value.nonce_trials_per_byte as u64);
if value.payload_length_extra_bytes < 0 {
return Err(TryIntoPrivateKeysError::OutOfRange);
}
let payload_length_extra_bytes =
PayloadLengthExtraBytes::from(value.payload_length_extra_bytes as u64);
let bytes: &[u8] = value.signing_key.as_ref();
let private_signing_key = PrivateKey::new(bytes.try_into()?)?;
let bytes: &[u8] = value.encryption_key.as_ref();
let private_encryption_key = PrivateKey::new(bytes.try_into()?)?;
let private_identity = PrivateIdentity::new(
address.version(),
address.stream(),
features,
nonce_trials_per_byte,
payload_length_extra_bytes,
private_signing_key,
private_encryption_key,
value.chan,
)?;
Ok(Self {
user_id: value.user,
enabled: value.enabled,
private_identity,
})
}
}
#[derive(Debug)]
pub struct Manager {
conn: rusqlite::Connection,
}
impl Manager {
pub fn new(conn: rusqlite::Connection) -> Result<Manager, Error> {
conn.execute(
"CREATE TABLE IF NOT EXISTS users (
id BLOB NOT NULL PRIMARY KEY,
enabled INTEGER NOT NULL,
name TEXT NOT NULL
)",
rusqlite::params![],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS subscriptions (
user BLOB NOT NULL,
address TEXT NOT NULL,
enabled INTEGER NOT NULL,
PRIMARY KEY(user, address)
)",
rusqlite::params![],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS messages (
user BLOB NOT NULL,
hash BLOB NOT NULL,
object BLOB NOT NULL,
time INTEGER NOT NULL,
to_address TEXT NOT NULL,
from_address TEXT NOT NULL,
encoding INTEGER NOT NULL,
content BLOB NOT NULL,
subject TEXT NOT NULL,
read INTEGER NOT NULL,
PRIMARY KEY(user, hash)
)",
rusqlite::params![],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS private_keys (
user BLOB NOT NULL,
address TEXT NOT NULL,
enabled INTEGER NOT NULL,
features INTEGER NOT NULL,
nonce_trials_per_byte INTEGER NOT NULL,
payload_length_extra_bytes INTEGER NOT NULL,
signing_key BLOB NOT NULL,
encryption_key BLOB NOT NULL,
chan INTEGER NOT NULL,
PRIMARY KEY(user, address)
)",
rusqlite::params![],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS contacts (
user BLOB NOT NULL,
address TEXT NOT NULL,
enabled INTEGER NOT NULL,
PRIMARY KEY(user, address)
)",
rusqlite::params![],
)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS aliases (
user BLOB NOT NULL,
address TEXT NOT NULL,
alias TEXT NOT NULL,
PRIMARY KEY(user, address)
)",
rusqlite::params![],
)?;
Ok(Self { conn })
}
pub fn user(&self, id: &[u8]) -> Result<User, Error> {
let mut stmt = self
.conn
.prepare("SELECT enabled, name FROM users WHERE id=?1")?;
let row = stmt.query_row(rusqlite::params![id], |row| {
Ok((row.get::<usize, i8>(0)?, row.get::<usize, String>(1)?))
});
if row.is_err() {
return Err(Error::NotExists);
}
let mut stmt = self
.conn
.prepare("SELECT address, enabled FROM subscriptions WHERE user=?1")?;
let subscriptions = stmt.query_map(rusqlite::params![id], |row| {
Ok((row.get::<usize, String>(0)?, row.get::<usize, i8>(1)?))
})?;
let mut slist = Vec::new();
for subscription in subscriptions {
let subscription = subscription?;
let address = subscription.0.parse::<Address>();
if address.is_err() {
continue;
}
slist.push(address.unwrap());
}
let mut stmt = self.conn.prepare(
"SELECT user, address, enabled,
features, nonce_trials_per_byte, payload_length_extra_bytes,
signing_key, encryption_key, chan
FROM private_keys
WHERE user=?1",
)?;
let private_identities = stmt.query_map(rusqlite::params![id], |row| {
Ok(PrivateKeysRow {
user: row.get::<usize, Vec<u8>>(0)?,
address: row.get::<usize, String>(1)?,
enabled: row.get::<usize, bool>(2)?,
features: row.get::<usize, i64>(3)?,
nonce_trials_per_byte: row.get::<usize, i64>(4)?,
payload_length_extra_bytes: row.get::<usize, i64>(5)?,
signing_key: row.get::<usize, Vec<u8>>(6)?,
encryption_key: row.get::<usize, Vec<u8>>(7)?,
chan: row.get::<usize, bool>(8)?,
})
})?;
let mut ilist = Vec::new();
for i in private_identities {
let private_keys: PrivateKeys = i?.try_into()?;
ilist.push(private_keys.private_identity);
}
let mut stmt = self
.conn
.prepare("SELECT address FROM contacts WHERE user=?1")?;
let contacts =
stmt.query_map(
rusqlite::params![id],
|row| Ok(row.get::<usize, String>(0)?),
)?;
let mut clist = Vec::new();
for i in contacts {
let address = i?.parse::<Address>()?;
let contact = Contact { address };
clist.push(contact);
}
let mut stmt = self
.conn
.prepare("SELECT address, alias FROM aliases WHERE user=?1")?;
let aliases = stmt.query_map(rusqlite::params![id], |row| {
Ok((row.get::<usize, String>(0)?, row.get::<usize, String>(1)?))
})?;
let mut amap = HashMap::new();
for alias in aliases {
let alias = alias?;
let address = alias.0.parse::<Address>();
if address.is_err() {
continue;
}
amap.insert(alias.0.clone(), alias.1.clone());
}
Ok(User {
id: id.to_vec(),
subscriptions: slist,
private_identities: ilist,
contacts: clist,
aliases: amap,
})
}
pub fn add_user(&self, id: &[u8], name: &str) -> Result<(), Error> {
if let Ok(_user) = self.user(id) {
return Err(Error::AlreadyExists);
}
let result = self.conn.execute(
"INSERT INTO users (
id, enabled, name
) VALUES (?1, ?2, ?3)",
rusqlite::params![id, true, name],
);
match result {
Ok(_) => Ok(()),
Err(rusqlite::Error::SqliteFailure(err, code)) => {
if let Some(code) = &code {
debug!("code: {}", code);
if code == &SQLITE_CONSTRAINT_PRIMARYKEY.to_string() {
return Err(Error::AlreadyExists);
}
}
Err(Error::RusqliteError(rusqlite::Error::SqliteFailure(
err, code,
)))
}
Err(err) => Err(Error::RusqliteError(err)),
}
}
pub fn subscribe(&self, user_id: &[u8], address: &Address) -> Result<(), Error> {
let result = self.conn.execute(
"INSERT INTO subscriptions (
user, address, enabled
) VALUES (?1, ?2, ?3)",
rusqlite::params![user_id, address.to_string(), true],
);
match result {
Ok(_) => Ok(()),
Err(rusqlite::Error::SqliteFailure(err, code)) => {
if let Some(code) = &code {
if code == &SQLITE_CONSTRAINT_PRIMARYKEY.to_string() {
return Err(Error::AlreadyExists);
}
}
Err(Error::RusqliteError(rusqlite::Error::SqliteFailure(
err, code,
)))
}
Err(err) => Err(Error::RusqliteError(err)),
}
}
pub fn get_message(&self, user_id: &[u8], hash: &InvHash) -> Result<Option<Message>, Error> {
let mut stmt = self.conn.prepare("SELECT user, object, time, to_address,
from_address, encoding, content, subject, read FROM messages WHERE user=?1 AND hash=?2")?;
let row = stmt.query_row(rusqlite::params![user_id, hash.as_ref()], |row| {
Ok(MessageRow {
user: row.get(0)?,
object: row.get(1)?,
time: row.get(2)?,
to_address: row.get(3)?,
from_address: row.get(4)?,
encoding: row.get(5)?,
content: row.get(6)?,
subject: row.get(7)?,
read: row.get(8)?,
})
});
if row.is_err() {
return Ok(None);
}
Ok(Some(row.unwrap().try_into()?))
}
fn insert_message(&self, message: &Message) -> Result<(), Error> {
let hash = message.object.inv_hash();
let row: MessageRow = message.clone().into();
self.conn.execute("INSERT INTO messages (
user, hash, object, time, to_address, from_address, encoding, content, subject, read
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
rusqlite::params![row.user, hash.as_ref(), row.object, row.time, row.to_address, row.from_address,
row.encoding, row.content, row.subject, row.read])?;
Ok(())
}
pub fn insert_msg(
&self,
user_id: Vec<u8>,
identity: &PrivateIdentity,
object: message::Object,
) -> Result<Message, Error> {
let msg = object::Msg::try_from(object.clone())?;
println!("****** MSG FOUND ******");
let content = msg.decrypt(object.header(), &identity)?;
let subject = if content.encoding() == encoding::Encoding::Simple {
if let Ok(simple) = encoding::Simple::try_from(content.message()) {
String::from_utf8_lossy(simple.subject()).to_string()
} else {
"(No subject)".to_string()
}
} else {
"(No subject)".to_string()
};
let message = Message {
user_id,
object,
time: Time::now(),
to: Some(identity.address()),
from: content.address()?,
encoding: content.encoding(),
content: content.message().to_vec(),
subject,
read: false,
};
self.insert_message(&message)?;
Ok(message)
}
pub fn insert_broadcast(
&self,
user_id: Vec<u8>,
address: Address,
object: message::Object,
) -> Result<Message, Error> {
let broadcast = object::Broadcast::try_from(object.clone())?;
match &broadcast {
object::Broadcast::V4(_) => (),
object::Broadcast::V5(v5) => {
if address.broadcast_tag() != *v5.tag() {
return Err(Error::TagMismatch);
}
println!("****** BROADCAST V5 FOUND ******");
}
}
let content = broadcast.decrypt(object.header(), &address)?;
let subject = if content.encoding() == encoding::Encoding::Simple {
if let Ok(simple) = encoding::Simple::try_from(content.message()) {
String::from_utf8_lossy(simple.subject()).to_string()
} else {
"(No subject)".to_string()
}
} else {
"(No subject)".to_string()
};
let message = Message {
user_id,
object,
time: Time::now(),
to: None,
from: address,
encoding: content.encoding(),
content: content.message().to_vec(),
subject,
read: false,
};
self.insert_message(&message)?;
Ok(message)
}
pub fn message_list(&self, user_id: &[u8]) -> Result<Vec<MessageEntry>, Error> {
let mut stmt = self.conn.prepare(
"SELECT hash, time, to_address,
from_address, subject, read FROM messages WHERE user=?1 ORDER BY time DESC",
)?;
let list = stmt.query_map(rusqlite::params![user_id], |row| {
Ok((
row.get::<usize, Vec<u8>>(0)?,
row.get::<usize, i64>(1)?,
row.get::<usize, String>(2)?,
row.get::<usize, String>(3)?,
row.get::<usize, String>(4)?,
row.get::<usize, i8>(5)?,
))
})?;
let mut r: Vec<MessageEntry> = Vec::new();
for elem in list {
let elem = elem?;
if elem.0.len() != 32 {
continue;
}
let hash: &[u8] = elem.0.as_ref();
let hash: [u8; 32] = hash.try_into().unwrap();
let hash = InvHash::new(hash);
let time = Time::new(elem.1 as u64);
let to = if elem.2 == SUBSCRIBERS {
None
} else {
let to = elem.2.parse::<Address>();
if to.is_err() {
continue;
}
Some(to.unwrap())
};
let from = elem.3.parse::<Address>();
if from.is_err() {
continue;
}
let from = from.unwrap();
let entry = MessageEntry {
hash,
time,
to,
from,
subject: elem.4,
read: elem.5 != 0,
};
r.push(entry);
}
Ok(r)
}
pub fn set_read(&self, user_id: &[u8], hash: &InvHash, read: bool) -> Result<(), Error> {
self.conn.execute(
"UPDATE messages SET read=?1 WHERE user=?2 and hash=?3",
rusqlite::params![read, user_id, hash.as_ref()],
)?;
Ok(())
}
pub fn add_private_identity(
&self,
user_id: &[u8],
identity: PrivateIdentity,
) -> Result<(), Error> {
if identity.nonce_trials_per_byte().as_u64() > i64::MAX as u64 {
return Err(Error::InvalidIdentity);
}
if identity.payload_length_extra_bytes().as_u64() > i64::MAX as u64 {
return Err(Error::InvalidIdentity);
}
self.conn.execute(
"INSERT INTO private_keys (
user, address, enabled,
features, nonce_trials_per_byte, payload_length_extra_bytes,
signing_key, encryption_key, chan
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)",
rusqlite::params![
user_id,
identity.address().to_string(),
true,
identity.features().bits() as i64,
identity.nonce_trials_per_byte().as_u64() as i64,
identity.payload_length_extra_bytes().as_u64() as i64,
identity.private_signing_key().as_ref(),
identity.private_encryption_key().as_ref(),
identity.chan()
],
)?;
Ok(())
}
pub fn add_contact(&self, user_id: &[u8], contact: &Contact) -> Result<(), Error> {
let result = self.conn.execute(
"INSERT INTO contacts (
user, address, enabled
) VALUES (?1, ?2, ?3)",
rusqlite::params![user_id, contact.address.to_string(), true],
);
match result {
Ok(_) => Ok(()),
Err(rusqlite::Error::SqliteFailure(err, code)) => {
if let Some(code) = &code {
if code == &SQLITE_CONSTRAINT_PRIMARYKEY.to_string() {
return Err(Error::AlreadyExists);
}
}
Err(Error::RusqliteError(rusqlite::Error::SqliteFailure(
err, code,
)))
}
Err(err) => Err(Error::RusqliteError(err)),
}
}
pub fn add_alias(&self, user_id: &[u8], address: &Address, alias: &str) -> Result<(), Error> {
let result = self.conn.execute(
"INSERT INTO aliases (
user, address, alias
) VALUES (?1, ?2, ?3)",
rusqlite::params![user_id, address.to_string(), alias],
);
match result {
Ok(_) => Ok(()),
Err(rusqlite::Error::SqliteFailure(err, code)) => {
if let Some(code) = &code {
if code == &SQLITE_CONSTRAINT_PRIMARYKEY.to_string() {
return Err(Error::AlreadyExists);
}
}
Err(Error::RusqliteError(rusqlite::Error::SqliteFailure(
err, code,
)))
}
Err(err) => Err(Error::RusqliteError(err)),
}
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct MessageEntry {
hash: InvHash,
time: Time,
to: Option<Address>,
from: Address,
subject: String,
read: bool,
}
impl MessageEntry {
pub fn hash(&self) -> &InvHash {
&self.hash
}
pub fn subject(&self) -> &str {
&self.subject
}
pub fn read(&self) -> bool {
self.read
}
pub fn set_read(&mut self, read: bool) {
self.read = read;
}
}
impl From<&Message> for MessageEntry {
fn from(message: &Message) -> MessageEntry {
Self {
hash: message.object.inv_hash(),
time: message.time,
to: message.to.clone(),
from: message.from.clone(),
subject: message.subject.clone(),
read: message.read,
}
}
}