use std::collections::BTreeMap;
use std::fmt;
use std::path::PathBuf;
use std::str;
use num_traits::FromPrimitive;
use crate::blob::{BlobError, BlobObject};
use crate::context::Context;
use crate::error;
use crate::mimeparser::SystemMessage;
/// Available param keys.
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord, FromPrimitive)]
#[repr(u8)]
pub enum Param {
/// For messages and jobs
File = b'f',
/// For Messages
Width = b'w',
/// For Messages
Height = b'h',
/// For Messages
Duration = b'd',
/// For Messages
MimeType = b'm',
/// For Messages: message is encryoted, outgoing: guarantee E2EE or the message is not send
GuaranteeE2ee = b'c',
/// For Messages: decrypted with validation errors or without mutual set, if neither
/// 'c' nor 'e' are preset, the messages is only transport encrypted.
ErroneousE2ee = b'e',
/// For Messages: force unencrypted message, either `ForcePlaintext::AddAutocryptHeader` (1),
/// `ForcePlaintext::NoAutocryptHeader` (2) or 0.
ForcePlaintext = b'u',
/// For Messages
WantsMdn = b'r',
/// For Messages
Forwarded = b'a',
/// For Messages
Cmd = b'S',
/// For Messages
Arg = b'E',
/// For Messages
Arg2 = b'F',
/// For Messages
Arg3 = b'G',
/// For Messages
Arg4 = b'H',
/// For Messages
Error = b'L',
/// For Messages: space-separated list of messaged IDs of forwarded copies.
///
/// This is used when a [crate::message::Message] is in the
/// [crate::message::MessageState::OutPending] state but is already forwarded.
/// In this case the forwarded messages are written to the
/// database and their message IDs are added to this parameter of
/// the original message, which is also saved in the database.
/// When the original message is then finally sent this parameter
/// is used to also send all the forwarded messages.
PrepForwards = b'P',
/// For Jobs
SetLatitude = b'l',
/// For Jobs
SetLongitude = b'n',
/// For Jobs
ServerFolder = b'Z',
/// For Jobs
ServerUid = b'z',
/// For Jobs
AlsoMove = b'M',
/// For Jobs: space-separated list of message recipients
Recipients = b'R',
// For Groups
Unpromoted = b'U',
// For Groups and Contacts
ProfileImage = b'i',
// For Chats
Selftalk = b'K',
// For Chats
Devicetalk = b'D',
// For QR
Auth = b's',
// For QR
GroupId = b'x',
// For QR
GroupName = b'g',
}
/// Possible values for `Param::ForcePlaintext`.
#[derive(PartialEq, Eq, Debug, Clone, Copy, FromPrimitive)]
#[repr(u8)]
pub enum ForcePlaintext {
AddAutocryptHeader = 1,
NoAutocryptHeader = 2,
}
/// An object for handling key=value parameter lists.
///
/// The structure is serialized by calling `to_string()` on it.
///
/// Only for library-internal use.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Params {
inner: BTreeMap,
}
impl fmt::Display for Params {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, (key, value)) in self.inner.iter().enumerate() {
if i > 0 {
writeln!(f)?;
}
write!(f, "{}={}", *key as u8 as char, value)?;
}
Ok(())
}
}
impl str::FromStr for Params {
type Err = error::Error;
fn from_str(s: &str) -> std::result::Result {
let mut inner = BTreeMap::new();
for pair in s.trim().lines() {
let pair = pair.trim();
if pair.is_empty() {
continue;
}
// TODO: probably nicer using a regex
ensure!(pair.len() > 1, "Invalid key pair: '{}'", pair);
let mut split = pair.splitn(2, '=');
let key = split.next();
let value = split.next();
ensure!(key.is_some(), "Missing key");
ensure!(value.is_some(), "Missing value");
let key = key.unwrap_or_default().trim();
let value = value.unwrap_or_default().trim();
if let Some(key) = Param::from_u8(key.as_bytes()[0]) {
inner.insert(key, value.to_string());
} else {
bail!("Unknown key: {}", key);
}
}
Ok(Params { inner })
}
}
impl Params {
/// Create new empty params.
pub fn new() -> Self {
Default::default()
}
/// Get the value of the given key, return `None` if no value is set.
pub fn get(&self, key: Param) -> Option<&str> {
self.inner.get(&key).map(|s| s.as_str())
}
/// Check if the given key is set.
pub fn exists(&self, key: Param) -> bool {
self.inner.contains_key(&key)
}
/// Set the given key to the passed in value.
pub fn set(&mut self, key: Param, value: impl AsRef) -> &mut Self {
self.inner.insert(key, value.as_ref().to_string());
self
}
/// Removes the given key, if it exists.
pub fn remove(&mut self, key: Param) -> &mut Self {
self.inner.remove(&key);
self
}
/// Check if there are any values in this.
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// Returns how many key-value pairs are set.
pub fn len(&self) -> usize {
self.inner.len()
}
/// Get the given parameter and parse as `i32`.
pub fn get_int(&self, key: Param) -> Option {
self.get(key).and_then(|s| s.parse().ok())
}
/// Get the parameter behind `Param::Cmd` interpreted as `SystemMessage`.
pub fn get_cmd(&self) -> SystemMessage {
self.get_int(Param::Cmd)
.and_then(SystemMessage::from_i32)
.unwrap_or_default()
}
/// Set the parameter behind `Param::Cmd`.
pub fn set_cmd(&mut self, value: SystemMessage) {
self.set_int(Param::Cmd, value as i32);
}
/// Get the given parameter and parse as `f64`.
pub fn get_float(&self, key: Param) -> Option {
self.get(key).and_then(|s| s.parse().ok())
}
/// Gets the given parameter and parse as [ParamsFile].
///
/// See also [Params::get_blob] and [Params::get_path] which may
/// be more convenient.
pub fn get_file<'a>(
&self,
key: Param,
context: &'a Context,
) -> Result