mirror of
https://github.com/chatmail/core.git
synced 2026-05-18 06:16:29 +03:00
Compare commits
16 Commits
1.61.0
...
no-protect
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3253d427ec | ||
|
|
7fb305e898 | ||
|
|
d4255a4979 | ||
|
|
b21dcd17b7 | ||
|
|
0caea85d16 | ||
|
|
42e0fb5eb9 | ||
|
|
6061d71492 | ||
|
|
dbd8814d2c | ||
|
|
a3562c5940 | ||
|
|
49b07c1c6a | ||
|
|
51d220f1e0 | ||
|
|
9f81a94d86 | ||
|
|
f6098fc931 | ||
|
|
6e3c2fc839 | ||
|
|
30e616f74f | ||
|
|
5e29cae81a |
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -76,14 +76,13 @@ jobs:
|
||||
rust: 1.54.0
|
||||
python: false # Python bindings compilation on Windows is not supported.
|
||||
|
||||
# Minimum Supported Rust Version = 1.48.0
|
||||
# This is the Debian "bullseye" release version of Rust.
|
||||
# Minimum Supported Rust Version = 1.51.0
|
||||
#
|
||||
# Minimum Supported Python Version = 3.7
|
||||
# This is the minimum version for which manylinux Python wheels are
|
||||
# built.
|
||||
- os: ubuntu-latest
|
||||
rust: 1.48.0
|
||||
rust: 1.51.0
|
||||
python: 3.7
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
|
||||
112
Cargo.lock
generated
112
Cargo.lock
generated
@@ -206,7 +206,7 @@ dependencies = [
|
||||
"httparse",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"pin-project 1.0.8",
|
||||
"pin-project",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -226,7 +226,7 @@ dependencies = [
|
||||
"nom 6.1.2",
|
||||
"ouroboros",
|
||||
"pin-utils",
|
||||
"stop-token",
|
||||
"stop-token 0.2.0",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -310,7 +310,7 @@ dependencies = [
|
||||
"hostname 0.1.5",
|
||||
"log",
|
||||
"nom 5.1.2",
|
||||
"pin-project 1.0.8",
|
||||
"pin-project",
|
||||
"pin-utils",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@@ -363,15 +363,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-tar"
|
||||
version = "0.3.0"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb619eae01ab289095debb1ff7c02710d5124c20edde1b2eca926572a34c3998"
|
||||
checksum = "5c49359998a76e32ef6e870dbc079ebad8f1e53e8441c5dd39d27b44493fe331"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"filetime",
|
||||
"libc",
|
||||
"pin-project 0.4.28",
|
||||
"redox_syscall 0.1.57",
|
||||
"pin-project",
|
||||
"redox_syscall",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
@@ -652,9 +652,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.70"
|
||||
version = "1.0.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
|
||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||
|
||||
[[package]]
|
||||
name = "cfb-mode"
|
||||
@@ -1131,7 +1131,7 @@ dependencies = [
|
||||
"sha-1",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"stop-token",
|
||||
"stop-token 0.5.1",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"surf",
|
||||
@@ -1518,7 +1518,7 @@ checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.10",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -2104,9 +2104,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.102"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2a5ac8f984bfcf3a823267e5fde638acc3325f6496633a5da6bb6eb2171e103"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -2561,7 +2561,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.10",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
]
|
||||
@@ -2633,33 +2633,13 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "918192b5c59119d51e0cd221f4d49dde9112824ba717369e903c97d076083d0f"
|
||||
dependencies = [
|
||||
"pin-project-internal 0.4.28",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08"
|
||||
dependencies = [
|
||||
"pin-project-internal 1.0.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2687,9 +2667,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
@@ -2871,9 +2851,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -3037,12 +3017,6 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.10"
|
||||
@@ -3059,7 +3033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
"redox_syscall 0.2.10",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3361,9 +3335,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_qs"
|
||||
version = "0.8.4"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a72808528a89fa9eca23bbb6a1eb92cb639b881357269b6510f11e50c0f8a9"
|
||||
checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
@@ -3590,6 +3564,18 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stop-token"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "561eec821556b41afc80e8943d59ff72e5dc9876c364f26331293f2eb6c8e461"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"cfg-if 1.0.0",
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "str-buf"
|
||||
version = "1.0.5"
|
||||
@@ -3604,15 +3590,15 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.21.0"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
|
||||
checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.21.1"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
|
||||
checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@@ -3649,9 +3635,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.77"
|
||||
version = "1.0.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
|
||||
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3685,7 +3671,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"rand 0.8.4",
|
||||
"redox_syscall 0.2.10",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
@@ -3710,18 +3696,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.29"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.29"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3901,9 +3887,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.6"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085"
|
||||
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -4220,9 +4206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2c1e130bebaeab2f23886bf9acbaca14b092408c452543c857f66399cd6dab1"
|
||||
checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -4,6 +4,7 @@ version = "1.61.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
resolver = "2"
|
||||
|
||||
[profile.dev]
|
||||
debug = 0
|
||||
@@ -21,7 +22,7 @@ async-native-tls = { version = "0.3" }
|
||||
async-smtp = { git = "https://github.com/async-email/async-smtp", branch="master", features = ["socks5"] }
|
||||
async-std-resolver = "0.20"
|
||||
async-std = { version = "1", features = ["unstable"] }
|
||||
async-tar = "0.3"
|
||||
async-tar = "0.4"
|
||||
async-trait = "0.1"
|
||||
backtrace = "0.3"
|
||||
base64 = "0.13"
|
||||
@@ -64,9 +65,9 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
sha-1 = "0.9"
|
||||
sha2 = "0.9"
|
||||
smallvec = "1"
|
||||
stop-token = "0.2"
|
||||
strum = "0.21"
|
||||
strum_macros = "0.21"
|
||||
stop-token = "0.5"
|
||||
strum = "0.22"
|
||||
strum_macros = "0.22"
|
||||
surf = { version = "2.3", default-features = false, features = ["h1-client"] }
|
||||
thiserror = "1"
|
||||
toml = "0.5"
|
||||
|
||||
@@ -2130,7 +2130,7 @@ void dc_stop_ongoing_process (dc_context_t* context);
|
||||
#define DC_QR_FPR_MISMATCH 220 // id=contact
|
||||
#define DC_QR_FPR_WITHOUT_ADDR 230 // test1=formatted fingerprint
|
||||
#define DC_QR_ACCOUNT 250 // text1=domain
|
||||
#define DC_QR_WEBRTC_INSTANCE 260 // text1=domain
|
||||
#define DC_QR_WEBRTC_INSTANCE 260 // text1=domain, text2=instance pattern
|
||||
#define DC_QR_ADDR 320 // id=contact
|
||||
#define DC_QR_TEXT 330 // text1=text
|
||||
#define DC_QR_URL 332 // text1=URL
|
||||
|
||||
@@ -15,7 +15,7 @@ extern crate num_traits;
|
||||
extern crate serde_json;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryInto;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Write;
|
||||
use std::ops::Deref;
|
||||
use std::ptr;
|
||||
@@ -39,6 +39,7 @@ use deltachat::*;
|
||||
use deltachat::{accounts::Accounts, log::LogExt};
|
||||
|
||||
mod dc_array;
|
||||
mod lot;
|
||||
|
||||
mod string;
|
||||
use self::string::*;
|
||||
@@ -2043,10 +2044,11 @@ pub unsafe extern "C" fn dc_check_qr(
|
||||
}
|
||||
let ctx = &*context;
|
||||
|
||||
block_on(async move {
|
||||
let lot = qr::check_qr(ctx, &to_string_lossy(qr)).await;
|
||||
Box::into_raw(Box::new(lot))
|
||||
})
|
||||
let lot = match block_on(qr::check_qr(ctx, &to_string_lossy(qr))) {
|
||||
Ok(qr) => qr.into(),
|
||||
Err(err) => err.into(),
|
||||
};
|
||||
Box::into_raw(Box::new(lot))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -3047,7 +3049,7 @@ pub unsafe extern "C" fn dc_msg_get_summary(
|
||||
let ffi_msg = &mut *msg;
|
||||
let ctx = &*ffi_msg.context;
|
||||
|
||||
let summary = block_on(async move { ffi_msg.message.get_summary(ctx, maybe_chat).await })
|
||||
let summary = block_on(ffi_msg.message.get_summary(ctx, maybe_chat))
|
||||
.log_err(ctx, "dc_msg_get_summary failed")
|
||||
.unwrap_or_default();
|
||||
Box::into_raw(Box::new(summary.into()))
|
||||
@@ -3065,12 +3067,13 @@ pub unsafe extern "C" fn dc_msg_get_summarytext(
|
||||
let ffi_msg = &mut *msg;
|
||||
let ctx = &*ffi_msg.context;
|
||||
|
||||
block_on({
|
||||
ffi_msg
|
||||
.message
|
||||
.get_summarytext(ctx, approx_characters.try_into().unwrap_or_default())
|
||||
})
|
||||
.strdup()
|
||||
let summary = block_on(ffi_msg.message.get_summary(ctx, None))
|
||||
.log_err(ctx, "dc_msg_get_summarytext failed")
|
||||
.unwrap_or_default();
|
||||
match usize::try_from(approx_characters) {
|
||||
Ok(chars) => summary.truncated_text(chars).strdup(),
|
||||
Err(_) => summary.text.strdup(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -3607,7 +3610,7 @@ pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot_t) -> libc::c_int {
|
||||
}
|
||||
|
||||
let lot = &*lot;
|
||||
lot.get_state().to_i64().expect("impossible") as libc::c_int
|
||||
lot.get_state() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
245
deltachat-ffi/src/lot.rs
Normal file
245
deltachat-ffi/src/lot.rs
Normal file
@@ -0,0 +1,245 @@
|
||||
//! # Legacy generic return values for C API.
|
||||
|
||||
use crate::message::MessageState;
|
||||
use crate::qr::Qr;
|
||||
use crate::summary::{Summary, SummaryPrefix};
|
||||
use anyhow::Error;
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// An object containing a set of values.
|
||||
/// The meaning of the values is defined by the function returning the object.
|
||||
/// Lot objects are created
|
||||
/// eg. by chatlist.get_summary() or dc_msg_get_summary().
|
||||
///
|
||||
/// *Lot* is used in the meaning *heap* here.
|
||||
#[derive(Debug)]
|
||||
pub enum Lot {
|
||||
Summary(Summary),
|
||||
Qr(Qr),
|
||||
Error(String),
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Meaning {
|
||||
None = 0,
|
||||
Text1Draft = 1,
|
||||
Text1Username = 2,
|
||||
Text1Self = 3,
|
||||
}
|
||||
|
||||
impl Default for Meaning {
|
||||
fn default() -> Self {
|
||||
Meaning::None
|
||||
}
|
||||
}
|
||||
|
||||
impl Lot {
|
||||
pub fn get_text1(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Summary(summary) => match &summary.prefix {
|
||||
None => None,
|
||||
Some(SummaryPrefix::Draft(text)) => Some(text),
|
||||
Some(SummaryPrefix::Username(username)) => Some(username),
|
||||
Some(SummaryPrefix::Me(text)) => Some(text),
|
||||
},
|
||||
Self::Qr(qr) => match qr {
|
||||
Qr::AskVerifyContact { .. } => None,
|
||||
Qr::AskVerifyGroup { grpname, .. } => Some(grpname),
|
||||
Qr::FprOk { .. } => None,
|
||||
Qr::FprMismatch { .. } => None,
|
||||
Qr::FprWithoutAddr { fingerprint, .. } => Some(fingerprint),
|
||||
Qr::Account { domain } => Some(domain),
|
||||
Qr::WebrtcInstance { domain, .. } => Some(domain),
|
||||
Qr::Addr { .. } => None,
|
||||
Qr::Url { url } => Some(url),
|
||||
Qr::Text { text } => Some(text),
|
||||
Qr::WithdrawVerifyContact { .. } => None,
|
||||
Qr::WithdrawVerifyGroup { grpname, .. } => Some(grpname),
|
||||
Qr::ReviveVerifyContact { .. } => None,
|
||||
Qr::ReviveVerifyGroup { grpname, .. } => Some(grpname),
|
||||
},
|
||||
Self::Error(err) => Some(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_text2(&self) -> Option<Cow<str>> {
|
||||
match self {
|
||||
Self::Summary(summary) => Some(summary.truncated_text(160)),
|
||||
Self::Qr(_) => None,
|
||||
Self::Error(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_text1_meaning(&self) -> Meaning {
|
||||
match self {
|
||||
Self::Summary(summary) => match &summary.prefix {
|
||||
None => Meaning::None,
|
||||
Some(SummaryPrefix::Draft(_text)) => Meaning::Text1Draft,
|
||||
Some(SummaryPrefix::Username(_username)) => Meaning::Text1Username,
|
||||
Some(SummaryPrefix::Me(_text)) => Meaning::Text1Self,
|
||||
},
|
||||
Self::Qr(_qr) => Meaning::None,
|
||||
Self::Error(_err) => Meaning::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> LotState {
|
||||
match self {
|
||||
Self::Summary(summary) => summary.state.into(),
|
||||
Self::Qr(qr) => match qr {
|
||||
Qr::AskVerifyContact { .. } => LotState::QrAskVerifyContact,
|
||||
Qr::AskVerifyGroup { .. } => LotState::QrAskVerifyGroup,
|
||||
Qr::FprOk { .. } => LotState::QrFprOk,
|
||||
Qr::FprMismatch { .. } => LotState::QrFprMismatch,
|
||||
Qr::FprWithoutAddr { .. } => LotState::QrFprWithoutAddr,
|
||||
Qr::Account { .. } => LotState::QrAccount,
|
||||
Qr::WebrtcInstance { .. } => LotState::QrWebrtcInstance,
|
||||
Qr::Addr { .. } => LotState::QrAddr,
|
||||
Qr::Url { .. } => LotState::QrUrl,
|
||||
Qr::Text { .. } => LotState::QrText,
|
||||
Qr::WithdrawVerifyContact { .. } => LotState::QrWithdrawVerifyContact,
|
||||
Qr::WithdrawVerifyGroup { .. } => LotState::QrWithdrawVerifyGroup,
|
||||
Qr::ReviveVerifyContact { .. } => LotState::QrReviveVerifyContact,
|
||||
Qr::ReviveVerifyGroup { .. } => LotState::QrReviveVerifyGroup,
|
||||
},
|
||||
Self::Error(_err) => LotState::QrError,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> u32 {
|
||||
match self {
|
||||
Self::Summary(_) => Default::default(),
|
||||
Self::Qr(qr) => match qr {
|
||||
Qr::AskVerifyContact { contact_id, .. } => *contact_id,
|
||||
Qr::AskVerifyGroup { .. } => Default::default(),
|
||||
Qr::FprOk { contact_id } => *contact_id,
|
||||
Qr::FprMismatch { contact_id } => contact_id.unwrap_or_default(),
|
||||
Qr::FprWithoutAddr { .. } => Default::default(),
|
||||
Qr::Account { .. } => Default::default(),
|
||||
Qr::WebrtcInstance { .. } => Default::default(),
|
||||
Qr::Addr { contact_id } => *contact_id,
|
||||
Qr::Url { .. } => Default::default(),
|
||||
Qr::Text { .. } => Default::default(),
|
||||
Qr::WithdrawVerifyContact { contact_id, .. } => *contact_id,
|
||||
Qr::WithdrawVerifyGroup { .. } => Default::default(),
|
||||
Qr::ReviveVerifyContact { contact_id, .. } => *contact_id,
|
||||
Qr::ReviveVerifyGroup { .. } => Default::default(),
|
||||
},
|
||||
Self::Error(_) => Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> i64 {
|
||||
match self {
|
||||
Self::Summary(summary) => summary.timestamp,
|
||||
Self::Qr(_) => Default::default(),
|
||||
Self::Error(_) => Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LotState {
|
||||
// Default
|
||||
Undefined = 0,
|
||||
|
||||
// Qr States
|
||||
/// id=contact
|
||||
QrAskVerifyContact = 200,
|
||||
|
||||
/// text1=groupname
|
||||
QrAskVerifyGroup = 202,
|
||||
|
||||
/// id=contact
|
||||
QrFprOk = 210,
|
||||
|
||||
/// id=contact
|
||||
QrFprMismatch = 220,
|
||||
|
||||
/// text1=formatted fingerprint
|
||||
QrFprWithoutAddr = 230,
|
||||
|
||||
/// text1=domain
|
||||
QrAccount = 250,
|
||||
|
||||
/// text1=domain, text2=instance pattern
|
||||
QrWebrtcInstance = 260,
|
||||
|
||||
/// id=contact
|
||||
QrAddr = 320,
|
||||
|
||||
/// text1=text
|
||||
QrText = 330,
|
||||
|
||||
/// text1=URL
|
||||
QrUrl = 332,
|
||||
|
||||
/// text1=error string
|
||||
QrError = 400,
|
||||
|
||||
QrWithdrawVerifyContact = 500,
|
||||
|
||||
/// text1=groupname
|
||||
QrWithdrawVerifyGroup = 502,
|
||||
|
||||
QrReviveVerifyContact = 510,
|
||||
|
||||
/// text1=groupname
|
||||
QrReviveVerifyGroup = 512,
|
||||
|
||||
// Message States
|
||||
MsgInFresh = 10,
|
||||
MsgInNoticed = 13,
|
||||
MsgInSeen = 16,
|
||||
MsgOutPreparing = 18,
|
||||
MsgOutDraft = 19,
|
||||
MsgOutPending = 20,
|
||||
MsgOutFailed = 24,
|
||||
MsgOutDelivered = 26,
|
||||
MsgOutMdnRcvd = 28,
|
||||
}
|
||||
|
||||
impl Default for LotState {
|
||||
fn default() -> Self {
|
||||
LotState::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageState> for LotState {
|
||||
fn from(s: MessageState) -> Self {
|
||||
use MessageState::*;
|
||||
match s {
|
||||
Undefined => LotState::Undefined,
|
||||
InFresh => LotState::MsgInFresh,
|
||||
InNoticed => LotState::MsgInNoticed,
|
||||
InSeen => LotState::MsgInSeen,
|
||||
OutPreparing => LotState::MsgOutPreparing,
|
||||
OutDraft => LotState::MsgOutDraft,
|
||||
OutPending => LotState::MsgOutPending,
|
||||
OutFailed => LotState::MsgOutFailed,
|
||||
OutDelivered => LotState::MsgOutDelivered,
|
||||
OutMdnRcvd => LotState::MsgOutMdnRcvd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Summary> for Lot {
|
||||
fn from(summary: Summary) -> Self {
|
||||
Lot::Summary(summary)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Qr> for Lot {
|
||||
fn from(qr: Qr) -> Self {
|
||||
Lot::Qr(qr)
|
||||
}
|
||||
}
|
||||
|
||||
// Make it easy to convert errors into the final `Lot`.
|
||||
impl From<Error> for Lot {
|
||||
fn from(error: Error) -> Self {
|
||||
Lot::Error(error.to_string())
|
||||
}
|
||||
}
|
||||
@@ -1158,14 +1158,8 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
|
||||
}
|
||||
"checkqr" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
||||
let res = check_qr(&context, arg1).await;
|
||||
println!(
|
||||
"state={}, id={}, text1={:?}, text2={:?}",
|
||||
res.get_state(),
|
||||
res.get_id(),
|
||||
res.get_text1(),
|
||||
res.get_text2()
|
||||
);
|
||||
let qr = check_qr(&context, arg1).await?;
|
||||
println!("qr={:?}", qr);
|
||||
}
|
||||
"setqr" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
||||
|
||||
@@ -290,12 +290,7 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
port: param.imap.port,
|
||||
socket: param.imap.security,
|
||||
username: param.imap.user.clone(),
|
||||
strict_tls: match param.imap.certificate_checks {
|
||||
CertificateChecks::Automatic => None,
|
||||
CertificateChecks::Strict => Some(true),
|
||||
CertificateChecks::AcceptInvalidCertificates2
|
||||
| CertificateChecks::AcceptInvalidCertificates => Some(false),
|
||||
},
|
||||
strict_tls: None,
|
||||
})
|
||||
}
|
||||
if !servers
|
||||
@@ -308,14 +303,24 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
|
||||
port: param.smtp.port,
|
||||
socket: param.smtp.security,
|
||||
username: param.smtp.user.clone(),
|
||||
strict_tls: match param.smtp.certificate_checks {
|
||||
CertificateChecks::Automatic => None,
|
||||
CertificateChecks::Strict => Some(true),
|
||||
CertificateChecks::AcceptInvalidCertificates2
|
||||
| CertificateChecks::AcceptInvalidCertificates => Some(false),
|
||||
},
|
||||
strict_tls: None,
|
||||
})
|
||||
}
|
||||
|
||||
// respect certificate setting from function parameters
|
||||
for mut server in &mut servers {
|
||||
let certificate_checks = match server.protocol {
|
||||
Protocol::Imap => param.imap.certificate_checks,
|
||||
Protocol::Smtp => param.smtp.certificate_checks,
|
||||
};
|
||||
server.strict_tls = match certificate_checks {
|
||||
CertificateChecks::AcceptInvalidCertificates
|
||||
| CertificateChecks::AcceptInvalidCertificates2 => Some(false),
|
||||
CertificateChecks::Strict => Some(true),
|
||||
CertificateChecks::Automatic => server.strict_tls,
|
||||
};
|
||||
}
|
||||
|
||||
let servers = expand_param_vector(servers, ¶m.addr, ¶m_domain);
|
||||
|
||||
progress!(ctx, 550);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use anyhow::{bail, ensure, format_err, Context as _, Result};
|
||||
use anyhow::{bail, ensure, Context as _, Result};
|
||||
use async_std::path::PathBuf;
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use itertools::Itertools;
|
||||
@@ -1339,12 +1339,11 @@ fn cat_fingerprint(
|
||||
impl Context {
|
||||
/// determine whether the specified addr maps to the/a self addr
|
||||
pub async fn is_self_addr(&self, addr: &str) -> Result<bool> {
|
||||
let self_addr = self
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.await?
|
||||
.ok_or_else(|| format_err!("Not configured"))?;
|
||||
|
||||
Ok(addr_cmp(self_addr, addr))
|
||||
if let Some(self_addr) = self.get_config(Config::ConfiguredAddr).await? {
|
||||
Ok(addr_cmp(self_addr, addr))
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1491,7 +1490,7 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_is_self_addr() -> Result<()> {
|
||||
let t = TestContext::new().await;
|
||||
assert!(t.is_self_addr("me@me.org").await.is_err());
|
||||
assert_eq!(t.is_self_addr("me@me.org").await?, false);
|
||||
|
||||
let addr = t.configure_alice().await;
|
||||
assert_eq!(t.is_self_addr("me@me.org").await?, false);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Internet Message Format reception pipeline.
|
||||
|
||||
use std::cmp::min;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
@@ -18,9 +19,7 @@ use crate::constants::{
|
||||
};
|
||||
use crate::contact::{addr_cmp, normalize_name, Contact, Origin, VerifiedStatus};
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::{
|
||||
dc_create_smeared_timestamp, dc_extract_grpid_from_rfc724_mid, dc_smeared_time,
|
||||
};
|
||||
use crate::dc_tools::{dc_extract_grpid_from_rfc724_mid, dc_smeared_time};
|
||||
use crate::download::DownloadState;
|
||||
use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
|
||||
use crate::events::EventType;
|
||||
@@ -106,15 +105,6 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut sent_timestamp = if let Some(value) = mime_parser
|
||||
.get_header(HeaderDef::Date)
|
||||
.and_then(|value| mailparse::dateparse(value).ok())
|
||||
{
|
||||
value
|
||||
} else {
|
||||
dc_create_smeared_timestamp(context).await
|
||||
};
|
||||
|
||||
let rfc724_mid = mime_parser.get_rfc724_mid().unwrap_or_else(||
|
||||
// missing Message-IDs may come if the mail was set from this account with another
|
||||
// client that relies in the SMTP server to generate one.
|
||||
@@ -193,6 +183,12 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
.await?,
|
||||
);
|
||||
|
||||
let rcvd_timestamp = dc_smeared_time(context).await;
|
||||
let sent_timestamp = mime_parser
|
||||
.get_header(HeaderDef::Date)
|
||||
.and_then(|value| mailparse::dateparse(value).ok())
|
||||
.map_or(rcvd_timestamp, |value| min(value, rcvd_timestamp));
|
||||
|
||||
// Add parts
|
||||
let chat_id = add_parts(
|
||||
context,
|
||||
@@ -204,7 +200,8 @@ pub(crate) async fn dc_receive_imf_inner(
|
||||
server_uid,
|
||||
&to_ids,
|
||||
rfc724_mid,
|
||||
&mut sent_timestamp,
|
||||
sent_timestamp,
|
||||
rcvd_timestamp,
|
||||
from_id,
|
||||
&mut hidden,
|
||||
seen || replace_partial_download,
|
||||
@@ -406,7 +403,8 @@ async fn add_parts(
|
||||
server_uid: u32,
|
||||
to_ids: &ContactIds,
|
||||
rfc724_mid: String,
|
||||
sent_timestamp: &mut i64,
|
||||
sent_timestamp: i64,
|
||||
rcvd_timestamp: i64,
|
||||
from_id: u32,
|
||||
hidden: &mut bool,
|
||||
seen: bool,
|
||||
@@ -455,9 +453,6 @@ async fn add_parts(
|
||||
}
|
||||
}
|
||||
|
||||
let rcvd_timestamp = dc_smeared_time(context).await;
|
||||
*sent_timestamp = std::cmp::min(*sent_timestamp, rcvd_timestamp);
|
||||
|
||||
// check if the message introduces a new chat:
|
||||
// - outgoing messages introduce a chat with the first to: address if they are sent by a messenger
|
||||
// - incoming messages introduce a chat only for known contacts if they are sent by a messenger
|
||||
@@ -531,7 +526,7 @@ async fn add_parts(
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = create_or_lookup_group(
|
||||
context,
|
||||
&mut mime_parser,
|
||||
*sent_timestamp,
|
||||
sent_timestamp,
|
||||
if test_normal_chat.is_none() {
|
||||
allow_creation
|
||||
} else {
|
||||
@@ -761,7 +756,7 @@ async fn add_parts(
|
||||
if let Some((new_chat_id, new_chat_id_blocked)) = create_or_lookup_group(
|
||||
context,
|
||||
&mut mime_parser,
|
||||
*sent_timestamp,
|
||||
sent_timestamp,
|
||||
allow_creation,
|
||||
Blocked::Not,
|
||||
from_id,
|
||||
@@ -857,7 +852,7 @@ async fn add_parts(
|
||||
// correct message_timestamp, it should not be used before,
|
||||
// however, we cannot do this earlier as we need chat_id to be set
|
||||
let in_fresh = state == MessageState::InFresh;
|
||||
let sort_timestamp = calc_sort_timestamp(context, *sent_timestamp, chat_id, in_fresh).await?;
|
||||
let sort_timestamp = calc_sort_timestamp(context, sent_timestamp, chat_id, in_fresh).await?;
|
||||
|
||||
// Apply ephemeral timer changes to the chat.
|
||||
//
|
||||
@@ -896,7 +891,7 @@ async fn add_parts(
|
||||
chat_id
|
||||
);
|
||||
} else if chat_id
|
||||
.update_timestamp(context, Param::EphemeralSettingsTimestamp, *sent_timestamp)
|
||||
.update_timestamp(context, Param::EphemeralSettingsTimestamp, sent_timestamp)
|
||||
.await?
|
||||
{
|
||||
if let Err(err) = chat_id
|
||||
@@ -960,35 +955,40 @@ async fn add_parts(
|
||||
warn!(context, "verification problem: {}", err);
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(s);
|
||||
} else {
|
||||
// change chat protection only when verification check passes
|
||||
if let Some(new_status) = new_status {
|
||||
if chat_id
|
||||
} else if let Some(new_status) = new_status {
|
||||
if !chat.is_protected()
|
||||
&& new_status == ProtectionStatus::Protected
|
||||
&& chat_id
|
||||
.update_timestamp(
|
||||
context,
|
||||
Param::ProtectionSettingsTimestamp,
|
||||
*sent_timestamp,
|
||||
sent_timestamp,
|
||||
)
|
||||
.await?
|
||||
{
|
||||
// Upgrade chat to a protected chat.
|
||||
// As this gives some guarantees to the user,
|
||||
// we do not allow downgrades over the wire.
|
||||
if let Err(e) = chat_id
|
||||
.inner_set_protection(context, ProtectionStatus::Protected)
|
||||
.await
|
||||
{
|
||||
if let Err(e) = chat_id.inner_set_protection(context, new_status).await {
|
||||
chat::add_info_msg(
|
||||
context,
|
||||
chat_id,
|
||||
format!("Cannot set protection: {}", e),
|
||||
sort_timestamp,
|
||||
)
|
||||
.await?;
|
||||
return Ok(chat_id); // do not return an error as this would result in retrying the message
|
||||
}
|
||||
chat::add_info_msg(
|
||||
context,
|
||||
chat_id,
|
||||
format!("Cannot set protection: {}", e),
|
||||
sort_timestamp,
|
||||
)
|
||||
.await?;
|
||||
return Ok(chat_id); // do not return an error as this would result in retrying the message
|
||||
}
|
||||
set_better_msg(
|
||||
mime_parser,
|
||||
context
|
||||
.stock_protection_msg(new_status, from_id as u32)
|
||||
.await,
|
||||
);
|
||||
}
|
||||
set_better_msg(
|
||||
mime_parser,
|
||||
context
|
||||
.stock_protection_msg(new_status, from_id as u32)
|
||||
.await,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1047,7 +1047,6 @@ async fn add_parts(
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let sent_timestamp = *sent_timestamp;
|
||||
let is_hidden = *hidden;
|
||||
|
||||
let mut is_hidden = is_hidden;
|
||||
@@ -1304,11 +1303,7 @@ async fn calc_sort_timestamp(
|
||||
}
|
||||
}
|
||||
|
||||
if sort_timestamp >= dc_smeared_time(context).await {
|
||||
sort_timestamp = dc_create_smeared_timestamp(context).await;
|
||||
}
|
||||
|
||||
Ok(sort_timestamp)
|
||||
Ok(min(sort_timestamp, dc_smeared_time(context).await))
|
||||
}
|
||||
|
||||
async fn lookup_chat_by_reply(
|
||||
|
||||
@@ -84,9 +84,9 @@ pub(crate) fn dc_gm2local_offset() -> i64 {
|
||||
// but at max `MAX_SECONDS_TO_LEND_FROM_FUTURE`
|
||||
const MAX_SECONDS_TO_LEND_FROM_FUTURE: i64 = 5;
|
||||
|
||||
// returns the currently smeared timestamp,
|
||||
// may be used to check if call to dc_create_smeared_timestamp() is needed or not.
|
||||
// the returned timestamp MUST NOT be used to be sent out or saved in the database!
|
||||
/// Returns the current smeared timestamp,
|
||||
///
|
||||
/// The returned timestamp MUST NOT be sent out.
|
||||
pub(crate) async fn dc_smeared_time(context: &Context) -> i64 {
|
||||
let mut now = time();
|
||||
let ts = *context.last_smeared_timestamp.read().await;
|
||||
@@ -97,7 +97,7 @@ pub(crate) async fn dc_smeared_time(context: &Context) -> i64 {
|
||||
now
|
||||
}
|
||||
|
||||
// returns a timestamp that is guaranteed to be unique.
|
||||
/// Returns a timestamp that is guaranteed to be unique.
|
||||
pub(crate) async fn dc_create_smeared_timestamp(context: &Context) -> i64 {
|
||||
let now = time();
|
||||
let mut ret = now;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
//! # List of email headers.
|
||||
|
||||
use crate::strum::AsStaticRef;
|
||||
use mailparse::{MailHeader, MailHeaderMap};
|
||||
|
||||
#[derive(Debug, Display, Clone, PartialEq, Eq, EnumVariantNames, AsStaticStr)]
|
||||
#[derive(Debug, Display, Clone, PartialEq, Eq, EnumVariantNames, IntoStaticStr)]
|
||||
#[strum(serialize_all = "kebab_case")]
|
||||
pub enum HeaderDef {
|
||||
MessageId,
|
||||
@@ -67,9 +66,9 @@ pub enum HeaderDef {
|
||||
}
|
||||
|
||||
impl HeaderDef {
|
||||
/// Returns the corresponding Event id.
|
||||
/// Returns the corresponding header string.
|
||||
pub fn get_headername(&self) -> &'static str {
|
||||
self.as_static()
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
27
src/job.rs
27
src/job.rs
@@ -1084,7 +1084,7 @@ pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_
|
||||
"{} thread increases job {} tries to {}", &connection, job, tries
|
||||
);
|
||||
job.tries = tries;
|
||||
let time_offset = get_backoff_time_offset(tries);
|
||||
let time_offset = get_backoff_time_offset(tries, job.action);
|
||||
job.desired_timestamp = time() + time_offset;
|
||||
info!(
|
||||
context,
|
||||
@@ -1170,15 +1170,24 @@ async fn perform_job_action(
|
||||
try_res
|
||||
}
|
||||
|
||||
fn get_backoff_time_offset(tries: u32) -> i64 {
|
||||
let n = 2_i32.pow(tries - 1) * 60;
|
||||
let mut rng = thread_rng();
|
||||
let r: i32 = rng.gen();
|
||||
let mut seconds = r % (n + 1);
|
||||
if seconds < 1 {
|
||||
seconds = 1;
|
||||
fn get_backoff_time_offset(tries: u32, action: Action) -> i64 {
|
||||
match action {
|
||||
// Just try every 10s to update the quota
|
||||
// If all retries are exhausted, a new job will be created when the quota information is needed
|
||||
Action::UpdateRecentQuota => 10,
|
||||
|
||||
_ => {
|
||||
// Exponential backoff
|
||||
let n = 2_i32.pow(tries - 1) * 60;
|
||||
let mut rng = thread_rng();
|
||||
let r: i32 = rng.gen();
|
||||
let mut seconds = r % (n + 1);
|
||||
if seconds < 1 {
|
||||
seconds = 1;
|
||||
}
|
||||
seconds as i64
|
||||
}
|
||||
}
|
||||
seconds as i64
|
||||
}
|
||||
|
||||
async fn send_mdn(context: &Context, msg: &Message) -> Result<()> {
|
||||
|
||||
@@ -68,7 +68,6 @@ pub mod key;
|
||||
mod keyring;
|
||||
pub mod location;
|
||||
mod login_param;
|
||||
pub mod lot;
|
||||
pub mod message;
|
||||
mod mimefactory;
|
||||
pub mod mimeparser;
|
||||
|
||||
180
src/lot.rs
180
src/lot.rs
@@ -1,180 +0,0 @@
|
||||
//! # Legacy generic return values for C API.
|
||||
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
|
||||
use crate::key::Fingerprint;
|
||||
use crate::message::MessageState;
|
||||
use crate::summary::{Summary, SummaryPrefix};
|
||||
|
||||
/// An object containing a set of values.
|
||||
/// The meaning of the values is defined by the function returning the object.
|
||||
/// Lot objects are created
|
||||
/// eg. by chatlist.get_summary() or dc_msg_get_summary().
|
||||
///
|
||||
/// *Lot* is used in the meaning *heap* here.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Lot {
|
||||
pub(crate) text1_meaning: Meaning,
|
||||
pub(crate) text1: Option<String>,
|
||||
pub(crate) text2: Option<String>,
|
||||
pub(crate) timestamp: i64,
|
||||
pub(crate) state: LotState,
|
||||
pub(crate) id: u32,
|
||||
pub(crate) fingerprint: Option<Fingerprint>,
|
||||
pub(crate) invitenumber: Option<String>,
|
||||
pub(crate) auth: Option<String>,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
||||
)]
|
||||
pub enum Meaning {
|
||||
None = 0,
|
||||
Text1Draft = 1,
|
||||
Text1Username = 2,
|
||||
Text1Self = 3,
|
||||
}
|
||||
|
||||
impl Default for Meaning {
|
||||
fn default() -> Self {
|
||||
Meaning::None
|
||||
}
|
||||
}
|
||||
|
||||
impl Lot {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn get_text1(&self) -> Option<&str> {
|
||||
self.text1.as_deref()
|
||||
}
|
||||
|
||||
pub fn get_text2(&self) -> Option<&str> {
|
||||
self.text2.as_deref()
|
||||
}
|
||||
|
||||
pub fn get_text1_meaning(&self) -> Meaning {
|
||||
self.text1_meaning
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> LotState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> i64 {
|
||||
self.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(
|
||||
Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
|
||||
)]
|
||||
pub enum LotState {
|
||||
// Default
|
||||
Undefined = 0,
|
||||
|
||||
// Qr States
|
||||
/// id=contact
|
||||
QrAskVerifyContact = 200,
|
||||
|
||||
/// text1=groupname
|
||||
QrAskVerifyGroup = 202,
|
||||
|
||||
/// id=contact
|
||||
QrFprOk = 210,
|
||||
|
||||
/// id=contact
|
||||
QrFprMismatch = 220,
|
||||
|
||||
/// test1=formatted fingerprint
|
||||
QrFprWithoutAddr = 230,
|
||||
|
||||
/// text1=domain
|
||||
QrAccount = 250,
|
||||
|
||||
/// text1=domain, text2=instance pattern
|
||||
QrWebrtcInstance = 260,
|
||||
|
||||
/// id=contact
|
||||
QrAddr = 320,
|
||||
|
||||
/// text1=text
|
||||
QrText = 330,
|
||||
|
||||
/// text1=URL
|
||||
QrUrl = 332,
|
||||
|
||||
/// text1=error string
|
||||
QrError = 400,
|
||||
|
||||
QrWithdrawVerifyContact = 500,
|
||||
|
||||
/// text1=groupname
|
||||
QrWithdrawVerifyGroup = 502,
|
||||
|
||||
QrReviveVerifyContact = 510,
|
||||
|
||||
/// text1=groupname
|
||||
QrReviveVerifyGroup = 512,
|
||||
|
||||
// Message States
|
||||
MsgInFresh = 10,
|
||||
MsgInNoticed = 13,
|
||||
MsgInSeen = 16,
|
||||
MsgOutPreparing = 18,
|
||||
MsgOutDraft = 19,
|
||||
MsgOutPending = 20,
|
||||
MsgOutFailed = 24,
|
||||
MsgOutDelivered = 26,
|
||||
MsgOutMdnRcvd = 28,
|
||||
}
|
||||
|
||||
impl Default for LotState {
|
||||
fn default() -> Self {
|
||||
LotState::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MessageState> for LotState {
|
||||
fn from(s: MessageState) -> Self {
|
||||
use MessageState::*;
|
||||
match s {
|
||||
Undefined => LotState::Undefined,
|
||||
InFresh => LotState::MsgInFresh,
|
||||
InNoticed => LotState::MsgInNoticed,
|
||||
InSeen => LotState::MsgInSeen,
|
||||
OutPreparing => LotState::MsgOutPreparing,
|
||||
OutDraft => LotState::MsgOutDraft,
|
||||
OutPending => LotState::MsgOutPending,
|
||||
OutFailed => LotState::MsgOutFailed,
|
||||
OutDelivered => LotState::MsgOutDelivered,
|
||||
OutMdnRcvd => LotState::MsgOutMdnRcvd,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Summary> for Lot {
|
||||
fn from(summary: Summary) -> Self {
|
||||
let (text1, text1_meaning) = match summary.prefix {
|
||||
None => (None, Meaning::None),
|
||||
Some(SummaryPrefix::Draft(text)) => (Some(text), Meaning::Text1Draft),
|
||||
Some(SummaryPrefix::Username(username)) => (Some(username), Meaning::Text1Username),
|
||||
Some(SummaryPrefix::Me(text)) => (Some(text), Meaning::Text1Self),
|
||||
};
|
||||
Self {
|
||||
text1_meaning,
|
||||
text1,
|
||||
text2: Some(summary.text),
|
||||
timestamp: summary.timestamp,
|
||||
state: summary.state.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ use crate::mimeparser::{parse_message_id, FailureReport, SystemMessage};
|
||||
use crate::param::{Param, Params};
|
||||
use crate::pgp::split_armored_data;
|
||||
use crate::stock_str;
|
||||
use crate::summary::{get_summarytext_by_raw, Summary};
|
||||
use crate::summary::Summary;
|
||||
|
||||
/// Message ID, including reserved IDs.
|
||||
///
|
||||
@@ -592,7 +592,7 @@ impl Message {
|
||||
}
|
||||
|
||||
/// Returns message summary for display in the search results.
|
||||
pub async fn get_summary(&mut self, context: &Context, chat: Option<&Chat>) -> Result<Summary> {
|
||||
pub async fn get_summary(&self, context: &Context, chat: Option<&Chat>) -> Result<Summary> {
|
||||
let chat_loaded: Chat;
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
@@ -616,18 +616,6 @@ impl Message {
|
||||
Ok(Summary::new(context, self, chat, contact.as_ref()).await)
|
||||
}
|
||||
|
||||
pub async fn get_summarytext(&self, context: &Context, approx_characters: usize) -> String {
|
||||
get_summarytext_by_raw(
|
||||
self.viewtype,
|
||||
self.text.as_ref(),
|
||||
self.is_forwarded(),
|
||||
&self.param,
|
||||
approx_characters,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// It's a little unfortunate that the UI has to first call dc_msg_get_override_sender_name() and then if it was NULL, call
|
||||
// dc_contact_get_display_name() but this was the best solution:
|
||||
// - We could load a Contact struct from the db here to call get_display_name() instead of returning None, but then we had a db
|
||||
@@ -871,7 +859,11 @@ impl Message {
|
||||
Param::Quote,
|
||||
if text.is_empty() {
|
||||
// Use summary, similar to "Image" to avoid sending empty quote.
|
||||
quote.get_summarytext(context, 500).await
|
||||
quote
|
||||
.get_summary(context, None)
|
||||
.await?
|
||||
.truncated_text(500)
|
||||
.to_string()
|
||||
} else {
|
||||
text
|
||||
},
|
||||
|
||||
@@ -1157,7 +1157,11 @@ impl<'a> MimeFactory<'a> {
|
||||
{
|
||||
stock_str::encrypted_msg(context).await
|
||||
} else {
|
||||
self.msg.get_summarytext(context, 32).await
|
||||
self.msg
|
||||
.get_summary(context, None)
|
||||
.await?
|
||||
.truncated_text(32)
|
||||
.to_string()
|
||||
};
|
||||
let p2 = stock_str::read_rcpt_mail_body(context, p1).await;
|
||||
let message_text = format!("{}\r\n", format_flowed(&p2));
|
||||
|
||||
13
src/quota.rs
13
src/quota.rs
@@ -110,12 +110,13 @@ pub fn needs_quota_warning(curr_percentage: u64, warned_at_percentage: u64) -> b
|
||||
impl Context {
|
||||
// Adds a job to update `quota.recent`
|
||||
pub(crate) async fn schedule_quota_update(&self) -> Result<()> {
|
||||
job::kill_action(self, Action::UpdateRecentQuota).await?;
|
||||
job::add(
|
||||
self,
|
||||
job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0),
|
||||
)
|
||||
.await?;
|
||||
if !job::action_exists(self, Action::UpdateRecentQuota).await? {
|
||||
job::add(
|
||||
self,
|
||||
job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ mod bobstate;
|
||||
mod qrinvite;
|
||||
|
||||
use bobstate::{BobHandshakeStage, BobState, BobStateHandle};
|
||||
use qrinvite::{QrError, QrInvite};
|
||||
use qrinvite::QrInvite;
|
||||
|
||||
pub const NON_ALPHANUMERIC_WITHOUT_DOT: &AsciiSet = &NON_ALPHANUMERIC.remove(b'.');
|
||||
|
||||
@@ -247,9 +247,6 @@ async fn get_self_fingerprint(context: &Context) -> Option<Fingerprint> {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum JoinError {
|
||||
#[error("Unknown QR-code: {0}")]
|
||||
QrCode(#[from] QrError),
|
||||
|
||||
#[error("An \"ongoing\" process is already running")]
|
||||
OngoingRunning,
|
||||
|
||||
@@ -293,7 +290,7 @@ async fn securejoin(context: &Context, qr: &str) -> Result<ChatId, JoinError> {
|
||||
========================================================*/
|
||||
|
||||
info!(context, "Requesting secure-join ...",);
|
||||
let qr_scan = check_qr(context, qr).await;
|
||||
let qr_scan = check_qr(context, qr).await?;
|
||||
|
||||
let invite = QrInvite::try_from(qr_scan)?;
|
||||
|
||||
@@ -1146,7 +1143,7 @@ mod tests {
|
||||
async fn test_setup_contact_bad_qr() {
|
||||
let bob = TestContext::new_bob().await;
|
||||
let ret = dc_join_securejoin(&bob.ctx, "not a qr code").await;
|
||||
assert!(matches!(ret, Err(JoinError::QrCode(_))));
|
||||
assert!(ret.is_err());
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
//! Supporting code for the QR-code invite.
|
||||
//!
|
||||
//! QR-codes are decoded into a more general-purpose [`Lot`] struct normally, this struct is
|
||||
//! so general it is not even specific to QR-codes. This makes working with it rather hard,
|
||||
//! so here we have a wrapper type that specifically deals with Secure-Join QR-codes so
|
||||
//! that the Secure-Join code can have many more guarantees when dealing with this.
|
||||
//! QR-codes are decoded into a more general-purpose [`Qr`] struct normally. This makes working
|
||||
//! with it rather hard, so here we have a wrapper type that specifically deals with Secure-Join
|
||||
//! QR-codes so that the Secure-Join code can have more guarantees when dealing with this.
|
||||
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Error, Result};
|
||||
|
||||
use crate::key::Fingerprint;
|
||||
use crate::lot::{Lot, LotState};
|
||||
use crate::qr::Qr;
|
||||
|
||||
/// Represents the data from a QR-code scan.
|
||||
///
|
||||
@@ -66,53 +65,38 @@ impl QrInvite {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Lot> for QrInvite {
|
||||
type Error = QrError;
|
||||
impl TryFrom<Qr> for QrInvite {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(lot: Lot) -> Result<Self, Self::Error> {
|
||||
if lot.state != LotState::QrAskVerifyContact && lot.state != LotState::QrAskVerifyGroup {
|
||||
return Err(QrError::UnsupportedProtocol);
|
||||
}
|
||||
if lot.id == 0 {
|
||||
return Err(QrError::MissingContactId);
|
||||
}
|
||||
let fingerprint = lot.fingerprint.ok_or(QrError::MissingFingerprint)?;
|
||||
let invitenumber = lot.invitenumber.ok_or(QrError::MissingInviteNumber)?;
|
||||
let authcode = lot.auth.ok_or(QrError::MissingAuthCode)?;
|
||||
match lot.state {
|
||||
LotState::QrAskVerifyContact => Ok(QrInvite::Contact {
|
||||
contact_id: lot.id,
|
||||
fn try_from(qr: Qr) -> Result<Self> {
|
||||
match qr {
|
||||
Qr::AskVerifyContact {
|
||||
contact_id,
|
||||
fingerprint,
|
||||
invitenumber,
|
||||
authcode,
|
||||
} => Ok(QrInvite::Contact {
|
||||
contact_id,
|
||||
fingerprint,
|
||||
invitenumber,
|
||||
authcode,
|
||||
}),
|
||||
LotState::QrAskVerifyGroup => Ok(QrInvite::Group {
|
||||
contact_id: lot.id,
|
||||
Qr::AskVerifyGroup {
|
||||
grpname,
|
||||
grpid,
|
||||
contact_id,
|
||||
fingerprint,
|
||||
name: lot.text1.ok_or(QrError::MissingGroupName)?,
|
||||
grpid: lot.text2.ok_or(QrError::MissingGroupId)?,
|
||||
invitenumber,
|
||||
authcode,
|
||||
} => Ok(QrInvite::Group {
|
||||
contact_id,
|
||||
fingerprint,
|
||||
name: grpname,
|
||||
grpid,
|
||||
invitenumber,
|
||||
authcode,
|
||||
}),
|
||||
_ => Err(QrError::UnsupportedProtocol),
|
||||
_ => bail!("Unsupported QR type {:?}", qr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum QrError {
|
||||
#[error("Unsupported protocol in QR-code")]
|
||||
UnsupportedProtocol,
|
||||
#[error("Missing fingerprint")]
|
||||
MissingFingerprint,
|
||||
#[error("Missing invitenumber")]
|
||||
MissingInviteNumber,
|
||||
#[error("Missing auth code")]
|
||||
MissingAuthCode,
|
||||
#[error("Missing group name")]
|
||||
MissingGroupName,
|
||||
#[error("Missing group id")]
|
||||
MissingGroupId,
|
||||
#[error("Missing contact id")]
|
||||
MissingContactId,
|
||||
}
|
||||
|
||||
347
src/summary.rs
347
src/summary.rs
@@ -7,15 +7,12 @@ use crate::context::Context;
|
||||
use crate::dc_tools::dc_truncate;
|
||||
use crate::message::{Message, MessageState};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::{Param, Params};
|
||||
use crate::param::Param;
|
||||
use crate::stock_str;
|
||||
use itertools::Itertools;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
|
||||
// In practice, the user additionally cuts the string themselves
|
||||
// pixel-accurate.
|
||||
const SUMMARY_CHARACTERS: usize = 160;
|
||||
|
||||
/// Prefix displayed before message and separated by ":" in the chatlist.
|
||||
#[derive(Debug)]
|
||||
pub enum SummaryPrefix {
|
||||
@@ -85,15 +82,7 @@ impl Summary {
|
||||
}
|
||||
};
|
||||
|
||||
let mut text = get_summarytext_by_raw(
|
||||
msg.viewtype,
|
||||
msg.text.as_ref(),
|
||||
msg.is_forwarded(),
|
||||
&msg.param,
|
||||
SUMMARY_CHARACTERS,
|
||||
context,
|
||||
)
|
||||
.await;
|
||||
let mut text = msg.get_summary_text(context).await;
|
||||
|
||||
if text.is_empty() && msg.quoted_text().is_some() {
|
||||
text = stock_str::reply_noun(context).await
|
||||
@@ -106,88 +95,87 @@ impl Summary {
|
||||
state: msg.state,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`Summary::text`] attribute truncated to an approximate length.
|
||||
pub fn truncated_text(&self, approx_chars: usize) -> Cow<str> {
|
||||
dc_truncate(&self.text, approx_chars)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a summary text.
|
||||
pub async fn get_summarytext_by_raw(
|
||||
viewtype: Viewtype,
|
||||
text: Option<impl AsRef<str>>,
|
||||
was_forwarded: bool,
|
||||
param: &Params,
|
||||
approx_characters: usize,
|
||||
context: &Context,
|
||||
) -> String {
|
||||
let mut append_text = true;
|
||||
let prefix = match viewtype {
|
||||
Viewtype::Image => stock_str::image(context).await,
|
||||
Viewtype::Gif => stock_str::gif(context).await,
|
||||
Viewtype::Sticker => stock_str::sticker(context).await,
|
||||
Viewtype::Video => stock_str::video(context).await,
|
||||
Viewtype::Voice => stock_str::voice_message(context).await,
|
||||
Viewtype::Audio | Viewtype::File => {
|
||||
if param.get_cmd() == SystemMessage::AutocryptSetupMessage {
|
||||
append_text = false;
|
||||
stock_str::ac_setup_msg_subject(context).await
|
||||
} else {
|
||||
let file_name: String = param
|
||||
.get_path(Param::File, context)
|
||||
.unwrap_or(None)
|
||||
.and_then(|path| {
|
||||
path.file_name()
|
||||
.map(|fname| fname.to_string_lossy().into_owned())
|
||||
})
|
||||
.unwrap_or_else(|| String::from("ErrFileName"));
|
||||
let label = if viewtype == Viewtype::Audio {
|
||||
stock_str::audio(context).await
|
||||
impl Message {
|
||||
/// Returns a summary text.
|
||||
async fn get_summary_text(&self, context: &Context) -> String {
|
||||
let mut append_text = true;
|
||||
let prefix = match self.viewtype {
|
||||
Viewtype::Image => stock_str::image(context).await,
|
||||
Viewtype::Gif => stock_str::gif(context).await,
|
||||
Viewtype::Sticker => stock_str::sticker(context).await,
|
||||
Viewtype::Video => stock_str::video(context).await,
|
||||
Viewtype::Voice => stock_str::voice_message(context).await,
|
||||
Viewtype::Audio | Viewtype::File => {
|
||||
if self.param.get_cmd() == SystemMessage::AutocryptSetupMessage {
|
||||
append_text = false;
|
||||
stock_str::ac_setup_msg_subject(context).await
|
||||
} else {
|
||||
stock_str::file(context).await
|
||||
};
|
||||
format!("{} – {}", label, file_name)
|
||||
let file_name: String = self
|
||||
.param
|
||||
.get_path(Param::File, context)
|
||||
.unwrap_or(None)
|
||||
.and_then(|path| {
|
||||
path.file_name()
|
||||
.map(|fname| fname.to_string_lossy().into_owned())
|
||||
})
|
||||
.unwrap_or_else(|| String::from("ErrFileName"));
|
||||
let label = if self.viewtype == Viewtype::Audio {
|
||||
stock_str::audio(context).await
|
||||
} else {
|
||||
stock_str::file(context).await
|
||||
};
|
||||
format!("{} – {}", label, file_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
Viewtype::VideochatInvitation => {
|
||||
append_text = false;
|
||||
stock_str::videochat_invitation(context).await
|
||||
}
|
||||
_ => {
|
||||
if param.get_cmd() != SystemMessage::LocationOnly {
|
||||
"".to_string()
|
||||
} else {
|
||||
Viewtype::VideochatInvitation => {
|
||||
append_text = false;
|
||||
stock_str::location(context).await
|
||||
stock_str::videochat_invitation(context).await
|
||||
}
|
||||
_ => {
|
||||
if self.param.get_cmd() != SystemMessage::LocationOnly {
|
||||
"".to_string()
|
||||
} else {
|
||||
append_text = false;
|
||||
stock_str::location(context).await
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if !append_text {
|
||||
return prefix;
|
||||
}
|
||||
};
|
||||
|
||||
if !append_text {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
let summary_content = if let Some(text) = text {
|
||||
if text.as_ref().is_empty() {
|
||||
prefix
|
||||
} else if prefix.is_empty() {
|
||||
dc_truncate(text.as_ref(), approx_characters).to_string()
|
||||
let summary_content = if let Some(text) = &self.text {
|
||||
if text.is_empty() {
|
||||
prefix
|
||||
} else if prefix.is_empty() {
|
||||
text.to_string()
|
||||
} else {
|
||||
format!("{} – {}", prefix, text)
|
||||
}
|
||||
} else {
|
||||
let tmp = format!("{} – {}", prefix, text.as_ref());
|
||||
dc_truncate(&tmp, approx_characters).to_string()
|
||||
}
|
||||
} else {
|
||||
prefix
|
||||
};
|
||||
prefix
|
||||
};
|
||||
|
||||
let summary = if was_forwarded {
|
||||
let tmp = format!(
|
||||
"{}: {}",
|
||||
stock_str::forwarded(context).await,
|
||||
let summary = if self.is_forwarded() {
|
||||
format!(
|
||||
"{}: {}",
|
||||
stock_str::forwarded(context).await,
|
||||
summary_content
|
||||
)
|
||||
} else {
|
||||
summary_content
|
||||
);
|
||||
dc_truncate(&tmp, approx_characters).to_string()
|
||||
} else {
|
||||
summary_content
|
||||
};
|
||||
};
|
||||
|
||||
summary.split_whitespace().join(" ")
|
||||
summary.split_whitespace().join(" ")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -196,7 +184,7 @@ mod tests {
|
||||
use crate::test_utils as test;
|
||||
|
||||
#[async_std::test]
|
||||
async fn test_get_summarytext_by_raw() {
|
||||
async fn test_get_summary_text() {
|
||||
let d = test::TestContext::new().await;
|
||||
let ctx = &d.ctx;
|
||||
|
||||
@@ -204,190 +192,125 @@ mod tests {
|
||||
let empty_text = Some("".to_string());
|
||||
let no_text: Option<String> = None;
|
||||
|
||||
let mut some_file = Params::new();
|
||||
some_file.set(Param::File, "foo.bar");
|
||||
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.set_text(some_text.clone());
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Text,
|
||||
some_text.as_ref(),
|
||||
false,
|
||||
&Params::new(),
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"bla bla" // for simple text, the type is not added to the summary
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Image);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Image,
|
||||
no_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Image" // file names are not added for images
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Video);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Video,
|
||||
no_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Video" // file names are not added for videos
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Gif);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(Viewtype::Gif, no_text.as_ref(), false, &some_file, 50, ctx,)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"GIF" // file names are not added for GIFs
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Sticker);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Sticker,
|
||||
no_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx,
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Sticker" // file names are not added for stickers
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Voice);
|
||||
msg.set_text(empty_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Voice,
|
||||
empty_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx,
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Voice message" // file names are not added for voice messages, empty text is skipped
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Voice);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Voice,
|
||||
no_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Voice message" // file names are not added for voice messages
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Voice);
|
||||
msg.set_text(some_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Voice,
|
||||
some_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Voice message \u{2013} bla bla" // `\u{2013}` explicitly checks for "EN DASH"
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Audio);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Audio,
|
||||
no_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Audio \u{2013} foo.bar" // file name is added for audio
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Audio);
|
||||
msg.set_text(empty_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Audio,
|
||||
empty_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx,
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Audio \u{2013} foo.bar" // file name is added for audio, empty text is not added
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::Audio);
|
||||
msg.set_text(some_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Audio,
|
||||
some_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Audio \u{2013} foo.bar \u{2013} bla bla" // file name and text added for audio
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::File);
|
||||
msg.set_text(some_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::File,
|
||||
some_text.as_ref(),
|
||||
false,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"File \u{2013} foo.bar \u{2013} bla bla" // file name is added for files
|
||||
);
|
||||
|
||||
// Forwarded
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.set_text(some_text.clone());
|
||||
msg.param.set_int(Param::Forwarded, 1);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::Text,
|
||||
some_text.as_ref(),
|
||||
true,
|
||||
&Params::new(),
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Forwarded: bla bla" // for simple text, the type is not added to the summary
|
||||
);
|
||||
|
||||
let mut msg = Message::new(Viewtype::File);
|
||||
msg.set_text(some_text.clone());
|
||||
msg.set_file("foo.bar", None);
|
||||
msg.param.set_int(Param::Forwarded, 1);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(
|
||||
Viewtype::File,
|
||||
some_text.as_ref(),
|
||||
true,
|
||||
&some_file,
|
||||
50,
|
||||
ctx
|
||||
)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Forwarded: File \u{2013} foo.bar \u{2013} bla bla"
|
||||
);
|
||||
|
||||
let mut asm_file = Params::new();
|
||||
asm_file.set(Param::File, "foo.bar");
|
||||
asm_file.set_cmd(SystemMessage::AutocryptSetupMessage);
|
||||
let mut msg = Message::new(Viewtype::File);
|
||||
msg.set_text(no_text.clone());
|
||||
msg.param.set(Param::File, "foo.bar");
|
||||
msg.param.set_cmd(SystemMessage::AutocryptSetupMessage);
|
||||
assert_eq!(
|
||||
get_summarytext_by_raw(Viewtype::File, no_text.as_ref(), false, &asm_file, 50, ctx)
|
||||
.await,
|
||||
msg.get_summary_text(ctx).await,
|
||||
"Autocrypt Setup Message" // file name is not added for autocrypt setup messages
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user