mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 13:32:11 +03:00
Compare commits
1 Commits
fix_secure
...
fix_progre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d13427cc84 |
@@ -1,4 +1,5 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
default:
|
||||
docker:
|
||||
@@ -12,7 +13,7 @@ restore-workspace: &restore-workspace
|
||||
restore-cache: &restore-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- repo-source-{{ .Branch }}-{{ .Revision }}
|
||||
|
||||
commands:
|
||||
@@ -52,11 +53,10 @@ jobs:
|
||||
command: cargo generate-lockfile
|
||||
- restore_cache:
|
||||
keys:
|
||||
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- run: rustup install $(cat rust-toolchain)
|
||||
- run: rustup default $(cat rust-toolchain)
|
||||
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
|
||||
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
|
||||
- run: cargo update
|
||||
- run: cargo fetch
|
||||
- run: rustc +stable --version
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
paths:
|
||||
- crate
|
||||
- save_cache:
|
||||
key: cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
key: cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
paths:
|
||||
- "~/.cargo"
|
||||
- "~/.rustup"
|
||||
@@ -152,24 +152,14 @@ jobs:
|
||||
- wheelhouse
|
||||
|
||||
upload_docs_wheels:
|
||||
machine: true
|
||||
machine: True
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: workspace
|
||||
- run: pyenv global 3.5.2
|
||||
- run: ls -laR workspace
|
||||
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
|
||||
|
||||
clippy:
|
||||
executor: default
|
||||
steps:
|
||||
- *restore-workspace
|
||||
- *restore-cache
|
||||
- run:
|
||||
name: Run cargo clippy
|
||||
command: cargo clippy --all
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2.1
|
||||
@@ -185,10 +175,6 @@ workflows:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
- clippy:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
# Linux Desktop 64bit
|
||||
- test_x86_64-unknown-linux-gnu:
|
||||
requires:
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -18,7 +18,3 @@ __pycache__
|
||||
python/src/deltachat/capi*.so
|
||||
|
||||
python/liveconfig*
|
||||
|
||||
# ignore doxgen generated files
|
||||
deltachat-ffi/html
|
||||
deltachat-ffi/xml
|
||||
|
||||
687
Cargo.lock
generated
687
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-alpha.4"
|
||||
version = "1.0.0-alpha.3"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL"
|
||||
@@ -16,7 +16,6 @@ pgp = { version = "0.2", default-features = false }
|
||||
hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
|
||||
smallvec = "0.6.9"
|
||||
reqwest = "0.9.15"
|
||||
num-derive = "0.2.5"
|
||||
@@ -24,7 +23,7 @@ num-traits = "0.2.6"
|
||||
native-tls = "0.2.3"
|
||||
lettre = "0.9.0"
|
||||
imap = "1.0.1"
|
||||
mmime = { git = "https://github.com/dignifiedquire/mmime", rev = "bccd2c2" }
|
||||
mmime = "0.1.0"
|
||||
base64 = "0.10"
|
||||
charset = "0.1"
|
||||
percent-encoding = "2.0"
|
||||
@@ -38,6 +37,7 @@ rustyline = "4.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||
addr = "0.2.0"
|
||||
r2d2_sqlite = "0.12.0"
|
||||
r2d2 = "0.8.5"
|
||||
strum = "0.15.0"
|
||||
@@ -46,10 +46,6 @@ thread-local-object = "0.1.0"
|
||||
backtrace = "0.3.33"
|
||||
byteorder = "1.3.1"
|
||||
itertools = "0.8.0"
|
||||
image-meta = "0.1.0"
|
||||
quick-xml = "0.15.0"
|
||||
escaper = "0.1.0"
|
||||
bitflags = "1.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
[dependencies.std]
|
||||
features = ["panic-unwind"]
|
||||
|
||||
# if using `cargo test`
|
||||
[dependencies.test]
|
||||
stage = 1
|
||||
@@ -34,7 +34,7 @@ echo -----------------------
|
||||
# Bundle external shared libraries into the wheels
|
||||
pushd $WHEELHOUSEDIR
|
||||
|
||||
pip3 install devpi-client
|
||||
pip install devpi-client
|
||||
devpi use https://m.devpi.net
|
||||
devpi login dc --password $DEVPI_LOGIN
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ if [ -n "$TESTS" ]; then
|
||||
# run tox
|
||||
# XXX we don't run liveconfig tests because they hang sometimes
|
||||
# see https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||
# unset DCC_PY_LIVECONFIG
|
||||
unset DCC_PY_LIVECONFIG
|
||||
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py35,py36,py37,auditwheels
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-alpha.4"
|
||||
version = "1.0.0-alpha.3"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
@@ -25,3 +25,4 @@ default = ["vendored", "nightly", "ringbuf"]
|
||||
vendored = ["deltachat/vendored"]
|
||||
nightly = ["deltachat/nightly"]
|
||||
ringbuf = ["deltachat/ringbuf"]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,7 +0,0 @@
|
||||
|
||||
/* the code snippet frame, defaults to white which tends to get badly readable in combination with explaining text around */
|
||||
div.fragment {
|
||||
background-color: #e0e0e0;
|
||||
border: 0;
|
||||
padding: 1em;
|
||||
}
|
||||
@@ -1,10 +1 @@
|
||||
# Delta Chat C Interface
|
||||
|
||||
## Documentation
|
||||
|
||||
To generate the C Interface documentation,
|
||||
call doxygen in the `deltachat-ffi` directory
|
||||
and browse the `html` subdirectory.
|
||||
|
||||
If thinks work,
|
||||
the documentation is also available online at <https://c.delta.chat>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@ pub fn to_sql_derive(input: TokenStream) -> TokenStream {
|
||||
let num = *self as i64;
|
||||
let value = rusqlite::types::Value::Integer(num);
|
||||
let output = rusqlite::types::ToSqlOutput::Owned(value);
|
||||
|
||||
std::result::Result::Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_job::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_lot::*;
|
||||
use deltachat::dc_msg::*;
|
||||
use deltachat::dc_qr::*;
|
||||
use deltachat::dc_receive_imf::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::error::Error;
|
||||
use deltachat::job::*;
|
||||
use deltachat::location;
|
||||
use deltachat::lot::LotState;
|
||||
use deltachat::message::*;
|
||||
use deltachat::peerstate::*;
|
||||
use deltachat::qr::*;
|
||||
use deltachat::sql;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
@@ -95,7 +94,7 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) -> libc::c_int {
|
||||
/* mainly for testing, may be called by dc_import_spec() */
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut data: *mut libc::c_char = ptr::null_mut();
|
||||
let mut data: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut data_bytes: size_t = 0;
|
||||
if !(dc_read_file(
|
||||
context,
|
||||
@@ -129,7 +128,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
let ok_to_continue;
|
||||
let mut success: libc::c_int = 0;
|
||||
let real_spec: *mut libc::c_char;
|
||||
let mut suffix: *mut libc::c_char = ptr::null_mut();
|
||||
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut read_cnt: libc::c_int = 0;
|
||||
|
||||
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
||||
@@ -152,7 +151,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
}
|
||||
if ok_to_continue {
|
||||
let ok_to_continue2;
|
||||
suffix = dc_get_filesuffix_lc(as_str(real_spec));
|
||||
suffix = dc_get_filesuffix_lc(real_spec);
|
||||
if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
|
||||
{
|
||||
if 0 != dc_poke_eml_file(context, real_spec) {
|
||||
@@ -212,16 +211,16 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
success
|
||||
}
|
||||
|
||||
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t) {
|
||||
let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
|
||||
let contact_name = contact.get_name();
|
||||
let contact_id = contact.get_id();
|
||||
|
||||
let statestr = match dc_msg_get_state(msg) {
|
||||
MessageState::OutPending => " o",
|
||||
MessageState::OutDelivered => " √",
|
||||
MessageState::OutMdnRcvd => " √√",
|
||||
MessageState::OutFailed => " !!",
|
||||
DC_STATE_OUT_PENDING => " o",
|
||||
DC_STATE_OUT_DELIVERED => " √",
|
||||
DC_STATE_OUT_MDN_RCVD => " √√",
|
||||
DC_STATE_OUT_FAILED => " !!",
|
||||
_ => "",
|
||||
};
|
||||
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
|
||||
@@ -244,9 +243,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
if dc_msg_is_starred(msg) { "★" } else { "" },
|
||||
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
|
||||
""
|
||||
} else if dc_msg_get_state(msg) == MessageState::InSeen {
|
||||
} else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN {
|
||||
"[SEEN]"
|
||||
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
|
||||
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
|
||||
"[NOTICED]"
|
||||
} else {
|
||||
"[FRESH]"
|
||||
@@ -262,9 +261,11 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
free(msgtext as *mut libc::c_void);
|
||||
}
|
||||
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
|
||||
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
||||
let cnt = dc_array_get_cnt(msglist) as usize;
|
||||
let mut lines_out = 0;
|
||||
for &msg_id in msglist {
|
||||
for i in 0..cnt {
|
||||
let msg_id = dc_array_get_id(msglist, i as size_t);
|
||||
if msg_id == 9 as libc::c_uint {
|
||||
info!(
|
||||
context,
|
||||
@@ -273,7 +274,7 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
);
|
||||
|
||||
lines_out += 1
|
||||
} else if msg_id > 0 {
|
||||
} else if msg_id > 0 as libc::c_uint {
|
||||
if lines_out == 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
@@ -281,8 +282,9 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
);
|
||||
lines_out += 1
|
||||
}
|
||||
let msg = dc_get_msg(context, msg_id)?;
|
||||
log_msg(context, "Msg", &msg);
|
||||
let msg = dc_get_msg(context, msg_id);
|
||||
log_msg(context, "Msg", msg);
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
}
|
||||
if lines_out > 0 {
|
||||
@@ -291,7 +293,6 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
0, "--------------------------------------------------------------------------------"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
@@ -348,16 +349,22 @@ pub unsafe fn dc_cmdline_skip_auth() {
|
||||
S_IS_AUTH = 1;
|
||||
}
|
||||
|
||||
fn chat_prefix(chat: &Chat) -> &'static str {
|
||||
chat.typ.into()
|
||||
unsafe fn chat_prefix(chat: *const Chat) -> &'static str {
|
||||
if (*chat).type_0 == 120 {
|
||||
"Group"
|
||||
} else if (*chat).type_0 == 130 {
|
||||
"VerifiedGroup"
|
||||
} else {
|
||||
"Single"
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
|
||||
let mut sel_chat = if chat_id > 0 {
|
||||
Chat::load_from_db(context, chat_id).ok()
|
||||
dc_get_chat(context, chat_id)
|
||||
} else {
|
||||
None
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
|
||||
let mut args = line.splitn(3, ' ');
|
||||
@@ -498,9 +505,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"get-setupcodebegin" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let msg_id: u32 = arg1.parse()?;
|
||||
let msg = dc_get_msg(context, msg_id)?;
|
||||
if dc_msg_is_setupmessage(&msg) {
|
||||
let setupcodebegin = dc_msg_get_setupcodebegin(&msg);
|
||||
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
|
||||
if dc_msg_is_setupmessage(msg) {
|
||||
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
|
||||
println!(
|
||||
"The setup code for setup message Msg#{} starts with: {}",
|
||||
msg_id,
|
||||
@@ -510,6 +517,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
} else {
|
||||
bail!("Msg#{} is no setup message.", msg_id,);
|
||||
}
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
"continue-key-transfer" => {
|
||||
ensure!(
|
||||
@@ -527,17 +535,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
"export-backup" => {
|
||||
dc_imex(context, 11, context.get_blobdir(), ptr::null());
|
||||
dc_imex(context, 11, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
}
|
||||
"import-backup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
|
||||
dc_imex(context, 12, arg1_c, ptr::null());
|
||||
dc_imex(context, 12, arg1_c, 0 as *const libc::c_char);
|
||||
}
|
||||
"export-keys" => {
|
||||
dc_imex(context, 1, context.get_blobdir(), ptr::null());
|
||||
dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
}
|
||||
"import-keys" => {
|
||||
dc_imex(context, 2, context.get_blobdir(), ptr::null());
|
||||
dc_imex(context, 2, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
}
|
||||
"export-setup" => {
|
||||
let setup_code = dc_create_setup_code(context);
|
||||
@@ -582,7 +590,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
println!("{}", to_string(dc_get_info(context)));
|
||||
}
|
||||
"maybenetwork" => {
|
||||
maybe_network(context);
|
||||
dc_maybe_network(context);
|
||||
}
|
||||
"housekeeping" => {
|
||||
sql::housekeeping(context);
|
||||
@@ -604,137 +612,170 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
|
||||
for i in (0..cnt).rev() {
|
||||
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
||||
let temp_subtitle = chat.get_subtitle();
|
||||
let temp_name = chat.get_name();
|
||||
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
|
||||
let temp_subtitle = dc_chat_get_subtitle(chat);
|
||||
let temp_name = dc_chat_get_name(chat);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}] [{} fresh]",
|
||||
chat_prefix(&chat),
|
||||
chat.get_id(),
|
||||
temp_name,
|
||||
temp_subtitle,
|
||||
chat::get_fresh_msg_cnt(context, chat.get_id()),
|
||||
chat_prefix(chat),
|
||||
dc_chat_get_id(chat) as libc::c_int,
|
||||
as_str(temp_name),
|
||||
as_str(temp_subtitle),
|
||||
dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int,
|
||||
);
|
||||
let lot = chatlist.get_summary(i, Some(&chat));
|
||||
let statestr = if chat.is_archived() {
|
||||
free(temp_subtitle as *mut libc::c_void);
|
||||
free(temp_name as *mut libc::c_void);
|
||||
let lot = chatlist.get_summary(i, chat);
|
||||
let statestr = if 0 != dc_chat_get_archived(chat) {
|
||||
" [Archived]"
|
||||
} else {
|
||||
match lot.get_state() {
|
||||
LotState::MsgOutPending => " o",
|
||||
LotState::MsgOutDelivered => " √",
|
||||
LotState::MsgOutMdnRcvd => " √√",
|
||||
LotState::MsgOutFailed => " !!",
|
||||
match dc_lot_get_state(lot) {
|
||||
20 => " o",
|
||||
26 => " √",
|
||||
28 => " √√",
|
||||
24 => " !!",
|
||||
_ => "",
|
||||
}
|
||||
};
|
||||
let timestr = dc_timestamp_to_str(lot.get_timestamp());
|
||||
let text1 = lot.get_text1();
|
||||
let text2 = lot.get_text2();
|
||||
let timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot));
|
||||
let text1 = dc_lot_get_text1(lot);
|
||||
let text2 = dc_lot_get_text2(lot);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}{}{}{} [{}]{}",
|
||||
text1.unwrap_or(""),
|
||||
if text1.is_some() { ": " } else { "" },
|
||||
text2.unwrap_or(""),
|
||||
to_string(text1),
|
||||
if !text1.is_null() { ": " } else { "" },
|
||||
to_string(text2),
|
||||
statestr,
|
||||
×tr,
|
||||
if chat.is_sending_locations() {
|
||||
if 0 != dc_chat_is_sending_locations(chat) {
|
||||
"📍"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
);
|
||||
free(text1 as *mut libc::c_void);
|
||||
free(text2 as *mut libc::c_void);
|
||||
dc_lot_unref(lot);
|
||||
dc_chat_unref(chat);
|
||||
info!(
|
||||
context, 0,
|
||||
"================================================================================"
|
||||
);
|
||||
}
|
||||
}
|
||||
if location::is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
if dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
info!(context, 0, "Location streaming enabled.");
|
||||
}
|
||||
println!("{} chats", cnt);
|
||||
}
|
||||
"chat" => {
|
||||
if sel_chat.is_none() && arg1.is_empty() {
|
||||
if sel_chat.is_null() && arg1.is_empty() {
|
||||
bail!("Argument [chat-id] is missing.");
|
||||
}
|
||||
if !sel_chat.is_null() && !arg1.is_empty() {
|
||||
dc_chat_unref(sel_chat);
|
||||
}
|
||||
if !arg1.is_empty() {
|
||||
let chat_id = arg1.parse()?;
|
||||
println!("Selecting chat #{}", chat_id);
|
||||
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
|
||||
sel_chat = dc_get_chat(context, chat_id);
|
||||
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
||||
}
|
||||
|
||||
ensure!(sel_chat.is_some(), "Failed to select chat");
|
||||
let sel_chat = sel_chat.as_ref().unwrap();
|
||||
ensure!(!sel_chat.is_null(), "Failed to select chat");
|
||||
|
||||
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
|
||||
let temp2 = sel_chat.get_subtitle();
|
||||
let temp_name = sel_chat.get_name();
|
||||
let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0);
|
||||
let temp2 = dc_chat_get_subtitle(sel_chat);
|
||||
let temp_name = dc_chat_get_name(sel_chat);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}]{}",
|
||||
chat_prefix(sel_chat),
|
||||
sel_chat.get_id(),
|
||||
temp_name,
|
||||
temp2,
|
||||
if sel_chat.is_sending_locations() {
|
||||
dc_chat_get_id(sel_chat),
|
||||
as_str(temp_name),
|
||||
as_str(temp2),
|
||||
if 0 != dc_chat_is_sending_locations(sel_chat) {
|
||||
"📍"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
);
|
||||
log_msglist(context, &msglist)?;
|
||||
if let Ok(draft) = chat::get_draft(context, sel_chat.get_id()) {
|
||||
log_msg(context, "Draft", &draft);
|
||||
free(temp_name as *mut libc::c_void);
|
||||
free(temp2 as *mut libc::c_void);
|
||||
if !msglist.is_null() {
|
||||
log_msglist(context, msglist);
|
||||
dc_array_unref(msglist);
|
||||
}
|
||||
let draft = dc_get_draft(context, dc_chat_get_id(sel_chat));
|
||||
if !draft.is_null() {
|
||||
log_msg(context, "Draft", draft);
|
||||
dc_msg_unref(draft);
|
||||
}
|
||||
|
||||
println!(
|
||||
"{} messages.",
|
||||
chat::get_msg_cnt(context, sel_chat.get_id())
|
||||
dc_get_msg_cnt(context, dc_chat_get_id(sel_chat))
|
||||
);
|
||||
chat::marknoticed_chat(context, sel_chat.get_id())?;
|
||||
dc_marknoticed_chat(context, dc_chat_get_id(sel_chat));
|
||||
}
|
||||
"createchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
let contact_id: libc::c_int = arg1.parse()?;
|
||||
let chat_id = chat::create_by_contact_id(context, contact_id as uint32_t)?;
|
||||
|
||||
println!("Single#{} created successfully.", chat_id,);
|
||||
let chat_id: libc::c_int =
|
||||
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
|
||||
if chat_id != 0 {
|
||||
println!("Single#{} created successfully.", chat_id,);
|
||||
} else {
|
||||
bail!("Failed to create chat");
|
||||
}
|
||||
}
|
||||
"createchatbymsg" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
|
||||
let msg_id: u32 = arg1.parse()?;
|
||||
let chat_id = chat::create_by_msg_id(context, msg_id)?;
|
||||
let chat = Chat::load_from_db(context, chat_id)?;
|
||||
|
||||
println!("{}#{} created successfully.", chat_prefix(&chat), chat_id,);
|
||||
let msg_id_0: libc::c_int = arg1.parse()?;
|
||||
let chat_id_0: libc::c_int =
|
||||
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
|
||||
if chat_id_0 != 0 {
|
||||
let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t);
|
||||
println!(
|
||||
"{}#{} created successfully.",
|
||||
chat_prefix(chat_0),
|
||||
chat_id_0,
|
||||
);
|
||||
dc_chat_unref(chat_0);
|
||||
} else {
|
||||
bail!("");
|
||||
}
|
||||
}
|
||||
"creategroup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1)?;
|
||||
|
||||
println!("Group#{} created successfully.", chat_id);
|
||||
let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int;
|
||||
if chat_id_1 != 0 {
|
||||
println!("Group#{} created successfully.", chat_id_1,);
|
||||
} else {
|
||||
bail!("Failed to create group");
|
||||
}
|
||||
}
|
||||
"createverified" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1)?;
|
||||
|
||||
println!("VerifiedGroup#{} created successfully.", chat_id);
|
||||
let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int;
|
||||
if chat_id_2 != 0 {
|
||||
println!("VerifiedGroup#{} created successfully.", chat_id_2,);
|
||||
} else {
|
||||
bail!("Failed to create verified group");
|
||||
}
|
||||
}
|
||||
"addmember" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected");
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
|
||||
let contact_id_0: libc::c_int = arg1.parse()?;
|
||||
if 0 != chat::add_contact_to_chat(
|
||||
if 0 != dc_add_contact_to_chat(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
dc_chat_get_id(sel_chat),
|
||||
contact_id_0 as uint32_t,
|
||||
) {
|
||||
println!("Contact added to chat.");
|
||||
@@ -743,92 +784,99 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
"removemember" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
let contact_id_1: libc::c_int = arg1.parse()?;
|
||||
chat::remove_contact_from_chat(
|
||||
if 0 != dc_remove_contact_from_chat(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
dc_chat_get_id(sel_chat),
|
||||
contact_id_1 as uint32_t,
|
||||
)?;
|
||||
|
||||
println!("Contact added to chat.");
|
||||
) {
|
||||
println!("Contact added to chat.");
|
||||
} else {
|
||||
bail!("Cannot remove member from chat.");
|
||||
}
|
||||
}
|
||||
"groupname" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
|
||||
|
||||
println!("Chat name set");
|
||||
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) {
|
||||
println!("Chat name set");
|
||||
} else {
|
||||
bail!("Failed to set chat name");
|
||||
}
|
||||
}
|
||||
"groupimage" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <image> missing.");
|
||||
|
||||
chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
|
||||
|
||||
println!("Chat image set");
|
||||
if 0 != dc_set_chat_profile_image(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
if !arg1.is_empty() {
|
||||
arg1_c
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
},
|
||||
) {
|
||||
println!("Chat image set");
|
||||
} else {
|
||||
bail!("Failed to set chat image");
|
||||
}
|
||||
}
|
||||
"chatinfo" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
|
||||
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
|
||||
let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat));
|
||||
info!(context, 0, "Memberlist:");
|
||||
|
||||
log_contactlist(context, &contacts);
|
||||
println!(
|
||||
"{} contacts\nLocation streaming: {}",
|
||||
contacts.len(),
|
||||
location::is_sending_locations_to_chat(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id()
|
||||
),
|
||||
dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)),
|
||||
);
|
||||
}
|
||||
"getlocations" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let contact_id = arg1.parse().unwrap_or_default();
|
||||
let locations = location::get_range(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
let default_marker = "-".to_string();
|
||||
for location in &locations {
|
||||
let marker = location.marker.as_ref().unwrap_or(&default_marker);
|
||||
let loc = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0);
|
||||
let mut j = 0;
|
||||
while j < dc_array_get_cnt(loc) {
|
||||
let timestr_0 = dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t));
|
||||
let marker = dc_array_get_marker(loc, j as size_t);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
|
||||
location.location_id,
|
||||
dc_timestamp_to_str(location.timestamp),
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
location.accuracy,
|
||||
location.chat_id,
|
||||
location.contact_id,
|
||||
location.msg_id,
|
||||
marker
|
||||
dc_array_get_id(loc, j as size_t),
|
||||
×tr_0,
|
||||
dc_array_get_latitude(loc, j as size_t),
|
||||
dc_array_get_longitude(loc, j as size_t),
|
||||
dc_array_get_accuracy(loc, j as size_t),
|
||||
dc_array_get_chat_id(loc, j as size_t),
|
||||
dc_array_get_contact_id(loc, j as size_t),
|
||||
dc_array_get_msg_id(loc, j as size_t),
|
||||
if !marker.is_null() {
|
||||
as_str(marker)
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
);
|
||||
free(marker as *mut libc::c_void);
|
||||
j += 1
|
||||
}
|
||||
if locations.is_empty() {
|
||||
if dc_array_get_cnt(loc) == 0 {
|
||||
info!(context, 0, "No locations.");
|
||||
}
|
||||
dc_array_unref(loc);
|
||||
}
|
||||
"sendlocations" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "No timeout given.");
|
||||
|
||||
let seconds = arg1.parse()?;
|
||||
location::send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds);
|
||||
println!(
|
||||
"Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.",
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
seconds
|
||||
);
|
||||
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
|
||||
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
|
||||
}
|
||||
"setlocation" => {
|
||||
ensure!(
|
||||
@@ -838,7 +886,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let latitude = arg1.parse()?;
|
||||
let longitude = arg2.parse()?;
|
||||
|
||||
let continue_streaming = location::set(context, latitude, longitude, 0.);
|
||||
let continue_streaming = dc_set_location(context, latitude, longitude, 0.);
|
||||
if 0 != continue_streaming {
|
||||
println!("Success, streaming should be continued.");
|
||||
} else {
|
||||
@@ -846,25 +894,33 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
"dellocations" => {
|
||||
location::delete_all(context)?;
|
||||
dc_delete_all_locations(context);
|
||||
}
|
||||
"send" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "No message text given.");
|
||||
|
||||
let msg = format!("{} {}", arg1, arg2);
|
||||
|
||||
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), msg)?;
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
|
||||
println!("Message sent.");
|
||||
} else {
|
||||
bail!("Sending failed.");
|
||||
}
|
||||
}
|
||||
"sendempty" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), "".into())?;
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) {
|
||||
println!("Message sent.");
|
||||
} else {
|
||||
bail!("Sending failed.");
|
||||
}
|
||||
}
|
||||
"sendimage" | "sendfile" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
|
||||
|
||||
let mut msg = dc_msg_new(
|
||||
let msg_0 = dc_msg_new(
|
||||
context,
|
||||
if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
@@ -872,53 +928,56 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
Viewtype::File
|
||||
},
|
||||
);
|
||||
dc_msg_set_file(&mut msg, arg1_c, ptr::null());
|
||||
dc_msg_set_text(&mut msg, arg2_c);
|
||||
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), &mut msg)?;
|
||||
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
|
||||
dc_msg_set_text(msg_0, arg2_c);
|
||||
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
|
||||
dc_msg_unref(msg_0);
|
||||
}
|
||||
"listmsgs" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <query> missing.");
|
||||
|
||||
let chat = if let Some(ref sel_chat) = sel_chat {
|
||||
sel_chat.get_id()
|
||||
let chat = if !sel_chat.is_null() {
|
||||
dc_chat_get_id(sel_chat)
|
||||
} else {
|
||||
0 as libc::c_uint
|
||||
};
|
||||
|
||||
let msglist = dc_search_msgs(context, chat, arg1_c);
|
||||
let msglist_0 = dc_search_msgs(context, chat, arg1_c);
|
||||
|
||||
log_msglist(context, &msglist)?;
|
||||
println!("{} messages.", msglist.len());
|
||||
if !msglist_0.is_null() {
|
||||
log_msglist(context, msglist_0);
|
||||
println!("{} messages.", dc_array_get_cnt(msglist_0));
|
||||
dc_array_unref(msglist_0);
|
||||
}
|
||||
}
|
||||
"draft" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
|
||||
if !arg1.is_empty() {
|
||||
let mut draft = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(&mut draft, arg1_c);
|
||||
chat::set_draft(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
Some(&mut draft),
|
||||
);
|
||||
let draft_0 = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(draft_0, arg1_c);
|
||||
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
|
||||
dc_msg_unref(draft_0);
|
||||
println!("Draft saved.");
|
||||
} else {
|
||||
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), None);
|
||||
dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t);
|
||||
println!("Draft deleted.");
|
||||
}
|
||||
}
|
||||
"listmedia" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
|
||||
let images = chat::get_chat_media(
|
||||
let images = dc_get_chat_media(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
dc_chat_get_id(sel_chat),
|
||||
Viewtype::Image,
|
||||
Viewtype::Gif,
|
||||
Viewtype::Video,
|
||||
);
|
||||
println!("{} images or videos: ", images.len());
|
||||
for (i, data) in images.iter().enumerate() {
|
||||
let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int;
|
||||
println!("{} images or videos: ", icnt);
|
||||
for i in 0..icnt {
|
||||
let data = dc_array_get_id(images, i as size_t);
|
||||
if 0 == i {
|
||||
print!("Msg#{}", data);
|
||||
} else {
|
||||
@@ -926,20 +985,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
print!("\n");
|
||||
dc_array_unref(images);
|
||||
}
|
||||
"archive" | "unarchive" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = arg1.parse()?;
|
||||
chat::archive(
|
||||
context,
|
||||
chat_id,
|
||||
if arg0 == "archive" { true } else { false },
|
||||
)?;
|
||||
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
|
||||
}
|
||||
"delchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = arg1.parse()?;
|
||||
chat::delete(context, chat_id)?;
|
||||
dc_delete_chat(context, chat_id);
|
||||
}
|
||||
"msginfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
@@ -949,9 +1005,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"listfresh" => {
|
||||
let msglist = dc_get_fresh_msgs(context);
|
||||
ensure!(!msglist.is_null(), "Failed to retrieve messages");
|
||||
|
||||
log_msglist(context, &msglist)?;
|
||||
print!("{} fresh messages.", msglist.len());
|
||||
log_msglist(context, msglist);
|
||||
print!("{} fresh messages.", dc_array_get_cnt(msglist));
|
||||
dc_array_unref(msglist);
|
||||
}
|
||||
"forward" => {
|
||||
ensure!(
|
||||
@@ -962,7 +1020,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let mut msg_ids = [0; 1];
|
||||
let chat_id = arg2.parse()?;
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
|
||||
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
|
||||
}
|
||||
"markseen" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
@@ -1032,8 +1090,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
if 0 != i {
|
||||
res += ", ";
|
||||
}
|
||||
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
||||
res += &format!("{}#{}", chat_prefix(&chat), chat.get_id());
|
||||
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
|
||||
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
|
||||
dc_chat_unref(chat);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1045,14 +1104,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"checkqr" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
||||
let res = check_qr(context, arg1);
|
||||
let res = dc_check_qr(context, arg1_c);
|
||||
println!(
|
||||
"state={}, id={}, text1={:?}, text2={:?}",
|
||||
res.get_state(),
|
||||
res.get_id(),
|
||||
res.get_text1(),
|
||||
res.get_text2()
|
||||
"state={}, id={}, text1={}, text2={}",
|
||||
(*res).state as libc::c_int,
|
||||
(*res).id,
|
||||
to_string((*res).text1),
|
||||
to_string((*res).text2)
|
||||
);
|
||||
dc_lot_unref(res);
|
||||
}
|
||||
"event" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
||||
@@ -1066,10 +1126,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"fileinfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <file> missing.");
|
||||
let mut buf = 0 as *mut libc::c_uchar;
|
||||
let mut buf_bytes = 0;
|
||||
let mut w = 0;
|
||||
let mut h = 0;
|
||||
|
||||
if let Some(buf) = dc_read_file_safe(context, &arg1) {
|
||||
let (width, height) = dc_get_filemeta(&buf)?;
|
||||
println!("width={}, height={}", width, height);
|
||||
if 0 != dc_read_file(
|
||||
context,
|
||||
arg1_c,
|
||||
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
|
||||
&mut buf_bytes,
|
||||
) {
|
||||
dc_get_filemeta(buf as *const libc::c_void, buf_bytes, &mut w, &mut h);
|
||||
println!("width={}, height={}", w, h,);
|
||||
free(buf as *mut libc::c_void);
|
||||
} else {
|
||||
bail!("Command failed.");
|
||||
}
|
||||
@@ -1078,6 +1148,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
||||
}
|
||||
|
||||
if !sel_chat.is_null() {
|
||||
dc_chat_unref(sel_chat);
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
free(arg2_c as *mut _);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//!
|
||||
//! Usage: cargo run --example repl --release -- <databasefile>
|
||||
//! All further options can be set using the set-command (type ? for help).
|
||||
#![feature(ptr_cast)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate deltachat;
|
||||
@@ -14,17 +15,16 @@ extern crate lazy_static;
|
||||
extern crate rusqlite;
|
||||
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::ptr;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_job::*;
|
||||
use deltachat::dc_securejoin::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
@@ -173,11 +173,13 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c.clone();
|
||||
let handle_imap = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_imap_jobs(&ctx.read().unwrap());
|
||||
perform_imap_fetch(&ctx.read().unwrap());
|
||||
unsafe {
|
||||
dc_perform_imap_jobs(&ctx.read().unwrap());
|
||||
dc_perform_imap_fetch(&ctx.read().unwrap());
|
||||
}
|
||||
while_running!({
|
||||
let context = ctx.read().unwrap();
|
||||
perform_imap_idle(&context);
|
||||
dc_perform_imap_idle(&context);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -185,9 +187,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c.clone();
|
||||
let handle_mvbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_mvbox_fetch(&ctx.read().unwrap());
|
||||
unsafe { dc_perform_mvbox_fetch(&ctx.read().unwrap()) };
|
||||
while_running!({
|
||||
perform_mvbox_idle(&ctx.read().unwrap());
|
||||
unsafe { dc_perform_mvbox_idle(&ctx.read().unwrap()) };
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -195,9 +197,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c.clone();
|
||||
let handle_sentbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_sentbox_fetch(&ctx.read().unwrap());
|
||||
unsafe { dc_perform_sentbox_fetch(&ctx.read().unwrap()) };
|
||||
while_running!({
|
||||
perform_sentbox_idle(&ctx.read().unwrap());
|
||||
unsafe { dc_perform_sentbox_idle(&ctx.read().unwrap()) };
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -205,9 +207,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c;
|
||||
let handle_smtp = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
unsafe { dc_perform_smtp_jobs(&ctx.read().unwrap()) };
|
||||
while_running!({
|
||||
perform_smtp_idle(&ctx.read().unwrap());
|
||||
unsafe { dc_perform_smtp_idle(&ctx.read().unwrap()) };
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -225,10 +227,12 @@ fn stop_threads(context: &Context) {
|
||||
println!("Stopping threads");
|
||||
IS_RUNNING.store(false, Ordering::Relaxed);
|
||||
|
||||
interrupt_imap_idle(context);
|
||||
interrupt_mvbox_idle(context);
|
||||
interrupt_sentbox_idle(context);
|
||||
interrupt_smtp_idle(context);
|
||||
unsafe {
|
||||
dc_interrupt_imap_idle(context);
|
||||
dc_interrupt_mvbox_idle(context);
|
||||
dc_interrupt_sentbox_idle(context);
|
||||
dc_interrupt_smtp_idle(context);
|
||||
}
|
||||
|
||||
handle.handle_imap.take().unwrap().join().unwrap();
|
||||
handle.handle_mvbox.take().unwrap().join().unwrap();
|
||||
@@ -385,7 +389,11 @@ impl Highlighter for DcHelper {
|
||||
impl Helper for DcHelper {}
|
||||
|
||||
fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
let mut context = dc_context_new(Some(receive_event), ptr::null_mut(), Some("CLI".into()));
|
||||
let mut context = dc_context_new(
|
||||
Some(receive_event),
|
||||
0 as *mut libc::c_void,
|
||||
Some("CLI".into()),
|
||||
);
|
||||
|
||||
unsafe { dc_cmdline_skip_auth() };
|
||||
|
||||
@@ -480,19 +488,19 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("smtp-jobs are already running in a thread.",);
|
||||
} else {
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
dc_perform_smtp_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"imap-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("imap-jobs are already running in a thread.");
|
||||
} else {
|
||||
perform_imap_jobs(&ctx.read().unwrap());
|
||||
dc_perform_imap_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"configure" => {
|
||||
start_threads(ctx.clone());
|
||||
configure(&ctx.read().unwrap());
|
||||
dc_configure(&ctx.read().unwrap());
|
||||
}
|
||||
"oauth2" => {
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||
|
||||
@@ -5,16 +5,18 @@ use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use deltachat::chat;
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::constants::Event;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::job::{
|
||||
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_job::{
|
||||
dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle,
|
||||
dc_perform_smtp_jobs,
|
||||
};
|
||||
use deltachat::dc_lot::*;
|
||||
|
||||
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
|
||||
println!("[{:?}]", event);
|
||||
@@ -51,12 +53,12 @@ fn main() {
|
||||
let r1 = running.clone();
|
||||
let t1 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
perform_imap_jobs(&ctx1);
|
||||
dc_perform_imap_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
perform_imap_fetch(&ctx1);
|
||||
dc_perform_imap_fetch(&ctx1);
|
||||
|
||||
if *r1.read().unwrap() {
|
||||
perform_imap_idle(&ctx1);
|
||||
dc_perform_imap_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,9 +68,9 @@ fn main() {
|
||||
let r1 = running.clone();
|
||||
let t2 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
perform_smtp_jobs(&ctx1);
|
||||
dc_perform_smtp_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
perform_smtp_idle(&ctx1);
|
||||
dc_perform_smtp_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -87,24 +89,36 @@ fn main() {
|
||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||
configure(&ctx);
|
||||
dc_configure(&ctx);
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
println!("sending a message");
|
||||
let contact_id =
|
||||
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
|
||||
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
|
||||
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
|
||||
let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id);
|
||||
dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into());
|
||||
|
||||
println!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
let summary = chats.get_summary(0, std::ptr::null_mut());
|
||||
let text1 = dc_lot_get_text1(summary);
|
||||
let text2 = dc_lot_get_text2(summary);
|
||||
|
||||
let text1_s = if !text1.is_null() {
|
||||
Some(CStr::from_ptr(text1))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let text2_s = if !text2.is_null() {
|
||||
Some(CStr::from_ptr(text2))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
|
||||
dc_lot_unref(summary);
|
||||
}
|
||||
|
||||
thread::sleep(duration);
|
||||
@@ -122,8 +136,8 @@ fn main() {
|
||||
println!("stopping threads");
|
||||
|
||||
*running.clone().write().unwrap() = false;
|
||||
deltachat::job::interrupt_imap_idle(&ctx);
|
||||
deltachat::job::interrupt_smtp_idle(&ctx);
|
||||
deltachat::dc_job::dc_interrupt_imap_idle(&ctx);
|
||||
deltachat::dc_job::dc_interrupt_smtp_idle(&ctx);
|
||||
|
||||
println!("joining");
|
||||
t1.join().unwrap();
|
||||
|
||||
@@ -37,3 +37,16 @@ Message
|
||||
.. autoclass:: deltachat.message.Message
|
||||
:members:
|
||||
|
||||
MessageType
|
||||
------------
|
||||
|
||||
.. autoclass:: deltachat.message.MessageType
|
||||
:members:
|
||||
|
||||
MessageState
|
||||
------------
|
||||
|
||||
.. autoclass:: deltachat.message.MessageState
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ Playing around on the commandline
|
||||
----------------------------------
|
||||
|
||||
Once you have :doc:`installed deltachat bindings <install>`
|
||||
you can start playing from the python interpreter commandline.
|
||||
you can start playing from the python interpreter commandline::
|
||||
|
||||
For example you can type ``python`` and then::
|
||||
|
||||
# instantiate and configure deltachat account
|
||||
@@ -22,7 +23,7 @@ For example you can type ``python`` and then::
|
||||
# create a contact and send a message
|
||||
contact = ac.create_contact("someother@email.address")
|
||||
chat = ac.create_chat_by_contact(contact)
|
||||
chat.send_text("hi from the python interpreter command line")
|
||||
chat.send_text_message("hi from the python interpreter command line")
|
||||
|
||||
Checkout our :doc:`api` for the various high-level things you can do
|
||||
to send/receive messages, create contacts and chats.
|
||||
|
||||
@@ -34,14 +34,13 @@ def py_dc_callback(ctx, evt, data1, data2):
|
||||
if data1 and event_sig_types & 1:
|
||||
data1 = ffi.string(ffi.cast('char*', data1)).decode("utf8")
|
||||
if data2 and event_sig_types & 2:
|
||||
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
|
||||
try:
|
||||
if isinstance(data2, bytes):
|
||||
data2 = data2.decode("utf8")
|
||||
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
|
||||
except UnicodeDecodeError:
|
||||
# XXX ignoring the decode error is not quite correct but for now
|
||||
# XXX ignoring this error is not quite correct but for now
|
||||
# i don't want to hunt down encoding problems in the c lib
|
||||
pass
|
||||
data2 = ffi.string(ffi.cast('char*', data2))
|
||||
|
||||
try:
|
||||
ret = callback(ctx, evt_name, data1, data2)
|
||||
if ret is None:
|
||||
|
||||
@@ -176,7 +176,7 @@ class Account(object):
|
||||
whose name or e-mail matches query.
|
||||
:param only_verified: if true only return verified contacts.
|
||||
:param with_self: if true the self-contact is also returned.
|
||||
:returns: list of :class:`deltachat.chatting.Contact` objects.
|
||||
:returns: list of :class:`deltachat.message.Message` objects.
|
||||
"""
|
||||
flags = 0
|
||||
query = as_dc_charpointer(query)
|
||||
|
||||
@@ -16,4 +16,4 @@ def iter_array(dc_array_t, constructor):
|
||||
|
||||
|
||||
def from_dc_charpointer(obj):
|
||||
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
|
||||
return ffi.string(obj).decode("utf8")
|
||||
|
||||
@@ -142,7 +142,7 @@ class Message(object):
|
||||
import email.parser
|
||||
mime_headers = lib.dc_get_mime_headers(self._dc_context, self.id)
|
||||
if mime_headers:
|
||||
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
|
||||
s = ffi.string(mime_headers)
|
||||
if isinstance(s, bytes):
|
||||
s = s.decode("ascii")
|
||||
return email.message_from_string(s)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[tox]
|
||||
# make sure to update environment list in travis.yml and appveyor.yml
|
||||
envlist =
|
||||
py27
|
||||
py35
|
||||
lint
|
||||
auditwheels
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2019-08-13
|
||||
nightly-2019-07-10
|
||||
|
||||
1990
src/chat.rs
1990
src/chat.rs
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,11 @@
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Result;
|
||||
use crate::lot::Lot;
|
||||
use crate::message::*;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// An object representing a single chatlist in memory.
|
||||
@@ -195,9 +196,9 @@ impl<'a> Chatlist<'a> {
|
||||
|
||||
if 0 != add_archived_link_item && dc_get_archived_cnt(context) > 0 {
|
||||
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
|
||||
ids.push((DC_CHAT_ID_ALLDONE_HINT, 0));
|
||||
ids.push((DC_CHAT_ID_ALLDONE_HINT as u32, 0));
|
||||
}
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK as u32, 0));
|
||||
}
|
||||
|
||||
Ok(Chatlist { context, ids })
|
||||
@@ -247,63 +248,57 @@ impl<'a> Chatlist<'a> {
|
||||
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
|
||||
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
|
||||
// 0 if not applicable.
|
||||
pub fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
|
||||
pub unsafe fn get_summary(&self, index: usize, mut chat: *mut Chat<'a>) -> *mut dc_lot_t {
|
||||
// The summary is created by the chat, not by the last message.
|
||||
// This is because we may want to display drafts here or stuff as
|
||||
// "is typing".
|
||||
// Also, sth. as "No messages" would not work if the summary comes from a message.
|
||||
|
||||
let mut ret = Lot::new();
|
||||
let mut ret = dc_lot_new();
|
||||
if index >= self.ids.len() {
|
||||
ret.text2 = Some("ErrBadChatlistIndex".to_string());
|
||||
(*ret).text2 = "ErrBadChatlistIndex".strdup();
|
||||
return ret;
|
||||
}
|
||||
|
||||
let chat_loaded: Chat;
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
let lastmsg_id = self.ids[index].1;
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if 0 != lastmsg_id {
|
||||
if let Ok(lastmsg) = dc_msg_load_from_db(self.context, lastmsg_id) {
|
||||
if lastmsg.from_id != 1 as libc::c_uint
|
||||
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
lastcontact = Contact::load_from_db(self.context, lastmsg.from_id).ok();
|
||||
}
|
||||
if chat.is_null() {
|
||||
chat = dc_chat_new(self.context);
|
||||
let chat_to_delete = chat;
|
||||
if !dc_chat_load_from_db(chat, self.ids[index].0) {
|
||||
(*ret).text2 = "ErrCannotReadChat".strdup();
|
||||
dc_chat_unref(chat_to_delete);
|
||||
|
||||
Some(lastmsg)
|
||||
} else {
|
||||
None
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
let lastmsg = if 0 != lastmsg_id {
|
||||
let lastmsg = dc_msg_new_untyped(self.context);
|
||||
dc_msg_load_from_db(lastmsg, self.context, lastmsg_id);
|
||||
|
||||
if (*lastmsg).from_id != 1 as libc::c_uint
|
||||
&& ((*chat).type_0 == DC_CHAT_TYPE_GROUP
|
||||
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP)
|
||||
{
|
||||
lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok();
|
||||
}
|
||||
lastmsg
|
||||
} else {
|
||||
None
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
|
||||
if chat.id == DC_CHAT_ID_ARCHIVED_LINK {
|
||||
ret.text2 = None;
|
||||
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
|
||||
{
|
||||
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
|
||||
if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
|
||||
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
|
||||
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 {
|
||||
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
|
||||
} else {
|
||||
ret.fill(
|
||||
&mut lastmsg.unwrap(),
|
||||
chat,
|
||||
lastcontact.as_ref(),
|
||||
self.context,
|
||||
);
|
||||
dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context);
|
||||
}
|
||||
|
||||
dc_msg_unref(lastmsg);
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
|
||||
|
||||
use crate::constants::DC_VERSION_STR;
|
||||
use crate::context::Context;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::job::*;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// The available configuration keys.
|
||||
@@ -33,7 +33,6 @@ pub enum Config {
|
||||
E2eeEnabled,
|
||||
#[strum(props(default = "1"))]
|
||||
MdnsEnabled,
|
||||
#[strum(props(default = "1"))]
|
||||
InboxWatch,
|
||||
#[strum(props(default = "1"))]
|
||||
SentboxWatch,
|
||||
@@ -49,14 +48,11 @@ pub enum Config {
|
||||
ConfiguredMailUser,
|
||||
ConfiguredMailPw,
|
||||
ConfiguredMailPort,
|
||||
ConfiguredMailSecurity,
|
||||
ConfiguredSendServer,
|
||||
ConfiguredSendUser,
|
||||
ConfiguredSendPw,
|
||||
ConfiguredSendPort,
|
||||
ConfiguredServerFlags,
|
||||
ConfiguredSendSecurity,
|
||||
ConfiguredE2EEEnabled,
|
||||
Configured,
|
||||
// Deprecated
|
||||
#[strum(serialize = "sys.version")]
|
||||
@@ -75,7 +71,7 @@ impl Context {
|
||||
let rel_path = self.sql.get_config(self, key);
|
||||
rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string())
|
||||
}
|
||||
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
|
||||
Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
|
||||
Config::SysConfigKeys => Some(get_config_keys_string()),
|
||||
_ => self.sql.get_config(self, key),
|
||||
@@ -103,17 +99,17 @@ impl Context {
|
||||
}
|
||||
Config::InboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
interrupt_imap_idle(self);
|
||||
unsafe { dc_interrupt_imap_idle(self) };
|
||||
ret
|
||||
}
|
||||
Config::SentboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
interrupt_sentbox_idle(self);
|
||||
unsafe { dc_interrupt_sentbox_idle(self) };
|
||||
ret
|
||||
}
|
||||
Config::MvboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
interrupt_mvbox_idle(self);
|
||||
unsafe { dc_interrupt_mvbox_idle(self) };
|
||||
ret
|
||||
}
|
||||
Config::Selfstatus => {
|
||||
@@ -124,7 +120,8 @@ impl Context {
|
||||
value
|
||||
};
|
||||
|
||||
self.sql.set_config(self, key, val)
|
||||
let ret = self.sql.set_config(self, key, val);
|
||||
ret
|
||||
}
|
||||
_ => self.sql.set_config(self, key, value),
|
||||
}
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
use super::read_autoconf_file;
|
||||
/* ******************************************************************************
|
||||
* Thunderbird's Autoconfigure
|
||||
******************************************************************************/
|
||||
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
|
||||
#[repr(C)]
|
||||
struct moz_autoconfigure_t<'a> {
|
||||
pub in_0: &'a dc_loginparam_t,
|
||||
pub in_emaildomain: *mut libc::c_char,
|
||||
pub in_emaillocalpart: *mut libc::c_char,
|
||||
pub out: dc_loginparam_t,
|
||||
pub out_imap_set: libc::c_int,
|
||||
pub out_smtp_set: libc::c_int,
|
||||
pub tag_server: libc::c_int,
|
||||
pub tag_config: libc::c_int,
|
||||
}
|
||||
|
||||
pub unsafe fn moz_autoconfigure(
|
||||
context: &Context,
|
||||
url: &str,
|
||||
param_in: &dc_loginparam_t,
|
||||
) -> Option<dc_loginparam_t> {
|
||||
let mut moz_ac = moz_autoconfigure_t {
|
||||
in_0: param_in,
|
||||
in_emaildomain: std::ptr::null_mut(),
|
||||
in_emaillocalpart: std::ptr::null_mut(),
|
||||
out: dc_loginparam_new(),
|
||||
out_imap_set: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_server: 0,
|
||||
tag_config: 0,
|
||||
};
|
||||
|
||||
let url_c = url.strdup();
|
||||
let xml_raw = read_autoconf_file(context, url_c);
|
||||
free(url_c as *mut libc::c_void);
|
||||
if xml_raw.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
moz_ac.in_emaillocalpart = param_in.addr.strdup();
|
||||
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
|
||||
|
||||
if p.is_null() {
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
*p = 0 as libc::c_char;
|
||||
moz_ac.in_emaildomain = dc_strdup(p.offset(1isize));
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
|
||||
reader.trim_text(true);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
moz_autoconfigure_starttag_cb(e, &mut moz_ac, &reader)
|
||||
}
|
||||
Ok(quick_xml::events::Event::End(ref e)) => moz_autoconfigure_endtag_cb(e, &mut moz_ac),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => {
|
||||
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
if moz_ac.out.mail_server.is_empty()
|
||||
|| moz_ac.out.mail_port == 0
|
||||
|| moz_ac.out.send_server.is_empty()
|
||||
|| moz_ac.out.send_port == 0
|
||||
{
|
||||
let r = dc_loginparam_get_readable(&moz_ac.out);
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
Some(moz_ac.out)
|
||||
}
|
||||
|
||||
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
|
||||
event: &BytesText,
|
||||
moz_ac: &mut moz_autoconfigure_t,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
||||
|
||||
let addr = &moz_ac.in_0.addr;
|
||||
let email_local = as_str(moz_ac.in_emaillocalpart);
|
||||
let email_domain = as_str(moz_ac.in_emaildomain);
|
||||
|
||||
let val = val
|
||||
.trim()
|
||||
.replace("%EMAILADDRESS%", addr)
|
||||
.replace("%EMAILLOCALPART%", email_local)
|
||||
.replace("%EMAILDOMAIN%", email_domain);
|
||||
|
||||
if moz_ac.tag_server == 1 {
|
||||
match moz_ac.tag_config {
|
||||
10 => moz_ac.out.mail_server = val,
|
||||
11 => moz_ac.out.mail_port = val.parse().unwrap_or_default(),
|
||||
12 => moz_ac.out.mail_user = val,
|
||||
13 => {
|
||||
let val_lower = val.to_lowercase();
|
||||
if val_lower == "ssl" {
|
||||
moz_ac.out.server_flags |= 0x200
|
||||
}
|
||||
if val_lower == "starttls" {
|
||||
moz_ac.out.server_flags |= 0x100
|
||||
}
|
||||
if val_lower == "plain" {
|
||||
moz_ac.out.server_flags |= 0x400
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if moz_ac.tag_server == 2 {
|
||||
match moz_ac.tag_config {
|
||||
10 => moz_ac.out.send_server = val,
|
||||
11 => moz_ac.out.send_port = val.parse().unwrap_or_default(),
|
||||
12 => moz_ac.out.send_user = val,
|
||||
13 => {
|
||||
let val_lower = val.to_lowercase();
|
||||
if val_lower == "ssl" {
|
||||
moz_ac.out.server_flags |= 0x20000
|
||||
}
|
||||
if val_lower == "starttls" {
|
||||
moz_ac.out.server_flags |= 0x10000
|
||||
}
|
||||
if val_lower == "plain" {
|
||||
moz_ac.out.server_flags |= 0x40000
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut moz_autoconfigure_t) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "incomingserver" {
|
||||
moz_ac.tag_server = 0;
|
||||
moz_ac.tag_config = 0;
|
||||
moz_ac.out_imap_set = 1;
|
||||
} else if tag == "outgoingserver" {
|
||||
moz_ac.tag_server = 0;
|
||||
moz_ac.tag_config = 0;
|
||||
moz_ac.out_smtp_set = 1;
|
||||
} else {
|
||||
moz_ac.tag_config = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
|
||||
event: &BytesStart,
|
||||
moz_ac: &mut moz_autoconfigure_t,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "incomingserver" {
|
||||
moz_ac.tag_server = if let Some(typ) = event.attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "type")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
let typ = typ
|
||||
.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
|
||||
if typ == "imap" && moz_ac.out_imap_set == 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
moz_ac.tag_config = 0;
|
||||
} else if tag == "outgoingserver" {
|
||||
moz_ac.tag_server = if moz_ac.out_smtp_set == 0 { 2 } else { 0 };
|
||||
moz_ac.tag_config = 0;
|
||||
} else if tag == "hostname" {
|
||||
moz_ac.tag_config = 10;
|
||||
} else if tag == "port" {
|
||||
moz_ac.tag_config = 11;
|
||||
} else if tag == "sockettype" {
|
||||
moz_ac.tag_config = 13;
|
||||
} else if tag == "username" {
|
||||
moz_ac.tag_config = 12;
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
use std::ptr;
|
||||
|
||||
use super::read_autoconf_file;
|
||||
/* ******************************************************************************
|
||||
* Outlook's Autodiscover
|
||||
******************************************************************************/
|
||||
#[repr(C)]
|
||||
struct outlk_autodiscover_t<'a> {
|
||||
pub in_0: &'a dc_loginparam_t,
|
||||
pub out: dc_loginparam_t,
|
||||
pub out_imap_set: libc::c_int,
|
||||
pub out_smtp_set: libc::c_int,
|
||||
pub tag_config: libc::c_int,
|
||||
pub config: [*mut libc::c_char; 6],
|
||||
pub redirect: *mut libc::c_char,
|
||||
}
|
||||
|
||||
pub unsafe fn outlk_autodiscover(
|
||||
context: &Context,
|
||||
url__: &str,
|
||||
param_in: &dc_loginparam_t,
|
||||
) -> Option<dc_loginparam_t> {
|
||||
let mut xml_raw: *mut libc::c_char = ptr::null_mut();
|
||||
let mut url = url__.strdup();
|
||||
let mut outlk_ad = outlk_autodiscover_t {
|
||||
in_0: param_in,
|
||||
out: dc_loginparam_new(),
|
||||
out_imap_set: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_config: 0,
|
||||
config: [ptr::null_mut(); 6],
|
||||
redirect: ptr::null_mut(),
|
||||
};
|
||||
let ok_to_continue;
|
||||
let mut i = 0;
|
||||
loop {
|
||||
if !(i < 10) {
|
||||
ok_to_continue = true;
|
||||
break;
|
||||
}
|
||||
memset(
|
||||
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
|
||||
0,
|
||||
::std::mem::size_of::<outlk_autodiscover_t>(),
|
||||
);
|
||||
xml_raw = read_autoconf_file(context, url);
|
||||
if xml_raw.is_null() {
|
||||
ok_to_continue = false;
|
||||
break;
|
||||
}
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
|
||||
reader.trim_text(true);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
outlk_autodiscover_starttag_cb(e, &mut outlk_ad)
|
||||
}
|
||||
Ok(quick_xml::events::Event::End(ref e)) => {
|
||||
outlk_autodiscover_endtag_cb(e, &mut outlk_ad)
|
||||
}
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => {
|
||||
outlk_autodiscover_text_cb(e, &mut outlk_ad, &reader)
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
if !(!outlk_ad.config[5].is_null()
|
||||
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
|
||||
{
|
||||
ok_to_continue = true;
|
||||
break;
|
||||
}
|
||||
free(url as *mut libc::c_void);
|
||||
url = dc_strdup(outlk_ad.config[5usize]);
|
||||
|
||||
outlk_clean_config(&mut outlk_ad);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
xml_raw = ptr::null_mut();
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if ok_to_continue {
|
||||
if outlk_ad.out.mail_server.is_empty()
|
||||
|| outlk_ad.out.mail_port == 0
|
||||
|| outlk_ad.out.send_server.is_empty()
|
||||
|| outlk_ad.out.send_port == 0
|
||||
{
|
||||
let r = dc_loginparam_get_readable(&outlk_ad.out);
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
free(url as *mut libc::c_void);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
outlk_clean_config(&mut outlk_ad);
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
free(url as *mut libc::c_void);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
outlk_clean_config(&mut outlk_ad);
|
||||
Some(outlk_ad.out)
|
||||
}
|
||||
|
||||
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
|
||||
for i in 0..6 {
|
||||
free((*outlk_ad).config[i] as *mut libc::c_void);
|
||||
(*outlk_ad).config[i] = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
|
||||
fn outlk_autodiscover_text_cb<B: std::io::BufRead>(
|
||||
event: &BytesText,
|
||||
outlk_ad: &mut outlk_autodiscover_t,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
||||
|
||||
unsafe {
|
||||
free(outlk_ad.config[outlk_ad.tag_config as usize].cast());
|
||||
outlk_ad.config[outlk_ad.tag_config as usize] = val.trim().strdup();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut outlk_autodiscover_t) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "protocol" {
|
||||
if !outlk_ad.config[1].is_null() {
|
||||
let port = dc_atoi_null_is_0(outlk_ad.config[3]);
|
||||
let ssl_on = (!outlk_ad.config[4].is_null()
|
||||
&& strcasecmp(
|
||||
outlk_ad.config[4],
|
||||
b"on\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0) as libc::c_int;
|
||||
let ssl_off = (!outlk_ad.config[4].is_null()
|
||||
&& strcasecmp(
|
||||
outlk_ad.config[4],
|
||||
b"off\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0) as libc::c_int;
|
||||
if strcasecmp(
|
||||
outlk_ad.config[1],
|
||||
b"imap\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
&& outlk_ad.out_imap_set == 0
|
||||
{
|
||||
outlk_ad.out.mail_server = to_string(outlk_ad.config[2]);
|
||||
outlk_ad.out.mail_port = port;
|
||||
if 0 != ssl_on {
|
||||
outlk_ad.out.server_flags |= 0x200
|
||||
} else if 0 != ssl_off {
|
||||
outlk_ad.out.server_flags |= 0x400
|
||||
}
|
||||
outlk_ad.out_imap_set = 1
|
||||
} else if strcasecmp(
|
||||
outlk_ad.config[1usize],
|
||||
b"smtp\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
&& outlk_ad.out_smtp_set == 0
|
||||
{
|
||||
outlk_ad.out.send_server = to_string(outlk_ad.config[2]);
|
||||
outlk_ad.out.send_port = port;
|
||||
if 0 != ssl_on {
|
||||
outlk_ad.out.server_flags |= 0x20000
|
||||
} else if 0 != ssl_off {
|
||||
outlk_ad.out.server_flags |= 0x40000
|
||||
}
|
||||
outlk_ad.out_smtp_set = 1
|
||||
}
|
||||
}
|
||||
outlk_clean_config(outlk_ad);
|
||||
}
|
||||
outlk_ad.tag_config = 0;
|
||||
}
|
||||
|
||||
fn outlk_autodiscover_starttag_cb(event: &BytesStart, outlk_ad: &mut outlk_autodiscover_t) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "protocol" {
|
||||
unsafe { outlk_clean_config(outlk_ad) };
|
||||
} else if tag == "type" {
|
||||
outlk_ad.tag_config = 1
|
||||
} else if tag == "server" {
|
||||
outlk_ad.tag_config = 2
|
||||
} else if tag == "port" {
|
||||
outlk_ad.tag_config = 3
|
||||
} else if tag == "ssl" {
|
||||
outlk_ad.tag_config = 4
|
||||
} else if tag == "redirecturl" {
|
||||
outlk_ad.tag_config = 5
|
||||
};
|
||||
}
|
||||
272
src/constants.rs
272
src/constants.rs
@@ -1,13 +1,8 @@
|
||||
//! Constants
|
||||
#![allow(non_camel_case_types, dead_code)]
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
use deltachat_derive::*;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
|
||||
}
|
||||
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
@@ -19,30 +14,20 @@ pub enum MoveState {
|
||||
}
|
||||
|
||||
// some defaults
|
||||
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
|
||||
pub const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
|
||||
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
|
||||
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
|
||||
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
|
||||
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
|
||||
const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
|
||||
pub const DC_INBOX_WATCH_DEFAULT: i32 = 1;
|
||||
pub const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
|
||||
pub const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
|
||||
pub const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
#[repr(u8)]
|
||||
pub enum Blocked {
|
||||
Not = 0,
|
||||
Manually = 1,
|
||||
Deaddrop = 2,
|
||||
}
|
||||
|
||||
impl Default for Blocked {
|
||||
fn default() -> Self {
|
||||
Blocked::Not
|
||||
}
|
||||
}
|
||||
pub const DC_CHAT_NOT_BLOCKED: i32 = 0;
|
||||
pub const DC_CHAT_MANUALLY_BLOCKED: i32 = 1;
|
||||
pub const DC_CHAT_DEADDROP_BLOCKED: i32 = 2;
|
||||
|
||||
pub const DC_IMAP_SEEN: u32 = 0x1;
|
||||
|
||||
const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
|
||||
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
|
||||
|
||||
@@ -50,75 +35,88 @@ pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
|
||||
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
|
||||
|
||||
const DC_GCM_ADDDAYMARKER: usize = 0x01;
|
||||
pub const DC_GCM_ADDDAYMARKER: usize = 0x01;
|
||||
|
||||
const DC_GCL_VERIFIED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_ADD_SELF: usize = 0x02;
|
||||
|
||||
/// param1 is a directory where the keys are written to
|
||||
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
|
||||
pub const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
|
||||
/// param1 is a directory where the keys are searched in and read from
|
||||
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
|
||||
pub const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
|
||||
/// param1 is a directory where the backup is written to
|
||||
const DC_IMEX_EXPORT_BACKUP: usize = 11;
|
||||
pub const DC_IMEX_EXPORT_BACKUP: usize = 11;
|
||||
/// param1 is the file with the backup to import
|
||||
const DC_IMEX_IMPORT_BACKUP: usize = 12;
|
||||
pub const DC_IMEX_IMPORT_BACKUP: usize = 12;
|
||||
|
||||
/// id=contact
|
||||
pub const DC_QR_ASK_VERIFYCONTACT: usize = 200;
|
||||
/// text1=groupname
|
||||
pub const DC_QR_ASK_VERIFYGROUP: usize = 202;
|
||||
/// id=contact
|
||||
pub const DC_QR_FPR_OK: usize = 210;
|
||||
/// id=contact
|
||||
pub const DC_QR_FPR_MISMATCH: usize = 220;
|
||||
/// test1=formatted fingerprint
|
||||
pub const DC_QR_FPR_WITHOUT_ADDR: usize = 230;
|
||||
/// id=contact
|
||||
pub const DC_QR_ADDR: usize = 320;
|
||||
/// text1=text
|
||||
pub const DC_QR_TEXT: usize = 330;
|
||||
/// text1=URL
|
||||
pub const DC_QR_URL: usize = 332;
|
||||
/// text1=error string
|
||||
pub const DC_QR_ERROR: usize = 400;
|
||||
|
||||
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
|
||||
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 1;
|
||||
pub const DC_CHAT_ID_DEADDROP: usize = 1;
|
||||
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
|
||||
pub const DC_CHAT_ID_TRASH: u32 = 3;
|
||||
pub const DC_CHAT_ID_TRASH: usize = 3;
|
||||
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
|
||||
const DC_CHAT_ID_MSGS_IN_CREATION: u32 = 4;
|
||||
pub const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
|
||||
/// virtual chat showing all messages flagged with msgs.starred=2
|
||||
const DC_CHAT_ID_STARRED: u32 = 5;
|
||||
pub const DC_CHAT_ID_STARRED: usize = 5;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: u32 = 6;
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: usize = 6;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ALLDONE_HINT: u32 = 7;
|
||||
pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7;
|
||||
/// larger chat IDs are "real" chats, their messages are "real" messages.
|
||||
pub const DC_CHAT_ID_LAST_SPECIAL: u32 = 9;
|
||||
pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
FromSql,
|
||||
ToSql,
|
||||
IntoStaticStr,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Chattype {
|
||||
Undefined = 0,
|
||||
Single = 100,
|
||||
Group = 120,
|
||||
VerifiedGroup = 130,
|
||||
}
|
||||
pub const DC_CHAT_TYPE_UNDEFINED: i32 = 0;
|
||||
pub const DC_CHAT_TYPE_SINGLE: i32 = 100;
|
||||
pub const DC_CHAT_TYPE_GROUP: i32 = 120;
|
||||
pub const DC_CHAT_TYPE_VERIFIED_GROUP: i32 = 130;
|
||||
|
||||
impl Default for Chattype {
|
||||
fn default() -> Self {
|
||||
Chattype::Undefined
|
||||
}
|
||||
}
|
||||
pub const DC_MSG_ID_MARKER1: usize = 1;
|
||||
pub const DC_MSG_ID_DAYMARKER: usize = 9;
|
||||
pub const DC_MSG_ID_LAST_SPECIAL: usize = 9;
|
||||
|
||||
pub const DC_MSG_ID_MARKER1: u32 = 1;
|
||||
const DC_MSG_ID_DAYMARKER: u32 = 9;
|
||||
pub const DC_MSG_ID_LAST_SPECIAL: u32 = 9;
|
||||
pub const DC_STATE_UNDEFINED: i32 = 0;
|
||||
pub const DC_STATE_IN_FRESH: i32 = 10;
|
||||
pub const DC_STATE_IN_NOTICED: i32 = 13;
|
||||
pub const DC_STATE_IN_SEEN: i32 = 16;
|
||||
pub const DC_STATE_OUT_PREPARING: i32 = 18;
|
||||
pub const DC_STATE_OUT_DRAFT: i32 = 19;
|
||||
pub const DC_STATE_OUT_PENDING: i32 = 20;
|
||||
pub const DC_STATE_OUT_FAILED: i32 = 24;
|
||||
/// to check if a mail was sent, use dc_msg_is_sent()
|
||||
pub const DC_STATE_OUT_DELIVERED: i32 = 26;
|
||||
pub const DC_STATE_OUT_MDN_RCVD: i32 = 28;
|
||||
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
const DC_MAX_GET_TEXT_LEN: usize = 30000;
|
||||
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
|
||||
/// approx. max. length returned by dc_get_msg_info()
|
||||
const DC_MAX_GET_INFO_LEN: usize = 100000;
|
||||
pub const DC_MAX_GET_INFO_LEN: usize = 100000;
|
||||
|
||||
pub const DC_CONTACT_ID_UNDEFINED: u32 = 0;
|
||||
pub const DC_CONTACT_ID_SELF: u32 = 1;
|
||||
const DC_CONTACT_ID_DEVICE: u32 = 2;
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: u32 = 9;
|
||||
pub const DC_CONTACT_ID_UNDEFINED: usize = 0;
|
||||
pub const DC_CONTACT_ID_SELF: usize = 1;
|
||||
pub const DC_CONTACT_ID_DEVICE: usize = 2;
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
|
||||
|
||||
pub const DC_TEXT1_DRAFT: usize = 1;
|
||||
pub const DC_TEXT1_USERNAME: usize = 2;
|
||||
pub const DC_TEXT1_SELF: usize = 3;
|
||||
|
||||
pub const DC_CREATE_MVBOX: usize = 1;
|
||||
|
||||
@@ -128,13 +126,13 @@ pub const DC_CREATE_MVBOX: usize = 1;
|
||||
// via dc_set_config() using the key "server_flags".
|
||||
|
||||
/// Force OAuth2 authorization. This flag does not skip automatic configuration.
|
||||
/// Before calling configure() with DC_LP_AUTH_OAUTH2 set,
|
||||
/// Before calling dc_configure() with DC_LP_AUTH_OAUTH2 set,
|
||||
/// the user has to confirm access at the URL returned by dc_get_oauth2_url().
|
||||
pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
|
||||
|
||||
/// Force NORMAL authorization, this is the default.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
const DC_LP_AUTH_NORMAL: usize = 0x4;
|
||||
pub const DC_LP_AUTH_NORMAL: usize = 0x4;
|
||||
|
||||
/// Connect to IMAP via STARTTLS.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
@@ -142,7 +140,7 @@ pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
|
||||
|
||||
/// Connect to IMAP via SSL.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
|
||||
pub const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
|
||||
|
||||
/// Connect to IMAP unencrypted, this should not be used.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
@@ -154,19 +152,19 @@ pub const DC_LP_SMTP_SOCKET_STARTTLS: usize = 0x10000;
|
||||
|
||||
/// Connect to SMTP via SSL.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
|
||||
/// Connect to SMTP unencrypted, this should not be used.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
|
||||
|
||||
/// if none of these flags are set, the default is chosen
|
||||
const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
|
||||
pub const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
|
||||
/// if none of these flags are set, the default is chosen
|
||||
const DC_LP_IMAP_SOCKET_FLAGS: usize =
|
||||
pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
|
||||
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
|
||||
/// if none of these flags are set, the default is chosen
|
||||
const DC_LP_SMTP_SOCKET_FLAGS: usize =
|
||||
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
|
||||
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
@@ -282,7 +280,7 @@ pub enum Event {
|
||||
/// As most things are asynchronous, things may go wrong at any time and the user
|
||||
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||
///
|
||||
/// However, for ongoing processes (eg. configure())
|
||||
/// However, for ongoing processes (eg. dc_configure())
|
||||
/// or for functions that are expected to fail (eg. dc_continue_key_transfer())
|
||||
/// it might be better to delay showing these events until the function has really
|
||||
/// failed (returned false). It should be sufficient to report only the _last_ error
|
||||
@@ -400,7 +398,7 @@ pub enum Event {
|
||||
/// @return 0
|
||||
LOCATION_CHANGED = 2035,
|
||||
|
||||
/// Inform about the configuration progress started by configure().
|
||||
/// Inform about the configuration progress started by dc_configure().
|
||||
///
|
||||
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
@@ -465,65 +463,65 @@ pub enum Event {
|
||||
GET_STRING = 2091,
|
||||
}
|
||||
|
||||
const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
|
||||
const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
|
||||
const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
|
||||
const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
|
||||
const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
|
||||
pub const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
|
||||
pub const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
|
||||
pub const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
|
||||
pub const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
|
||||
pub const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
|
||||
|
||||
/// Values for dc_get|set_config("show_emails")
|
||||
const DC_SHOW_EMAILS_OFF: usize = 0;
|
||||
const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
|
||||
const DC_SHOW_EMAILS_ALL: usize = 2;
|
||||
pub const DC_SHOW_EMAILS_OFF: usize = 0;
|
||||
pub const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
|
||||
pub const DC_SHOW_EMAILS_ALL: usize = 2;
|
||||
|
||||
// TODO: Strings need some doumentation about used placeholders.
|
||||
// These constants are used to request strings using #DC_EVENT_GET_STRING.
|
||||
|
||||
const DC_STR_NOMESSAGES: usize = 1;
|
||||
const DC_STR_SELF: usize = 2;
|
||||
const DC_STR_DRAFT: usize = 3;
|
||||
const DC_STR_MEMBER: usize = 4;
|
||||
const DC_STR_CONTACT: usize = 6;
|
||||
const DC_STR_VOICEMESSAGE: usize = 7;
|
||||
const DC_STR_DEADDROP: usize = 8;
|
||||
const DC_STR_IMAGE: usize = 9;
|
||||
const DC_STR_VIDEO: usize = 10;
|
||||
const DC_STR_AUDIO: usize = 11;
|
||||
const DC_STR_FILE: usize = 12;
|
||||
const DC_STR_STATUSLINE: usize = 13;
|
||||
const DC_STR_NEWGROUPDRAFT: usize = 14;
|
||||
const DC_STR_MSGGRPNAME: usize = 15;
|
||||
const DC_STR_MSGGRPIMGCHANGED: usize = 16;
|
||||
const DC_STR_MSGADDMEMBER: usize = 17;
|
||||
const DC_STR_MSGDELMEMBER: usize = 18;
|
||||
const DC_STR_MSGGROUPLEFT: usize = 19;
|
||||
const DC_STR_GIF: usize = 23;
|
||||
const DC_STR_ENCRYPTEDMSG: usize = 24;
|
||||
const DC_STR_E2E_AVAILABLE: usize = 25;
|
||||
const DC_STR_ENCR_TRANSP: usize = 27;
|
||||
const DC_STR_ENCR_NONE: usize = 28;
|
||||
const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
|
||||
const DC_STR_FINGERPRINTS: usize = 30;
|
||||
const DC_STR_READRCPT: usize = 31;
|
||||
const DC_STR_READRCPT_MAILBODY: usize = 32;
|
||||
const DC_STR_MSGGRPIMGDELETED: usize = 33;
|
||||
const DC_STR_E2E_PREFERRED: usize = 34;
|
||||
const DC_STR_CONTACT_VERIFIED: usize = 35;
|
||||
const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
|
||||
const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
|
||||
const DC_STR_ARCHIVEDCHATS: usize = 40;
|
||||
const DC_STR_STARREDMSGS: usize = 41;
|
||||
const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
|
||||
const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
|
||||
const DC_STR_SELFTALK_SUBTITLE: usize = 50;
|
||||
const DC_STR_CANNOT_LOGIN: usize = 60;
|
||||
const DC_STR_SERVER_RESPONSE: usize = 61;
|
||||
const DC_STR_MSGACTIONBYUSER: usize = 62;
|
||||
const DC_STR_MSGACTIONBYME: usize = 63;
|
||||
const DC_STR_MSGLOCATIONENABLED: usize = 64;
|
||||
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
|
||||
const DC_STR_LOCATION: usize = 66;
|
||||
const DC_STR_COUNT: usize = 66;
|
||||
pub const DC_STR_NOMESSAGES: usize = 1;
|
||||
pub const DC_STR_SELF: usize = 2;
|
||||
pub const DC_STR_DRAFT: usize = 3;
|
||||
pub const DC_STR_MEMBER: usize = 4;
|
||||
pub const DC_STR_CONTACT: usize = 6;
|
||||
pub const DC_STR_VOICEMESSAGE: usize = 7;
|
||||
pub const DC_STR_DEADDROP: usize = 8;
|
||||
pub const DC_STR_IMAGE: usize = 9;
|
||||
pub const DC_STR_VIDEO: usize = 10;
|
||||
pub const DC_STR_AUDIO: usize = 11;
|
||||
pub const DC_STR_FILE: usize = 12;
|
||||
pub const DC_STR_STATUSLINE: usize = 13;
|
||||
pub const DC_STR_NEWGROUPDRAFT: usize = 14;
|
||||
pub const DC_STR_MSGGRPNAME: usize = 15;
|
||||
pub const DC_STR_MSGGRPIMGCHANGED: usize = 16;
|
||||
pub const DC_STR_MSGADDMEMBER: usize = 17;
|
||||
pub const DC_STR_MSGDELMEMBER: usize = 18;
|
||||
pub const DC_STR_MSGGROUPLEFT: usize = 19;
|
||||
pub const DC_STR_GIF: usize = 23;
|
||||
pub const DC_STR_ENCRYPTEDMSG: usize = 24;
|
||||
pub const DC_STR_E2E_AVAILABLE: usize = 25;
|
||||
pub const DC_STR_ENCR_TRANSP: usize = 27;
|
||||
pub const DC_STR_ENCR_NONE: usize = 28;
|
||||
pub const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
|
||||
pub const DC_STR_FINGERPRINTS: usize = 30;
|
||||
pub const DC_STR_READRCPT: usize = 31;
|
||||
pub const DC_STR_READRCPT_MAILBODY: usize = 32;
|
||||
pub const DC_STR_MSGGRPIMGDELETED: usize = 33;
|
||||
pub const DC_STR_E2E_PREFERRED: usize = 34;
|
||||
pub const DC_STR_CONTACT_VERIFIED: usize = 35;
|
||||
pub const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
|
||||
pub const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
|
||||
pub const DC_STR_ARCHIVEDCHATS: usize = 40;
|
||||
pub const DC_STR_STARREDMSGS: usize = 41;
|
||||
pub const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
|
||||
pub const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
|
||||
pub const DC_STR_SELFTALK_SUBTITLE: usize = 50;
|
||||
pub const DC_STR_CANNOT_LOGIN: usize = 60;
|
||||
pub const DC_STR_SERVER_RESPONSE: usize = 61;
|
||||
pub const DC_STR_MSGACTIONBYUSER: usize = 62;
|
||||
pub const DC_STR_MSGACTIONBYME: usize = 63;
|
||||
pub const DC_STR_MSGLOCATIONENABLED: usize = 64;
|
||||
pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
|
||||
pub const DC_STR_LOCATION: usize = 66;
|
||||
pub const DC_STR_COUNT: usize = 66;
|
||||
|
||||
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
|
||||
|
||||
@@ -539,6 +537,6 @@ pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
|
||||
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
|
||||
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
|
||||
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
|
||||
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
|
||||
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
|
||||
@@ -7,12 +7,12 @@ use crate::aheader::EncryptPreference;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee;
|
||||
use crate::error::Result;
|
||||
use crate::key::*;
|
||||
use crate::message::MessageState;
|
||||
use crate::peerstate::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -139,7 +139,7 @@ pub enum Modifier {
|
||||
Created,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum VerifiedStatus {
|
||||
/// Contact is not verified.
|
||||
@@ -152,7 +152,7 @@ pub enum VerifiedStatus {
|
||||
|
||||
impl<'a> Contact<'a> {
|
||||
pub fn load_from_db(context: &'a Context, contact_id: u32) -> Result<Self> {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
if contact_id == DC_CONTACT_ID_SELF as u32 {
|
||||
let contact = Contact {
|
||||
context,
|
||||
id: contact_id,
|
||||
@@ -251,7 +251,7 @@ impl<'a> Contact<'a> {
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
|
||||
params![MessageState::InNoticed, id as i32, MessageState::InFresh],
|
||||
params![DC_STATE_IN_NOTICED, id as i32, DC_STATE_IN_FRESH],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -328,7 +328,7 @@ impl<'a> Contact<'a> {
|
||||
"<unset>"
|
||||
},
|
||||
);
|
||||
bail!("Bad address supplied: {:?}", addr);
|
||||
bail!("Bad address supplied");
|
||||
}
|
||||
|
||||
let mut update_addr = false;
|
||||
@@ -545,7 +545,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
|
||||
if 0 != listflags & DC_GCL_ADD_SELF as u32 && add_self {
|
||||
ret.push(DC_CONTACT_ID_SELF);
|
||||
ret.push(DC_CONTACT_ID_SELF as u32);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
@@ -564,19 +564,24 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
|
||||
/// Get blocked contacts.
|
||||
pub fn get_all_blocked(context: &Context) -> Vec<u32> {
|
||||
pub fn get_all_blocked(context: &Context) -> *mut dc_array_t {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;",
|
||||
params![DC_CONTACT_ID_LAST_SPECIAL as i32],
|
||||
|row| row.get::<_, u32>(0),
|
||||
|row| row.get::<_, i32>(0),
|
||||
|ids| {
|
||||
ids.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
let mut ret = Vec::new();
|
||||
|
||||
for id in ids {
|
||||
ret.push(id? as u32);
|
||||
}
|
||||
|
||||
Ok(dc_array_t::from(ret).into_raw())
|
||||
},
|
||||
)
|
||||
.unwrap_or_default()
|
||||
.unwrap_or_else(|_| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
/// Returns a textual summary of the encryption state for the contact.
|
||||
@@ -603,7 +608,7 @@ impl<'a> Contact<'a> {
|
||||
});
|
||||
ret += &p;
|
||||
if self_key.is_none() {
|
||||
e2ee::ensure_secret_key_exists(context)?;
|
||||
dc_ensure_secret_key_exists(context)?;
|
||||
self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
|
||||
}
|
||||
let p = context.stock_str(StockMessage::FingerPrints);
|
||||
@@ -655,7 +660,7 @@ impl<'a> Contact<'a> {
|
||||
/// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
|
||||
pub fn delete(context: &Context, contact_id: u32) -> Result<()> {
|
||||
ensure!(
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL as u32,
|
||||
"Can not delete special contact"
|
||||
);
|
||||
|
||||
@@ -780,7 +785,7 @@ impl<'a> Contact<'a> {
|
||||
/// This is the image set by each remote user on their own
|
||||
/// using dc_set_config(context, "selfavatar", image).
|
||||
pub fn get_profile_image(&self) -> Option<String> {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
if self.id == DC_CONTACT_ID_SELF as u32 {
|
||||
return self.context.get_config(Config::Selfavatar);
|
||||
}
|
||||
// TODO: else get image_abs from contact param
|
||||
@@ -792,7 +797,7 @@ impl<'a> Contact<'a> {
|
||||
/// and can be used for an fallback avatar with white initials
|
||||
/// as well as for headlines in bubbles of group chats.
|
||||
pub fn get_color(&self) -> u32 {
|
||||
dc_str_to_color(&self.addr)
|
||||
dc_str_to_color_safe(&self.addr)
|
||||
}
|
||||
|
||||
/// Check if a contact was verified. E.g. by a secure-join QR code scan
|
||||
@@ -810,7 +815,7 @@ impl<'a> Contact<'a> {
|
||||
pub fn is_verified_ex(&self, peerstate: Option<&Peerstate<'a>>) -> VerifiedStatus {
|
||||
// We're always sort of secured-verified as we could verify the key on this device any time with the key
|
||||
// on this device
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
if self.id == DC_CONTACT_ID_SELF as u32 {
|
||||
return VerifiedStatus::BidirectVerified;
|
||||
}
|
||||
|
||||
@@ -904,14 +909,26 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_first_name<'a>(full_name: &'a str) -> &'a str {
|
||||
pub fn get_first_name<'a>(full_name: &'a str) -> &'a str {
|
||||
full_name.splitn(2, ' ').next().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns false if addr is an invalid address, otherwise true.
|
||||
pub fn may_be_valid_addr(addr: &str) -> bool {
|
||||
let res = addr.parse::<EmailAddress>();
|
||||
res.is_ok()
|
||||
if addr.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let at = addr.find('@').unwrap_or_default();
|
||||
if at < 1 {
|
||||
return false;
|
||||
}
|
||||
let dot = addr.find('.').unwrap_or_default();
|
||||
if dot < 1 || dot > addr.len() - 3 || dot < at + 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn addr_normalize(addr: &str) -> &str {
|
||||
|
||||
162
src/context.rs
162
src/context.rs
@@ -1,36 +1,35 @@
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_jobthread::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_lot::dc_lot_t;
|
||||
use crate::dc_move::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_receive_imf::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::*;
|
||||
use crate::job::*;
|
||||
use crate::job_thread::JobThread;
|
||||
use crate::key::*;
|
||||
use crate::lot::Lot;
|
||||
use crate::message::*;
|
||||
use crate::param::Params;
|
||||
use crate::smtp::*;
|
||||
use crate::sql::Sql;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
use std::ptr;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Context {
|
||||
pub userdata: *mut libc::c_void,
|
||||
pub dbfile: Arc<RwLock<Option<PathBuf>>>,
|
||||
pub dbfile: Arc<RwLock<*mut libc::c_char>>,
|
||||
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
|
||||
pub sql: Sql,
|
||||
pub inbox: Arc<RwLock<Imap>>,
|
||||
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
|
||||
pub probe_imap_network: Arc<RwLock<bool>>,
|
||||
pub sentbox_thread: Arc<RwLock<JobThread>>,
|
||||
pub mvbox_thread: Arc<RwLock<JobThread>>,
|
||||
pub sentbox_thread: Arc<RwLock<dc_jobthread_t>>,
|
||||
pub mvbox_thread: Arc<RwLock<dc_jobthread_t>>,
|
||||
pub smtp: Arc<Mutex<Smtp>>,
|
||||
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
|
||||
pub oauth2_critical: Arc<Mutex<()>>,
|
||||
@@ -55,17 +54,15 @@ pub struct RunningState {
|
||||
|
||||
impl Context {
|
||||
pub fn has_dbfile(&self) -> bool {
|
||||
self.get_dbfile().is_some()
|
||||
!self.get_dbfile().is_null()
|
||||
}
|
||||
|
||||
pub fn has_blobdir(&self) -> bool {
|
||||
!self.get_blobdir().is_null()
|
||||
}
|
||||
|
||||
pub fn get_dbfile(&self) -> Option<PathBuf> {
|
||||
(*self.dbfile.clone().read().unwrap())
|
||||
.as_ref()
|
||||
.map(|x| x.clone())
|
||||
pub fn get_dbfile(&self) -> *const libc::c_char {
|
||||
*self.dbfile.clone().read().unwrap()
|
||||
}
|
||||
|
||||
pub fn get_blobdir(&self) -> *const libc::c_char {
|
||||
@@ -98,11 +95,21 @@ impl Default for RunningState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct BobStatus {
|
||||
pub expects: i32,
|
||||
pub status: i32,
|
||||
pub qr_scan: Option<Lot>,
|
||||
pub qr_scan: *mut dc_lot_t,
|
||||
}
|
||||
|
||||
impl Default for BobStatus {
|
||||
fn default() -> Self {
|
||||
BobStatus {
|
||||
expects: 0,
|
||||
status: 0,
|
||||
qr_scan: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@@ -122,7 +129,7 @@ pub fn dc_context_new(
|
||||
) -> Context {
|
||||
Context {
|
||||
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
|
||||
dbfile: Arc::new(RwLock::new(None)),
|
||||
dbfile: Arc::new(RwLock::new(std::ptr::null_mut())),
|
||||
inbox: Arc::new(RwLock::new({
|
||||
Imap::new(
|
||||
cb_get_config,
|
||||
@@ -133,7 +140,7 @@ pub fn dc_context_new(
|
||||
})),
|
||||
userdata,
|
||||
cb,
|
||||
os_name,
|
||||
os_name: os_name,
|
||||
running_state: Arc::new(RwLock::new(Default::default())),
|
||||
sql: Sql::new(),
|
||||
smtp: Arc::new(Mutex::new(Smtp::new())),
|
||||
@@ -142,7 +149,7 @@ pub fn dc_context_new(
|
||||
bob: Arc::new(RwLock::new(Default::default())),
|
||||
last_smeared_timestamp: Arc::new(RwLock::new(0)),
|
||||
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
|
||||
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||
sentbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
|
||||
"SENTBOX",
|
||||
"configured_sentbox_folder",
|
||||
Imap::new(
|
||||
@@ -152,7 +159,7 @@ pub fn dc_context_new(
|
||||
cb_receive_imf,
|
||||
),
|
||||
))),
|
||||
mvbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||
mvbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
|
||||
"MVBOX",
|
||||
"configured_mvbox_folder",
|
||||
Imap::new(
|
||||
@@ -194,7 +201,7 @@ unsafe fn cb_precheck_imf(
|
||||
) -> libc::c_int {
|
||||
let mut rfc724_mid_exists: libc::c_int = 0i32;
|
||||
let msg_id: uint32_t;
|
||||
let mut old_server_folder: *mut libc::c_char = ptr::null_mut();
|
||||
let mut old_server_folder: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut old_server_uid: uint32_t = 0i32 as uint32_t;
|
||||
let mut mark_seen: libc::c_int = 0i32;
|
||||
msg_id = dc_rfc724_mid_exists(
|
||||
@@ -227,19 +234,13 @@ unsafe fn cb_precheck_imf(
|
||||
if as_str(old_server_folder) != server_folder || old_server_uid != server_uid {
|
||||
dc_update_server_uid(context, rfc724_mid, server_folder, server_uid);
|
||||
}
|
||||
do_heuristics_moves(context, server_folder, msg_id);
|
||||
dc_do_heuristics_moves(context, server_folder, msg_id);
|
||||
if 0 != mark_seen {
|
||||
job_add(
|
||||
context,
|
||||
Action::MarkseenMsgOnImap,
|
||||
msg_id as libc::c_int,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
|
||||
}
|
||||
}
|
||||
free(old_server_folder as *mut libc::c_void);
|
||||
rfc724_mid_exists
|
||||
return rfc724_mid_exists;
|
||||
}
|
||||
|
||||
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
|
||||
@@ -280,14 +281,18 @@ pub unsafe fn dc_close(context: &Context) {
|
||||
|
||||
context.sql.close(context);
|
||||
let mut dbfile = context.dbfile.write().unwrap();
|
||||
*dbfile = None;
|
||||
free(*dbfile as *mut libc::c_void);
|
||||
*dbfile = 0 as *mut libc::c_char;
|
||||
let mut blobdir = context.blobdir.write().unwrap();
|
||||
free(*blobdir as *mut libc::c_void);
|
||||
*blobdir = ptr::null_mut();
|
||||
*blobdir = 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
pub unsafe fn dc_is_open(context: &Context) -> libc::c_int {
|
||||
context.sql.is_open() as libc::c_int
|
||||
match context.sql.is_open() {
|
||||
true => 1,
|
||||
false => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
|
||||
@@ -299,11 +304,9 @@ pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) ->
|
||||
if 0 != dc_is_open(context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*context.dbfile.write().unwrap() = Some(PathBuf::from(dbfile));
|
||||
let blobdir = blobdir.unwrap_or_default();
|
||||
if !blobdir.is_empty() {
|
||||
let dir = dc_ensure_no_slash_safe(blobdir).strdup();
|
||||
*context.dbfile.write().unwrap() = dbfile.strdup();
|
||||
if blobdir.is_some() && blobdir.unwrap().len() > 0 {
|
||||
let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup();
|
||||
*context.blobdir.write().unwrap() = dir;
|
||||
} else {
|
||||
let dir = dbfile.to_string() + "-blobs";
|
||||
@@ -334,7 +337,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
let l = dc_loginparam_read(context, &context.sql, "");
|
||||
let l2 = dc_loginparam_read(context, &context.sql, "configured_");
|
||||
let displayname = context.sql.get_config(context, "displayname");
|
||||
let chats = get_chat_cnt(context) as usize;
|
||||
let chats = dc_get_chat_cnt(context) as usize;
|
||||
let real_msgs = dc_get_real_msg_cnt(context) as usize;
|
||||
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
|
||||
let contacts = Contact::get_real_cnt(context) as usize;
|
||||
@@ -436,7 +439,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
public_key_count={}\n\
|
||||
fingerprint={}\n\
|
||||
level=awesome\n",
|
||||
&*DC_VERSION_STR,
|
||||
as_str(DC_VERSION_STR as *const u8 as *const _),
|
||||
rusqlite::version(),
|
||||
sqlite3_threadsafe(),
|
||||
// arch
|
||||
@@ -445,10 +448,11 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
real_msgs,
|
||||
deaddrop_msgs,
|
||||
contacts,
|
||||
context
|
||||
.get_dbfile()
|
||||
.as_ref()
|
||||
.map_or(unset, |p| p.to_str().unwrap()),
|
||||
if context.has_dbfile() {
|
||||
as_str(context.get_dbfile())
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
dbversion,
|
||||
if context.has_blobdir() {
|
||||
as_str(context.get_blobdir())
|
||||
@@ -477,10 +481,10 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
|
||||
(&*DC_VERSION_STR).strdup()
|
||||
dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char)
|
||||
}
|
||||
|
||||
pub fn dc_get_fresh_msgs(context: &Context) -> Vec<u32> {
|
||||
pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
|
||||
let show_deaddrop = 0;
|
||||
|
||||
context
|
||||
@@ -500,7 +504,7 @@ pub fn dc_get_fresh_msgs(context: &Context) -> Vec<u32> {
|
||||
let id: u32 = row?;
|
||||
ret.push(id);
|
||||
}
|
||||
Ok(ret)
|
||||
Ok(dc_array_t::from(ret).into_raw())
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
@@ -511,14 +515,14 @@ pub fn dc_search_msgs(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
query: *const libc::c_char,
|
||||
) -> Vec<u32> {
|
||||
) -> *mut dc_array_t {
|
||||
if query.is_null() {
|
||||
return Vec::new();
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let real_query = to_string(query).trim().to_string();
|
||||
if real_query.is_empty() {
|
||||
return Vec::new();
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
let strLikeInText = format!("%{}%", &real_query);
|
||||
let strLikeBeg = format!("{}%", &real_query);
|
||||
@@ -534,21 +538,28 @@ pub fn dc_search_msgs(
|
||||
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
|
||||
};
|
||||
|
||||
context
|
||||
let mut ret = Vec::new();
|
||||
|
||||
let success = context
|
||||
.sql
|
||||
.query_map(
|
||||
query,
|
||||
params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|rows| {
|
||||
let mut ret = Vec::new();
|
||||
for id in rows {
|
||||
ret.push(id? as u32);
|
||||
}
|
||||
Ok(ret)
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap_or_default()
|
||||
.is_ok();
|
||||
|
||||
if success {
|
||||
return dc_array_t::from(ret).into_raw();
|
||||
}
|
||||
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
@@ -574,45 +585,6 @@ pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
.unwrap_or_else(|| 1)
|
||||
== 0
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Ok(msg) = dc_msg_new_load(context, msg_id) {
|
||||
if dc_msg_is_setupmessage(&msg) {
|
||||
// do not move setup messages;
|
||||
// there may be a non-delta device that wants to handle it
|
||||
return;
|
||||
}
|
||||
|
||||
if dc_is_mvbox(context, folder) {
|
||||
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Stay);
|
||||
}
|
||||
|
||||
// 1 = dc message, 2 = reply to dc message
|
||||
if 0 != msg.is_dc_message {
|
||||
job_add(
|
||||
context,
|
||||
Action::MoveMsg,
|
||||
msg.id as libc::c_int,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Moving);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
342
src/dc_array.rs
342
src/dc_array.rs
@@ -1,12 +1,13 @@
|
||||
use crate::location::Location;
|
||||
use crate::dc_location::dc_location;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
|
||||
/* * the structure behind dc_array_t */
|
||||
#[derive(Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum dc_array_t {
|
||||
Locations(Vec<Location>),
|
||||
Uint(Vec<u32>),
|
||||
Locations(Vec<dc_location>),
|
||||
Uint(Vec<uintptr_t>),
|
||||
}
|
||||
|
||||
impl dc_array_t {
|
||||
@@ -19,15 +20,23 @@ impl dc_array_t {
|
||||
dc_array_t::Locations(Vec::with_capacity(capacity))
|
||||
}
|
||||
|
||||
pub fn add_id(&mut self, item: uint32_t) {
|
||||
pub fn into_raw(self) -> *mut Self {
|
||||
Box::into_raw(Box::new(self))
|
||||
}
|
||||
|
||||
pub fn add_uint(&mut self, item: uintptr_t) {
|
||||
if let Self::Uint(array) = self {
|
||||
array.push(item);
|
||||
} else {
|
||||
panic!("Attempt to add id to array of other type");
|
||||
panic!("Attempt to add uint to array of other type");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_location(&mut self, location: Location) {
|
||||
pub fn add_id(&mut self, item: uint32_t) {
|
||||
self.add_uint(item as uintptr_t);
|
||||
}
|
||||
|
||||
pub fn add_location(&mut self, location: dc_location) {
|
||||
if let Self::Locations(array) = self {
|
||||
array.push(location)
|
||||
} else {
|
||||
@@ -35,6 +44,14 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_uint(&self, index: usize) -> uintptr_t {
|
||||
if let Self::Uint(array) = self {
|
||||
array[index]
|
||||
} else {
|
||||
panic!("Attempt to get uint from array of other type");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self, index: usize) -> uint32_t {
|
||||
match self {
|
||||
Self::Locations(array) => array[index].location_id,
|
||||
@@ -42,7 +59,15 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_location(&self, index: usize) -> &Location {
|
||||
pub fn get_ptr(&self, index: size_t) -> *mut libc::c_void {
|
||||
if let Self::Uint(array) = self {
|
||||
array[index] as *mut libc::c_void
|
||||
} else {
|
||||
panic!("Not an array of pointers");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_location(&self, index: usize) -> &dc_location {
|
||||
if let Self::Locations(array) = self {
|
||||
&array[index]
|
||||
} else {
|
||||
@@ -50,6 +75,34 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_latitude(&self, index: usize) -> libc::c_double {
|
||||
self.get_location(index).latitude
|
||||
}
|
||||
|
||||
pub fn get_longitude(&self, index: size_t) -> libc::c_double {
|
||||
self.get_location(index).longitude
|
||||
}
|
||||
|
||||
pub fn get_accuracy(&self, index: size_t) -> libc::c_double {
|
||||
self.get_location(index).accuracy
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self, index: size_t) -> i64 {
|
||||
self.get_location(index).timestamp
|
||||
}
|
||||
|
||||
pub fn get_chat_id(&self, index: size_t) -> uint32_t {
|
||||
self.get_location(index).chat_id
|
||||
}
|
||||
|
||||
pub fn get_contact_id(&self, index: size_t) -> uint32_t {
|
||||
self.get_location(index).contact_id
|
||||
}
|
||||
|
||||
pub fn get_msg_id(&self, index: size_t) -> uint32_t {
|
||||
self.get_location(index).msg_id
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
Self::Locations(array) => array.is_empty(),
|
||||
@@ -72,7 +125,7 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_id(&self, needle: u32) -> Option<usize> {
|
||||
pub fn search_id(&self, needle: uintptr_t) -> Option<usize> {
|
||||
if let Self::Uint(array) = self {
|
||||
for (i, &u) in array.iter().enumerate() {
|
||||
if u == needle {
|
||||
@@ -92,72 +145,263 @@ impl dc_array_t {
|
||||
panic!("Attempt to sort array of something other than uints");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *const u32 {
|
||||
if let dc_array_t::Uint(v) = self {
|
||||
v.as_ptr()
|
||||
} else {
|
||||
panic!("Attempt to convert array of something other than uints to raw");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u32>> for dc_array_t {
|
||||
fn from(array: Vec<u32>) -> Self {
|
||||
dc_array_t::Uint(array)
|
||||
dc_array_t::Uint(array.iter().map(|&x| x as uintptr_t).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Location>> for dc_array_t {
|
||||
fn from(array: Vec<Location>) -> Self {
|
||||
impl From<Vec<dc_location>> for dc_array_t {
|
||||
fn from(array: Vec<dc_location>) -> Self {
|
||||
dc_array_t::Locations(array)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_unref(array: *mut dc_array_t) {
|
||||
assert!(!array.is_null());
|
||||
Box::from_raw(array);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_uint(array: *mut dc_array_t, item: uintptr_t) {
|
||||
assert!(!array.is_null());
|
||||
(*array).add_uint(item);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_id(array: *mut dc_array_t, item: uint32_t) {
|
||||
assert!(!array.is_null());
|
||||
(*array).add_id(item);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
|
||||
dc_array_add_uint(array, item as uintptr_t);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
|
||||
assert!(!array.is_null());
|
||||
(*array).len()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_uint(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_id(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_ptr(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_latitude(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_longitude(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_accuracy(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_timestamp(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_chat_id(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_contact_id(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
assert!(!array.is_null());
|
||||
(*array).get_msg_id(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let dc_array_t::Locations(v) = &*array {
|
||||
if let Some(s) = &v[index].marker {
|
||||
s.strdup()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
} else {
|
||||
panic!("Not an array of locations");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the independent-state of the location at the given index.
|
||||
* Independent locations do not belong to the track of the user.
|
||||
*
|
||||
* @memberof dc_array_t
|
||||
* @param array The array object.
|
||||
* @param index Index of the item. Must be between 0 and dc_array_get_cnt()-1.
|
||||
* @return 0=Location belongs to the track of the user,
|
||||
* 1=Location was reported independently.
|
||||
*/
|
||||
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let dc_array_t::Locations(v) = &*array {
|
||||
v[index].independent as libc::c_int
|
||||
} else {
|
||||
panic!("Attempt to get location independent field from array of something other than locations");
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_search_id(
|
||||
array: *const dc_array_t,
|
||||
needle: uint32_t,
|
||||
ret_index: *mut size_t,
|
||||
) -> bool {
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let Some(i) = (*array).search_id(needle as uintptr_t) {
|
||||
if !ret_index.is_null() {
|
||||
*ret_index = i
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let dc_array_t::Uint(v) = &*array {
|
||||
v.as_ptr()
|
||||
} else {
|
||||
panic!("Attempt to convert array of something other than uints to raw");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
|
||||
dc_array_t::new(initsize).into_raw()
|
||||
}
|
||||
|
||||
pub fn dc_array_new_locations(initsize: size_t) -> *mut dc_array_t {
|
||||
dc_array_t::new_locations(initsize).into_raw()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_empty(array: *mut dc_array_t) {
|
||||
assert!(!array.is_null());
|
||||
|
||||
(*array).clear()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
|
||||
assert!(!array.is_null());
|
||||
|
||||
(*array).clone().into_raw()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_string(
|
||||
array: *const dc_array_t,
|
||||
sep: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
assert!(!array.is_null());
|
||||
assert!(!sep.is_null());
|
||||
|
||||
if let dc_array_t::Uint(v) = &*array {
|
||||
let cnt = v.len();
|
||||
let sep = as_str(sep);
|
||||
|
||||
let res = v
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(String::with_capacity(2 * cnt), |res, (i, n)| {
|
||||
if i == 0 {
|
||||
res + &n.to_string()
|
||||
} else {
|
||||
res + sep + &n.to_string()
|
||||
}
|
||||
});
|
||||
res.strdup()
|
||||
} else {
|
||||
panic!("Attempt to get string from array of other type");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::x::*;
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn test_dc_array() {
|
||||
let mut arr = dc_array_t::new(7);
|
||||
assert!(arr.is_empty());
|
||||
unsafe {
|
||||
let arr = dc_array_new(7 as size_t);
|
||||
assert_eq!(dc_array_get_cnt(arr), 0);
|
||||
|
||||
for i in 0..1000 {
|
||||
arr.add_id(i + 2);
|
||||
for i in 0..1000 {
|
||||
dc_array_add_id(arr, (i + 2) as uint32_t);
|
||||
}
|
||||
|
||||
assert_eq!(dc_array_get_cnt(arr), 1000);
|
||||
|
||||
for i in 0..1000 {
|
||||
assert_eq!(
|
||||
dc_array_get_id(arr, i as size_t),
|
||||
(i + 1i32 * 2i32) as libc::c_uint
|
||||
);
|
||||
}
|
||||
|
||||
dc_array_empty(arr);
|
||||
|
||||
assert_eq!(dc_array_get_cnt(arr), 0);
|
||||
|
||||
dc_array_add_id(arr, 13 as uint32_t);
|
||||
dc_array_add_id(arr, 7 as uint32_t);
|
||||
dc_array_add_id(arr, 666 as uint32_t);
|
||||
dc_array_add_id(arr, 0 as uint32_t);
|
||||
dc_array_add_id(arr, 5000 as uint32_t);
|
||||
|
||||
(*arr).sort_ids();
|
||||
|
||||
assert_eq!(dc_array_get_id(arr, 0 as size_t), 0);
|
||||
assert_eq!(dc_array_get_id(arr, 1 as size_t), 7);
|
||||
assert_eq!(dc_array_get_id(arr, 2 as size_t), 13);
|
||||
assert_eq!(dc_array_get_id(arr, 3 as size_t), 666);
|
||||
|
||||
let str = dc_array_get_string(arr, b"-\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
|
||||
"0-7-13-666-5000"
|
||||
);
|
||||
free(str as *mut libc::c_void);
|
||||
|
||||
dc_array_unref(arr);
|
||||
}
|
||||
|
||||
assert_eq!(arr.len(), 1000);
|
||||
|
||||
for i in 0..1000 {
|
||||
assert_eq!(arr.get_id(i), (i + 2) as u32);
|
||||
}
|
||||
|
||||
arr.clear();
|
||||
|
||||
assert!(arr.is_empty());
|
||||
|
||||
arr.add_id(13);
|
||||
arr.add_id(7);
|
||||
arr.add_id(666);
|
||||
arr.add_id(0);
|
||||
arr.add_id(5000);
|
||||
|
||||
arr.sort_ids();
|
||||
|
||||
assert_eq!(arr.get_id(0), 0);
|
||||
assert_eq!(arr.get_id(1), 7);
|
||||
assert_eq!(arr.get_id(2), 13);
|
||||
assert_eq!(arr.get_id(3), 666);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_dc_array_out_of_bounds() {
|
||||
let mut arr = dc_array_t::new(7);
|
||||
let arr = dc_array_new(7);
|
||||
for i in 0..1000 {
|
||||
arr.add_id(i + 2);
|
||||
unsafe { dc_array_add_id(arr, (i + 2) as uint32_t) };
|
||||
}
|
||||
arr.get_id(1000);
|
||||
unsafe { dc_array_get_id(arr, 1000) };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
2259
src/dc_chat.rs
Normal file
2259
src/dc_chat.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,36 +2,74 @@ use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::context::Context;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_saxparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee;
|
||||
use crate::imap::*;
|
||||
use crate::job::*;
|
||||
use crate::oauth2::*;
|
||||
use crate::param::Params;
|
||||
use crate::types::*;
|
||||
|
||||
mod auto_outlook;
|
||||
use auto_outlook::outlk_autodiscover;
|
||||
mod auto_mozilla;
|
||||
use auto_mozilla::moz_autoconfigure;
|
||||
use crate::x::*;
|
||||
|
||||
macro_rules! progress {
|
||||
($context:tt, $progress:expr) => {
|
||||
assert!(
|
||||
$progress >= 0 && $progress <= 1000,
|
||||
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
|
||||
);
|
||||
$context.call_cb(
|
||||
Event::CONFIGURE_PROGRESS,
|
||||
$progress as uintptr_t,
|
||||
(if $progress < 1 {
|
||||
1
|
||||
} else if $progress > 1000 {
|
||||
1000
|
||||
} else {
|
||||
$progress
|
||||
}) as uintptr_t,
|
||||
0 as uintptr_t,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* Configure folders
|
||||
******************************************************************************/
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct dc_imapfolder_t {
|
||||
pub name_to_select: *mut libc::c_char,
|
||||
pub name_utf8: *mut libc::c_char,
|
||||
pub meaning: libc::c_int,
|
||||
}
|
||||
/* ******************************************************************************
|
||||
* Thunderbird's Autoconfigure
|
||||
******************************************************************************/
|
||||
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
|
||||
#[repr(C)]
|
||||
pub struct moz_autoconfigure_t<'a> {
|
||||
pub in_0: &'a dc_loginparam_t,
|
||||
pub in_emaildomain: *mut libc::c_char,
|
||||
pub in_emaillocalpart: *mut libc::c_char,
|
||||
pub out: dc_loginparam_t,
|
||||
pub out_imap_set: libc::c_int,
|
||||
pub out_smtp_set: libc::c_int,
|
||||
pub tag_server: libc::c_int,
|
||||
pub tag_config: libc::c_int,
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* Outlook's Autodiscover
|
||||
******************************************************************************/
|
||||
#[repr(C)]
|
||||
pub struct outlk_autodiscover_t<'a> {
|
||||
pub in_0: &'a dc_loginparam_t,
|
||||
pub out: dc_loginparam_t,
|
||||
pub out_imap_set: libc::c_int,
|
||||
pub out_smtp_set: libc::c_int,
|
||||
pub tag_config: libc::c_int,
|
||||
pub config: [*mut libc::c_char; 6],
|
||||
pub redirect: *mut libc::c_char,
|
||||
}
|
||||
// connect
|
||||
pub unsafe fn configure(context: &Context) {
|
||||
pub unsafe fn dc_configure(context: &Context) {
|
||||
if 0 != dc_has_ongoing(context) {
|
||||
warn!(
|
||||
context,
|
||||
@@ -39,11 +77,11 @@ pub unsafe fn configure(context: &Context) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
job_kill_action(context, Action::ConfigureImap);
|
||||
job_add(context, Action::ConfigureImap, 0, Params::new(), 0);
|
||||
dc_job_kill_action(context, 900);
|
||||
dc_job_add(context, 900, 0, Params::new(), 0);
|
||||
}
|
||||
|
||||
unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
|
||||
pub unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
|
||||
let s_a = context.running_state.clone();
|
||||
let s = s_a.read().unwrap();
|
||||
|
||||
@@ -80,7 +118,7 @@ pub fn dc_stop_ongoing_process(context: &Context) {
|
||||
|
||||
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
|
||||
#[allow(non_snake_case, unused_must_use)]
|
||||
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_job_t) {
|
||||
let flags: libc::c_int;
|
||||
let mut success = false;
|
||||
let mut imap_connected_here = false;
|
||||
@@ -113,7 +151,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
let s = s_a.read().unwrap();
|
||||
|
||||
if !s.shall_stop_ongoing {
|
||||
progress!(context, 1);
|
||||
progress!(context, 0);
|
||||
|
||||
let mut param = dc_loginparam_read(context, &context.sql, "");
|
||||
if param.addr.is_empty() {
|
||||
@@ -149,10 +187,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
ok_to_continue0 = true;
|
||||
}
|
||||
if ok_to_continue0 {
|
||||
let parsed: addr::Result<addr::Email> = param.addr.parse();
|
||||
let mut ok_to_continue7 = false;
|
||||
if let Ok(parsed) = param.addr.parse() {
|
||||
let parsed: EmailAddress = parsed;
|
||||
let param_domain = parsed.domain;
|
||||
if parsed.is_err() {
|
||||
error!(context, 0, "Bad email-address.");
|
||||
} else {
|
||||
let parsed = parsed.unwrap();
|
||||
let param_domain = parsed.host();
|
||||
let param_addr_urlencoded =
|
||||
utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string();
|
||||
|
||||
@@ -589,7 +630,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
.ok();
|
||||
if !s.shall_stop_ongoing {
|
||||
progress!(context, 920);
|
||||
e2ee::ensure_secret_key_exists(context);
|
||||
dc_ensure_secret_key_exists(context);
|
||||
success = true;
|
||||
info!(
|
||||
context,
|
||||
@@ -606,8 +647,6 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(context, 0, "Bad email-address.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -653,6 +692,410 @@ pub unsafe fn dc_free_ongoing(context: &Context) {
|
||||
s.shall_stop_ongoing = true;
|
||||
}
|
||||
|
||||
unsafe fn moz_autoconfigure(
|
||||
context: &Context,
|
||||
url: &str,
|
||||
param_in: &dc_loginparam_t,
|
||||
) -> Option<dc_loginparam_t> {
|
||||
let mut moz_ac = moz_autoconfigure_t {
|
||||
in_0: param_in,
|
||||
in_emaildomain: std::ptr::null_mut(),
|
||||
in_emaillocalpart: std::ptr::null_mut(),
|
||||
out: dc_loginparam_new(),
|
||||
out_imap_set: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_server: 0,
|
||||
tag_config: 0,
|
||||
};
|
||||
|
||||
let url_c = url.strdup();
|
||||
let xml_raw = read_autoconf_file(context, url_c);
|
||||
free(url_c as *mut libc::c_void);
|
||||
if xml_raw.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
moz_ac.in_emaillocalpart = param_in.addr.strdup();
|
||||
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
|
||||
|
||||
if p.is_null() {
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
*p = 0 as libc::c_char;
|
||||
moz_ac.in_emaildomain = dc_strdup(p.offset(1isize));
|
||||
let mut saxparser = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut moz_ac as *mut moz_autoconfigure_t as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(moz_autoconfigure_starttag_cb),
|
||||
Some(moz_autoconfigure_endtag_cb),
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(moz_autoconfigure_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, xml_raw);
|
||||
|
||||
if moz_ac.out.mail_server.is_empty()
|
||||
|| moz_ac.out.mail_port == 0
|
||||
|| moz_ac.out.send_server.is_empty()
|
||||
|| moz_ac.out.send_port == 0
|
||||
{
|
||||
let r = dc_loginparam_get_readable(&moz_ac.out);
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
free(moz_ac.in_emaildomain as *mut libc::c_void);
|
||||
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
|
||||
Some(moz_ac.out)
|
||||
}
|
||||
|
||||
unsafe fn moz_autoconfigure_text_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
text: *const libc::c_char,
|
||||
_len: libc::c_int,
|
||||
) {
|
||||
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
|
||||
let mut val: *mut libc::c_char = dc_strdup(text);
|
||||
dc_trim(val);
|
||||
let addr = (*moz_ac).in_0.addr.strdup();
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char,
|
||||
addr,
|
||||
);
|
||||
free(addr as *mut libc::c_void);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"%EMAILLOCALPART%\x00" as *const u8 as *const libc::c_char,
|
||||
(*moz_ac).in_emaillocalpart,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"%EMAILDOMAIN%\x00" as *const u8 as *const libc::c_char,
|
||||
(*moz_ac).in_emaildomain,
|
||||
);
|
||||
if (*moz_ac).tag_server == 1 {
|
||||
match (*moz_ac).tag_config {
|
||||
10 => {
|
||||
(*moz_ac).out.mail_server = to_string(val);
|
||||
val = 0 as *mut libc::c_char
|
||||
}
|
||||
11 => (*moz_ac).out.mail_port = dc_atoi_null_is_0(val),
|
||||
12 => {
|
||||
(*moz_ac).out.mail_user = to_string(val);
|
||||
val = 0 as *mut libc::c_char
|
||||
}
|
||||
13 => {
|
||||
if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).out.server_flags |= 0x200
|
||||
}
|
||||
if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).out.server_flags |= 0x100
|
||||
}
|
||||
if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).out.server_flags |= 0x400
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if (*moz_ac).tag_server == 2 {
|
||||
match (*moz_ac).tag_config {
|
||||
10 => {
|
||||
(*moz_ac).out.send_server = to_string(val);
|
||||
val = 0 as *mut libc::c_char
|
||||
}
|
||||
11 => (*moz_ac).out.send_port = as_str(val).parse().unwrap_or_default(),
|
||||
12 => {
|
||||
(*moz_ac).out.send_user = to_string(val);
|
||||
val = 0 as *mut libc::c_char
|
||||
}
|
||||
13 => {
|
||||
if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).out.server_flags |= 0x20000
|
||||
}
|
||||
if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).out.server_flags |= 0x10000
|
||||
}
|
||||
if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).out.server_flags |= 0x40000
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
free(val as *mut libc::c_void);
|
||||
}
|
||||
unsafe fn moz_autoconfigure_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
|
||||
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
|
||||
if strcmp(
|
||||
tag,
|
||||
b"incomingserver\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
(*moz_ac).tag_server = 0;
|
||||
(*moz_ac).tag_config = 0;
|
||||
(*moz_ac).out_imap_set = 1
|
||||
} else if strcmp(
|
||||
tag,
|
||||
b"outgoingserver\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
(*moz_ac).tag_server = 0;
|
||||
(*moz_ac).tag_config = 0;
|
||||
(*moz_ac).out_smtp_set = 1
|
||||
} else {
|
||||
(*moz_ac).tag_config = 0
|
||||
};
|
||||
}
|
||||
unsafe fn moz_autoconfigure_starttag_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
tag: *const libc::c_char,
|
||||
attr: *mut *mut libc::c_char,
|
||||
) {
|
||||
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
|
||||
let mut p1: *const libc::c_char = 0 as *const libc::c_char;
|
||||
if strcmp(
|
||||
tag,
|
||||
b"incomingserver\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
(*moz_ac).tag_server = if (*moz_ac).out_imap_set == 0
|
||||
&& {
|
||||
p1 = dc_attr_find(attr, b"type\x00" as *const u8 as *const libc::c_char);
|
||||
!p1.is_null()
|
||||
}
|
||||
&& strcasecmp(p1, b"imap\x00" as *const u8 as *const libc::c_char) == 0
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
(*moz_ac).tag_config = 0
|
||||
} else if strcmp(
|
||||
tag,
|
||||
b"outgoingserver\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
(*moz_ac).tag_server = if (*moz_ac).out_smtp_set == 0 { 2 } else { 0 };
|
||||
(*moz_ac).tag_config = 0
|
||||
} else if strcmp(tag, b"hostname\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).tag_config = 10
|
||||
} else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).tag_config = 11
|
||||
} else if strcmp(tag, b"sockettype\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).tag_config = 13
|
||||
} else if strcmp(tag, b"username\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*moz_ac).tag_config = 12
|
||||
};
|
||||
}
|
||||
|
||||
fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc::c_char {
|
||||
info!(context, 0, "Testing {} ...", to_string(url));
|
||||
|
||||
match reqwest::Client::new()
|
||||
.get(as_str(url))
|
||||
.send()
|
||||
.and_then(|mut res| res.text())
|
||||
{
|
||||
Ok(res) => unsafe { res.strdup() },
|
||||
Err(_err) => {
|
||||
info!(context, 0, "Can\'t read file.",);
|
||||
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn outlk_autodiscover(
|
||||
context: &Context,
|
||||
url__: &str,
|
||||
param_in: &dc_loginparam_t,
|
||||
) -> Option<dc_loginparam_t> {
|
||||
let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut url = url__.strdup();
|
||||
let mut outlk_ad = outlk_autodiscover_t {
|
||||
in_0: param_in,
|
||||
out: dc_loginparam_new(),
|
||||
out_imap_set: 0,
|
||||
out_smtp_set: 0,
|
||||
tag_config: 0,
|
||||
config: [0 as *mut libc::c_char; 6],
|
||||
redirect: 0 as *mut libc::c_char,
|
||||
};
|
||||
let ok_to_continue;
|
||||
let mut i = 0;
|
||||
loop {
|
||||
if !(i < 10) {
|
||||
ok_to_continue = true;
|
||||
break;
|
||||
}
|
||||
memset(
|
||||
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
|
||||
0,
|
||||
::std::mem::size_of::<outlk_autodiscover_t>(),
|
||||
);
|
||||
xml_raw = read_autoconf_file(context, url);
|
||||
if xml_raw.is_null() {
|
||||
ok_to_continue = false;
|
||||
break;
|
||||
}
|
||||
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(outlk_autodiscover_starttag_cb),
|
||||
Some(outlk_autodiscover_endtag_cb),
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(outlk_autodiscover_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, xml_raw);
|
||||
if !(!outlk_ad.config[5usize].is_null()
|
||||
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
|
||||
{
|
||||
ok_to_continue = true;
|
||||
break;
|
||||
}
|
||||
free(url as *mut libc::c_void);
|
||||
url = dc_strdup(outlk_ad.config[5usize]);
|
||||
|
||||
outlk_clean_config(&mut outlk_ad);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
xml_raw = 0 as *mut libc::c_char;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if ok_to_continue {
|
||||
if outlk_ad.out.mail_server.is_empty()
|
||||
|| outlk_ad.out.mail_port == 0
|
||||
|| outlk_ad.out.send_server.is_empty()
|
||||
|| outlk_ad.out.send_port == 0
|
||||
{
|
||||
let r = dc_loginparam_get_readable(&outlk_ad.out);
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
free(url as *mut libc::c_void);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
outlk_clean_config(&mut outlk_ad);
|
||||
|
||||
return None;
|
||||
}
|
||||
}
|
||||
free(url as *mut libc::c_void);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
outlk_clean_config(&mut outlk_ad);
|
||||
Some(outlk_ad.out)
|
||||
}
|
||||
|
||||
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
|
||||
let mut i: libc::c_int = 0;
|
||||
while i < 6 {
|
||||
free((*outlk_ad).config[i as usize] as *mut libc::c_void);
|
||||
(*outlk_ad).config[i as usize] = 0 as *mut libc::c_char;
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
unsafe fn outlk_autodiscover_text_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
text: *const libc::c_char,
|
||||
_len: libc::c_int,
|
||||
) {
|
||||
let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t;
|
||||
let val: *mut libc::c_char = dc_strdup(text);
|
||||
dc_trim(val);
|
||||
free((*outlk_ad).config[(*outlk_ad).tag_config as usize] as *mut libc::c_void);
|
||||
(*outlk_ad).config[(*outlk_ad).tag_config as usize] = val;
|
||||
}
|
||||
unsafe fn outlk_autodiscover_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
|
||||
let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t;
|
||||
if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
if !(*outlk_ad).config[1usize].is_null() {
|
||||
let port: libc::c_int = dc_atoi_null_is_0((*outlk_ad).config[3usize]);
|
||||
let ssl_on: libc::c_int = (!(*outlk_ad).config[4usize].is_null()
|
||||
&& strcasecmp(
|
||||
(*outlk_ad).config[4usize],
|
||||
b"on\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0) as libc::c_int;
|
||||
let ssl_off: libc::c_int = (!(*outlk_ad).config[4usize].is_null()
|
||||
&& strcasecmp(
|
||||
(*outlk_ad).config[4usize],
|
||||
b"off\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0) as libc::c_int;
|
||||
if strcasecmp(
|
||||
(*outlk_ad).config[1usize],
|
||||
b"imap\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
&& (*outlk_ad).out_imap_set == 0
|
||||
{
|
||||
(*outlk_ad).out.mail_server = to_string((*outlk_ad).config[2]);
|
||||
(*outlk_ad).out.mail_port = port;
|
||||
if 0 != ssl_on {
|
||||
(*outlk_ad).out.server_flags |= 0x200
|
||||
} else if 0 != ssl_off {
|
||||
(*outlk_ad).out.server_flags |= 0x400
|
||||
}
|
||||
(*outlk_ad).out_imap_set = 1
|
||||
} else if strcasecmp(
|
||||
(*outlk_ad).config[1usize],
|
||||
b"smtp\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
&& (*outlk_ad).out_smtp_set == 0
|
||||
{
|
||||
(*outlk_ad).out.send_server = to_string((*outlk_ad).config[2]);
|
||||
(*outlk_ad).out.send_port = port;
|
||||
if 0 != ssl_on {
|
||||
(*outlk_ad).out.server_flags |= 0x20000
|
||||
} else if 0 != ssl_off {
|
||||
(*outlk_ad).out.server_flags |= 0x40000
|
||||
}
|
||||
(*outlk_ad).out_smtp_set = 1
|
||||
}
|
||||
}
|
||||
outlk_clean_config(outlk_ad);
|
||||
}
|
||||
(*outlk_ad).tag_config = 0;
|
||||
}
|
||||
|
||||
unsafe fn outlk_autodiscover_starttag_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
tag: *const libc::c_char,
|
||||
_attr: *mut *mut libc::c_char,
|
||||
) {
|
||||
let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t;
|
||||
if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
outlk_clean_config(outlk_ad);
|
||||
} else if strcmp(tag, b"type\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*outlk_ad).tag_config = 1
|
||||
} else if strcmp(tag, b"server\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*outlk_ad).tag_config = 2
|
||||
} else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*outlk_ad).tag_config = 3
|
||||
} else if strcmp(tag, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*outlk_ad).tag_config = 4
|
||||
} else if strcmp(tag, b"redirecturl\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*outlk_ad).tag_config = 5
|
||||
};
|
||||
}
|
||||
pub unsafe fn dc_alloc_ongoing(context: &Context) -> libc::c_int {
|
||||
if 0 != dc_has_ongoing(context) {
|
||||
warn!(
|
||||
@@ -693,20 +1136,3 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_
|
||||
|
||||
ret_connected
|
||||
}
|
||||
|
||||
pub fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc::c_char {
|
||||
info!(context, 0, "Testing {} ...", to_string(url));
|
||||
|
||||
match reqwest::Client::new()
|
||||
.get(as_str(url))
|
||||
.send()
|
||||
.and_then(|mut res| res.text())
|
||||
{
|
||||
Ok(res) => unsafe { res.strdup() },
|
||||
Err(_err) => {
|
||||
info!(context, 0, "Can\'t read file.",);
|
||||
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
136
src/dc_dehtml.rs
136
src/dc_dehtml.rs
@@ -1,6 +1,8 @@
|
||||
use lazy_static::lazy_static;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::dc_saxparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref LINE_RE: regex::Regex = regex::Regex::new(r"(\r?\n)+").unwrap();
|
||||
@@ -21,79 +23,67 @@ enum AddText {
|
||||
|
||||
// dc_dehtml() returns way too many lineends; however, an optimisation on this issue is not needed as
|
||||
// the lineends are typically remove in further processing by the caller
|
||||
pub fn dc_dehtml(buf_terminated: &str) -> String {
|
||||
let buf_terminated = buf_terminated.trim();
|
||||
|
||||
if buf_terminated.is_empty() {
|
||||
return "".into();
|
||||
pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char {
|
||||
dc_trim(buf_terminated);
|
||||
if *buf_terminated.offset(0isize) as libc::c_int == 0i32 {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
|
||||
let mut dehtml = Dehtml {
|
||||
strbuilder: String::with_capacity(buf_terminated.len()),
|
||||
strbuilder: String::with_capacity(strlen(buf_terminated)),
|
||||
add_text: AddText::YesRemoveLineEnds,
|
||||
last_href: None,
|
||||
};
|
||||
let mut saxparser = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut dehtml as *mut Dehtml as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(dehtml_starttag_cb),
|
||||
Some(dehtml_endtag_cb),
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, buf_terminated);
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(buf_terminated);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
dehtml_starttag_cb(e, &mut dehtml, &reader)
|
||||
}
|
||||
Ok(quick_xml::events::Event::End(ref e)) => dehtml_endtag_cb(e, &mut dehtml),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => dehtml_text_cb(e, &mut dehtml),
|
||||
Ok(quick_xml::events::Event::CData(ref e)) => dehtml_cdata_cb(e, &mut dehtml),
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Parse html error: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
dehtml.strbuilder
|
||||
dehtml.strbuilder.strdup()
|
||||
}
|
||||
|
||||
fn dehtml_text_cb(event: &BytesText, dehtml: &mut Dehtml) {
|
||||
unsafe fn dehtml_text_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
text: *const libc::c_char,
|
||||
_len: libc::c_int,
|
||||
) {
|
||||
let dehtml = &mut *(userdata as *mut Dehtml);
|
||||
|
||||
if dehtml.add_text == AddText::YesPreserveLineEnds
|
||||
|| dehtml.add_text == AddText::YesRemoveLineEnds
|
||||
{
|
||||
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
|
||||
let last_added = std::ffi::CStr::from_ptr(text)
|
||||
.to_str()
|
||||
.expect("invalid utf8");
|
||||
// TODO: why does len does not match?
|
||||
// assert_eq!(last_added.len(), len as usize);
|
||||
|
||||
if dehtml.add_text == AddText::YesRemoveLineEnds {
|
||||
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
|
||||
dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref();
|
||||
} else {
|
||||
dehtml.strbuilder += &last_added;
|
||||
dehtml.strbuilder += last_added.as_ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dehtml_cdata_cb(event: &BytesText, dehtml: &mut Dehtml) {
|
||||
if dehtml.add_text == AddText::YesPreserveLineEnds
|
||||
|| dehtml.add_text == AddText::YesRemoveLineEnds
|
||||
{
|
||||
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
|
||||
unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
|
||||
let mut dehtml = &mut *(userdata as *mut Dehtml);
|
||||
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
|
||||
|
||||
if dehtml.add_text == AddText::YesRemoveLineEnds {
|
||||
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
|
||||
} else {
|
||||
dehtml.strbuilder += &last_added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
match tag.as_str() {
|
||||
match tag.as_ref() {
|
||||
"p" | "div" | "table" | "td" | "style" | "script" | "title" | "pre" => {
|
||||
dehtml.strbuilder += "\n\n";
|
||||
dehtml.add_text = AddText::YesRemoveLineEnds;
|
||||
@@ -115,14 +105,15 @@ fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) {
|
||||
}
|
||||
}
|
||||
|
||||
fn dehtml_starttag_cb<B: std::io::BufRead>(
|
||||
event: &BytesStart,
|
||||
dehtml: &mut Dehtml,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
unsafe fn dehtml_starttag_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
tag: *const libc::c_char,
|
||||
attr: *mut *mut libc::c_char,
|
||||
) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
let mut dehtml = &mut *(userdata as *mut Dehtml);
|
||||
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
|
||||
|
||||
match tag.as_str() {
|
||||
match tag.as_ref() {
|
||||
"p" | "div" | "table" | "td" => {
|
||||
dehtml.strbuilder += "\n\n";
|
||||
dehtml.add_text = AddText::YesRemoveLineEnds;
|
||||
@@ -139,21 +130,14 @@ fn dehtml_starttag_cb<B: std::io::BufRead>(
|
||||
dehtml.add_text = AddText::YesPreserveLineEnds;
|
||||
}
|
||||
"a" => {
|
||||
if let Some(href) = event.html_attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "href")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
let href = href
|
||||
.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
|
||||
if !href.is_empty() {
|
||||
dehtml.last_href = Some(href);
|
||||
dehtml.strbuilder += "[";
|
||||
}
|
||||
let text_c = std::ffi::CStr::from_ptr(dc_attr_find(
|
||||
attr,
|
||||
b"href\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
let text_r = text_c.to_str().expect("invalid utf8");
|
||||
if !text_r.is_empty() {
|
||||
dehtml.last_href = Some(text_r.to_string());
|
||||
dehtml.strbuilder += "[";
|
||||
}
|
||||
}
|
||||
"b" | "strong" => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
268
src/dc_imex.rs
268
src/dc_imex.rs
@@ -1,22 +1,21 @@
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use mmime::mailmime_content::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::chat;
|
||||
use crate::config::Config;
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee;
|
||||
use crate::error::*;
|
||||
use crate::job::*;
|
||||
use crate::key::*;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::pgp::*;
|
||||
use crate::sql::{self, Sql};
|
||||
@@ -44,8 +43,8 @@ pub unsafe fn dc_imex(
|
||||
param.set(Param::Arg2, as_str(param2));
|
||||
}
|
||||
|
||||
job_kill_action(context, Action::ImexImap);
|
||||
job_add(context, Action::ImexImap, 0, param, 0);
|
||||
dc_job_kill_action(context, 910);
|
||||
dc_job_add(context, 910, 0, param, 0);
|
||||
}
|
||||
|
||||
/// Returns the filename of the backup if found, nullptr otherwise.
|
||||
@@ -62,7 +61,7 @@ pub unsafe fn dc_imex_has_backup(
|
||||
"Backup check: Cannot open directory \"{}\".\x00",
|
||||
dir_name.display(),
|
||||
);
|
||||
return ptr::null_mut();
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
let mut newest_backup_time = 0;
|
||||
let mut newest_backup_path: Option<std::path::PathBuf> = None;
|
||||
@@ -101,8 +100,8 @@ pub unsafe fn dc_imex_has_backup(
|
||||
}
|
||||
|
||||
pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
let mut setup_file_name: *mut libc::c_char = ptr::null_mut();
|
||||
let mut msg: Message;
|
||||
let mut setup_file_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
if dc_alloc_ongoing(context) == 0 {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
@@ -138,15 +137,17 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
setup_file_content_c.as_bytes().len(),
|
||||
))
|
||||
{
|
||||
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
|
||||
let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t);
|
||||
if !(chat_id == 0i32 as libc::c_uint) {
|
||||
msg = dc_msg_new_untyped(context);
|
||||
msg.type_0 = Viewtype::File;
|
||||
msg.param.set(Param::File, as_str(setup_file_name));
|
||||
(*msg).type_0 = Viewtype::File;
|
||||
(*msg).param.set(Param::File, as_str(setup_file_name));
|
||||
|
||||
msg.param
|
||||
(*msg)
|
||||
.param
|
||||
.set(Param::MimeType, "application/autocrypt-setup");
|
||||
msg.param.set_int(Param::Cmd, 6);
|
||||
msg.param.set_int(Param::ForcePlaintext, 2);
|
||||
(*msg).param.set_int(Param::Cmd, 6);
|
||||
(*msg).param.set_int(Param::ForcePlaintext, 2);
|
||||
|
||||
if !context
|
||||
.running_state
|
||||
@@ -155,7 +156,10 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
.unwrap()
|
||||
.shall_stop_ongoing
|
||||
{
|
||||
if let Ok(msg_id) = chat::send_msg(context, chat_id, &mut msg) {
|
||||
let msg_id = dc_send_msg(context, chat_id, msg);
|
||||
if msg_id != 0 {
|
||||
dc_msg_unref(msg);
|
||||
msg = 0 as *mut dc_msg_t;
|
||||
info!(context, 0, "Wait for setup message being sent ...",);
|
||||
loop {
|
||||
if context
|
||||
@@ -168,12 +172,13 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
if let Ok(msg) = dc_get_msg(context, msg_id) {
|
||||
if 0 != dc_msg_is_sent(&msg) {
|
||||
info!(context, 0, "... setup message sent.",);
|
||||
break;
|
||||
}
|
||||
msg = dc_get_msg(context, msg_id);
|
||||
if 0 != dc_msg_is_sent(msg) {
|
||||
info!(context, 0, "... setup message sent.",);
|
||||
break;
|
||||
}
|
||||
dc_msg_unref(msg);
|
||||
msg = 0 as *mut dc_msg_t
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,6 +188,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
}
|
||||
}
|
||||
free(setup_file_name as *mut libc::c_void);
|
||||
dc_msg_unref(msg);
|
||||
dc_free_ongoing(context);
|
||||
|
||||
setup_code.strdup()
|
||||
@@ -196,7 +202,7 @@ pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<Strin
|
||||
passphrase.len() >= 2,
|
||||
"Passphrase must be at least 2 chars long."
|
||||
);
|
||||
let self_addr = e2ee::ensure_secret_key_exists(context)?;
|
||||
let self_addr = dc_ensure_secret_key_exists(context)?;
|
||||
let private_key = Key::from_self_private(context, self_addr, &context.sql)
|
||||
.ok_or(format_err!("Failed to get private key."))?;
|
||||
let ac_headers = match context
|
||||
@@ -279,17 +285,18 @@ pub unsafe fn dc_continue_key_transfer(
|
||||
setup_code: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut filename: *mut libc::c_char = ptr::null_mut();
|
||||
let mut filecontent: *mut libc::c_char = ptr::null_mut();
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut filecontent: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut filebytes: size_t = 0i32 as size_t;
|
||||
let mut armored_key: *mut libc::c_char = ptr::null_mut();
|
||||
let mut norm_sc: *mut libc::c_char = ptr::null_mut();
|
||||
let mut armored_key: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut norm_sc: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) {
|
||||
let msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_err()
|
||||
|| !dc_msg_is_setupmessage(msg.as_ref().unwrap())
|
||||
msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_null()
|
||||
|| !dc_msg_is_setupmessage(msg)
|
||||
|| {
|
||||
filename = dc_msg_get_file(msg.as_ref().unwrap());
|
||||
filename = dc_msg_get_file(msg);
|
||||
filename.is_null()
|
||||
}
|
||||
|| *filename.offset(0isize) as libc::c_int == 0i32
|
||||
@@ -325,6 +332,7 @@ pub unsafe fn dc_continue_key_transfer(
|
||||
free(armored_key as *mut libc::c_void);
|
||||
free(filecontent as *mut libc::c_void);
|
||||
free(filename as *mut libc::c_void);
|
||||
dc_msg_unref(msg);
|
||||
free(norm_sc as *mut libc::c_void);
|
||||
|
||||
success
|
||||
@@ -415,19 +423,19 @@ pub unsafe fn dc_decrypt_setup_file(
|
||||
filecontent: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
let fc_buf: *mut libc::c_char;
|
||||
let mut fc_headerline: *const libc::c_char = ptr::null();
|
||||
let mut fc_base64: *const libc::c_char = ptr::null();
|
||||
let mut binary: *mut libc::c_char = ptr::null_mut();
|
||||
let mut fc_headerline: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut fc_base64: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut binary: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut binary_bytes: size_t = 0i32 as size_t;
|
||||
let mut indx: size_t = 0i32 as size_t;
|
||||
|
||||
let mut payload: *mut libc::c_char = ptr::null_mut();
|
||||
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
fc_buf = dc_strdup(filecontent);
|
||||
if dc_split_armored_data(
|
||||
fc_buf,
|
||||
&mut fc_headerline,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut fc_base64,
|
||||
) && !fc_headerline.is_null()
|
||||
&& strcmp(
|
||||
@@ -471,7 +479,7 @@ pub unsafe fn dc_normalize_setup_code(
|
||||
in_0: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
if in_0.is_null() {
|
||||
return ptr::null_mut();
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
let mut out = String::new();
|
||||
let mut outlen;
|
||||
@@ -499,7 +507,7 @@ pub unsafe fn dc_normalize_setup_code(
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) {
|
||||
let mut ok_to_continue = true;
|
||||
let mut success: libc::c_int = 0;
|
||||
let mut ongoing_allocated_here: libc::c_int = 0;
|
||||
@@ -507,10 +515,10 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
|
||||
if !(0 == dc_alloc_ongoing(context)) {
|
||||
ongoing_allocated_here = 1;
|
||||
what = job.param.get_int(Param::Cmd).unwrap_or_default();
|
||||
let param1_s = job.param.get(Param::Arg).unwrap_or_default();
|
||||
what = (*job).param.get_int(Param::Cmd).unwrap_or_default();
|
||||
let param1_s = (*job).param.get(Param::Arg).unwrap_or_default();
|
||||
let param1 = CString::yolo(param1_s);
|
||||
let _param2 = CString::yolo(job.param.get(Param::Arg2).unwrap_or_default());
|
||||
let _param2 = CString::yolo((*job).param.get(Param::Arg2).unwrap_or_default());
|
||||
|
||||
if strlen(param1.as_ptr()) == 0 {
|
||||
error!(context, 0, "No Import/export dir/file given.",);
|
||||
@@ -522,7 +530,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
} else {
|
||||
if what == 1 || what == 11 {
|
||||
/* before we export anything, make sure the private key exists */
|
||||
if e2ee::ensure_secret_key_exists(context).is_err() {
|
||||
if dc_ensure_secret_key_exists(context).is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
@@ -588,10 +596,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
0,
|
||||
"Import \"{}\" to \"{}\".",
|
||||
as_str(backup_to_import),
|
||||
context
|
||||
.get_dbfile()
|
||||
.as_ref()
|
||||
.map_or("<<None>>", |p| p.to_str().unwrap())
|
||||
as_str(context.get_dbfile()),
|
||||
);
|
||||
|
||||
if 0 != dc_is_configured(context) {
|
||||
@@ -599,8 +604,8 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
return 0;
|
||||
}
|
||||
&context.sql.close(&context);
|
||||
dc_delete_file(context, context.get_dbfile().unwrap());
|
||||
if dc_file_exist(context, context.get_dbfile().unwrap()) {
|
||||
dc_delete_file(context, as_path(context.get_dbfile()));
|
||||
if dc_file_exist(context, as_path(context.get_dbfile())) {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot import backups: Cannot delete the old file.",
|
||||
@@ -611,16 +616,13 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
if !dc_copy_file(
|
||||
context,
|
||||
as_path(backup_to_import),
|
||||
context.get_dbfile().unwrap(),
|
||||
as_path(context.get_dbfile()),
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
/* error already logged */
|
||||
/* re-open copied database file */
|
||||
if !context
|
||||
.sql
|
||||
.open(&context, &context.get_dbfile().unwrap(), 0)
|
||||
{
|
||||
if !context.sql.open(&context, as_path(context.get_dbfile()), 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -712,7 +714,7 @@ The macro avoids weird values of 0% or 100% while still working. */
|
||||
// TODO should return bool /rtn
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_int {
|
||||
let mut ok_to_continue: bool;
|
||||
let mut current_block: u64;
|
||||
let mut success: libc::c_int = 0;
|
||||
|
||||
let mut delete_dest_file: libc::c_int = 0;
|
||||
@@ -739,20 +741,15 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
context,
|
||||
0,
|
||||
"Backup \"{}\" to \"{}\".",
|
||||
context
|
||||
.get_dbfile()
|
||||
.as_ref()
|
||||
.map_or("<<None>>", |p| p.to_str().unwrap()),
|
||||
as_str(context.get_dbfile()),
|
||||
as_str(dest_pathNfilename),
|
||||
);
|
||||
if dc_copy_file(
|
||||
context,
|
||||
context.get_dbfile().unwrap(),
|
||||
as_path(context.get_dbfile()),
|
||||
as_path(dest_pathNfilename),
|
||||
) {
|
||||
context
|
||||
.sql
|
||||
.open(&context, &context.get_dbfile().unwrap(), 0);
|
||||
context.sql.open(&context, as_path(context.get_dbfile()), 0);
|
||||
closed = false;
|
||||
/* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
|
||||
/*for logging only*/
|
||||
@@ -768,30 +765,51 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
.is_err()
|
||||
{
|
||||
/* error already logged */
|
||||
ok_to_continue = false;
|
||||
current_block = 11487273724841241105;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
current_block = 14648156034262866959;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
current_block = 14648156034262866959;
|
||||
}
|
||||
if ok_to_continue {
|
||||
let mut total_files_cnt = 0;
|
||||
let dir = std::path::Path::new(as_str(context.get_blobdir()));
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
total_files_cnt += dir_handle.filter(|r| r.is_ok()).count();
|
||||
match current_block {
|
||||
11487273724841241105 => {}
|
||||
_ => {
|
||||
let mut total_files_cnt = 0;
|
||||
let dir = std::path::Path::new(as_str(context.get_blobdir()));
|
||||
let dir_handle = std::fs::read_dir(dir);
|
||||
if dir_handle.is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot get info for blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir()),
|
||||
);
|
||||
} else {
|
||||
let dir_handle = dir_handle.unwrap();
|
||||
total_files_cnt += dir_handle.filter(|r| r.is_ok()).count();
|
||||
|
||||
info!(context, 0, "EXPORT: total_files_cnt={}", total_files_cnt);
|
||||
if total_files_cnt > 0 {
|
||||
// scan directory, pass 2: copy files
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
sql.prepare(
|
||||
info!(context, 0, "EXPORT: total_files_cnt={}", total_files_cnt);
|
||||
if total_files_cnt > 0 {
|
||||
// scan directory, pass 2: copy files
|
||||
let dir_handle = std::fs::read_dir(dir);
|
||||
if dir_handle.is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot copy from blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir()),
|
||||
);
|
||||
} else {
|
||||
let dir_handle = dir_handle.unwrap();
|
||||
|
||||
sql.prepare(
|
||||
"INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);",
|
||||
move |mut stmt, _| {
|
||||
let mut processed_files_cnt = 0;
|
||||
for entry in dir_handle {
|
||||
if entry.is_err() {
|
||||
ok_to_continue = true;
|
||||
current_block = 2631791190359682872;
|
||||
break;
|
||||
}
|
||||
let entry = entry.unwrap();
|
||||
@@ -803,7 +821,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
.shall_stop_ongoing
|
||||
{
|
||||
delete_dest_file = 1;
|
||||
ok_to_continue = false;
|
||||
current_block = 11487273724841241105;
|
||||
break;
|
||||
} else {
|
||||
processed_files_cnt += 1;
|
||||
@@ -848,7 +866,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
&curr_pathNfilename,
|
||||
);
|
||||
/* this is not recoverable! writing to the sqlite database should work! */
|
||||
ok_to_continue = false;
|
||||
current_block = 11487273724841241105;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -860,46 +878,34 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
Ok(())
|
||||
}
|
||||
).unwrap();
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot copy from blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir()),
|
||||
);
|
||||
info!(context, 0, "Backup: No files to copy.",);
|
||||
current_block = 2631791190359682872;
|
||||
}
|
||||
} else {
|
||||
info!(context, 0, "Backup: No files to copy.",);
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if sql
|
||||
.set_config_int(context, "backup_time", now as i32)
|
||||
.is_ok()
|
||||
{
|
||||
context.call_cb(
|
||||
Event::IMEX_FILE_WRITTEN,
|
||||
dest_pathNfilename as uintptr_t,
|
||||
0,
|
||||
);
|
||||
success = 1;
|
||||
match current_block {
|
||||
11487273724841241105 => {}
|
||||
_ => {
|
||||
if sql
|
||||
.set_config_int(context, "backup_time", now as i32)
|
||||
.is_ok()
|
||||
{
|
||||
context.call_cb(
|
||||
Event::IMEX_FILE_WRITTEN,
|
||||
dest_pathNfilename as uintptr_t,
|
||||
0,
|
||||
);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot get info for blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir())
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if closed {
|
||||
context
|
||||
.sql
|
||||
.open(&context, &context.get_dbfile().unwrap(), 0);
|
||||
context.sql.open(&context, as_path(context.get_dbfile()), 0);
|
||||
}
|
||||
if 0 != delete_dest_file {
|
||||
dc_delete_file(context, as_path(dest_pathNfilename));
|
||||
@@ -920,19 +926,28 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
Maybe we should make the "default" key handlong also a little bit smarter
|
||||
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
|
||||
let mut imported_cnt: libc::c_int = 0;
|
||||
let mut suffix: *mut libc::c_char = ptr::null_mut();
|
||||
let mut path_plus_name: *mut libc::c_char = ptr::null_mut();
|
||||
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut set_default: libc::c_int;
|
||||
let mut buf: *mut libc::c_char = ptr::null_mut();
|
||||
let mut buf: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut buf_bytes: size_t = 0 as size_t;
|
||||
// a pointer inside buf, MUST NOT be free()'d
|
||||
let mut private_key: *const libc::c_char;
|
||||
let mut buf2: *mut libc::c_char = ptr::null_mut();
|
||||
let mut buf2: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
// a pointer inside buf2, MUST NOT be free()'d
|
||||
let mut buf2_headerline: *const libc::c_char = ptr::null_mut();
|
||||
let mut buf2_headerline: *const libc::c_char = 0 as *const libc::c_char;
|
||||
if !dir_name.is_null() {
|
||||
let dir = std::path::Path::new(as_str(dir_name));
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
let dir_handle = std::fs::read_dir(dir);
|
||||
if dir_handle.is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import: Cannot open directory \"{}\".",
|
||||
as_str(dir_name),
|
||||
);
|
||||
} else {
|
||||
let dir_handle = dir_handle.unwrap();
|
||||
for entry in dir_handle {
|
||||
if entry.is_err() {
|
||||
break;
|
||||
@@ -941,7 +956,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
free(suffix as *mut libc::c_void);
|
||||
let name_f = entry.file_name();
|
||||
let name_c = name_f.to_c_string().unwrap();
|
||||
suffix = dc_get_filesuffix_lc(name_f.to_string_lossy());
|
||||
suffix = dc_get_filesuffix_lc(name_c.as_ptr());
|
||||
if suffix.is_null()
|
||||
|| strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0
|
||||
{
|
||||
@@ -955,7 +970,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
);
|
||||
info!(context, 0, "Checking: {}", as_str(path_plus_name));
|
||||
free(buf as *mut libc::c_void);
|
||||
buf = ptr::null_mut();
|
||||
buf = 0 as *mut libc::c_char;
|
||||
if 0 == dc_read_file(
|
||||
context,
|
||||
path_plus_name,
|
||||
@@ -971,9 +986,9 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
if dc_split_armored_data(
|
||||
buf2,
|
||||
&mut buf2_headerline,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
) && strcmp(
|
||||
buf2_headerline,
|
||||
b"-----BEGIN PGP PUBLIC KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -1016,13 +1031,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
as_str(dir_name),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import: Cannot open directory \"{}\".",
|
||||
as_str(dir_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1149
src/dc_job.rs
Normal file
1149
src/dc_job.rs
Normal file
File diff suppressed because it is too large
Load Diff
209
src/dc_jobthread.rs
Normal file
209
src/dc_jobthread.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::imap::Imap;
|
||||
use crate::x::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct dc_jobthread_t {
|
||||
pub name: &'static str,
|
||||
pub folder_config_name: &'static str,
|
||||
pub imap: Imap,
|
||||
pub state: Arc<(Mutex<JobState>, Condvar)>,
|
||||
}
|
||||
|
||||
pub fn dc_jobthread_init(
|
||||
name: &'static str,
|
||||
folder_config_name: &'static str,
|
||||
imap: Imap,
|
||||
) -> dc_jobthread_t {
|
||||
dc_jobthread_t {
|
||||
name,
|
||||
folder_config_name,
|
||||
imap,
|
||||
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct JobState {
|
||||
idle: bool,
|
||||
jobs_needed: i32,
|
||||
suspended: i32,
|
||||
using_handle: i32,
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_suspend(
|
||||
context: &Context,
|
||||
jobthread: &dc_jobthread_t,
|
||||
suspend: libc::c_int,
|
||||
) {
|
||||
if 0 != suspend {
|
||||
info!(context, 0, "Suspending {}-thread.", jobthread.name,);
|
||||
{
|
||||
jobthread.state.0.lock().unwrap().suspended = 1;
|
||||
}
|
||||
dc_jobthread_interrupt_idle(context, jobthread);
|
||||
loop {
|
||||
let using_handle = jobthread.state.0.lock().unwrap().using_handle;
|
||||
if using_handle == 0 {
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
|
||||
}
|
||||
} else {
|
||||
info!(context, 0, "Unsuspending {}-thread.", jobthread.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.suspended = 0;
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_interrupt_idle(context: &Context, jobthread: &dc_jobthread_t) {
|
||||
{
|
||||
jobthread.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, 0, "Interrupting {}-IDLE...", jobthread.name);
|
||||
|
||||
jobthread.imap.interrupt_idle();
|
||||
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_fetch(
|
||||
context: &Context,
|
||||
jobthread: &mut dc_jobthread_t,
|
||||
use_network: libc::c_int,
|
||||
) {
|
||||
let start;
|
||||
|
||||
{
|
||||
let &(ref lock, _) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.suspended {
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = 1;
|
||||
}
|
||||
|
||||
if 0 != use_network {
|
||||
start = clock();
|
||||
if !(0 == connect_to_imap(context, jobthread)) {
|
||||
info!(context, 0, "{}-fetch started...", jobthread.name);
|
||||
jobthread.imap.fetch(context);
|
||||
|
||||
if jobthread.imap.should_reconnect() {
|
||||
info!(
|
||||
context,
|
||||
0, "{}-fetch aborted, starting over...", jobthread.name,
|
||||
);
|
||||
jobthread.imap.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
jobthread.name,
|
||||
clock().wrapping_sub(start) as f64 / 1000.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
jobthread.state.0.lock().unwrap().using_handle = 0;
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* the typical fetch, idle, interrupt-idle
|
||||
******************************************************************************/
|
||||
|
||||
unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int {
|
||||
if jobthread.imap.is_connected() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
|
||||
|
||||
if !(0 == ret_connected) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
jobthread.imap.configure_folders(context, 0x1);
|
||||
}
|
||||
|
||||
if let Some(mvbox_name) = context
|
||||
.sql
|
||||
.get_config(context, jobthread.folder_config_name)
|
||||
{
|
||||
jobthread.imap.set_watch_folder(mvbox_name);
|
||||
} else {
|
||||
jobthread.imap.disconnect(context);
|
||||
ret_connected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret_connected
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_idle(
|
||||
context: &Context,
|
||||
jobthread: &dc_jobthread_t,
|
||||
use_network: libc::c_int,
|
||||
) {
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
jobthread.name,
|
||||
);
|
||||
state.jobs_needed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if 0 != state.suspended {
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = 1;
|
||||
|
||||
if 0 == use_network {
|
||||
state.using_handle = 0;
|
||||
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connect_to_imap(context, jobthread);
|
||||
info!(context, 0, "{}-IDLE started...", jobthread.name,);
|
||||
jobthread.imap.idle(context);
|
||||
info!(context, 0, "{}-IDLE ended.", jobthread.name);
|
||||
|
||||
jobthread.state.0.lock().unwrap().using_handle = 0;
|
||||
}
|
||||
754
src/dc_location.rs
Normal file
754
src/dc_location.rs
Normal file
@@ -0,0 +1,754 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_saxparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
// location handling
|
||||
#[derive(Clone, Default)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct dc_location {
|
||||
pub location_id: uint32_t,
|
||||
pub latitude: libc::c_double,
|
||||
pub longitude: libc::c_double,
|
||||
pub accuracy: libc::c_double,
|
||||
pub timestamp: i64,
|
||||
pub contact_id: uint32_t,
|
||||
pub msg_id: uint32_t,
|
||||
pub chat_id: uint32_t,
|
||||
pub marker: Option<String>,
|
||||
pub independent: uint32_t,
|
||||
}
|
||||
|
||||
impl dc_location {
|
||||
pub fn new() -> Self {
|
||||
dc_location {
|
||||
location_id: 0,
|
||||
latitude: 0.0,
|
||||
longitude: 0.0,
|
||||
accuracy: 0.0,
|
||||
timestamp: 0,
|
||||
contact_id: 0,
|
||||
msg_id: 0,
|
||||
chat_id: 0,
|
||||
marker: None,
|
||||
independent: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct dc_kml_t {
|
||||
pub addr: *mut libc::c_char,
|
||||
pub locations: Option<Vec<dc_location>>,
|
||||
pub tag: libc::c_int,
|
||||
pub curr: dc_location,
|
||||
}
|
||||
|
||||
impl dc_kml_t {
|
||||
pub fn new() -> Self {
|
||||
dc_kml_t {
|
||||
addr: std::ptr::null_mut(),
|
||||
locations: None,
|
||||
tag: 0,
|
||||
curr: dc_location::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// location streaming
|
||||
pub unsafe fn dc_send_locations_to_chat(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
seconds: libc::c_int,
|
||||
) {
|
||||
let now = time();
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let is_sending_locations_before: bool;
|
||||
if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) {
|
||||
is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id);
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats \
|
||||
SET locations_send_begin=?, \
|
||||
locations_send_until=? \
|
||||
WHERE id=?",
|
||||
params![
|
||||
if 0 != seconds { now } else { 0 },
|
||||
if 0 != seconds {
|
||||
now + seconds as i64
|
||||
} else {
|
||||
0
|
||||
},
|
||||
chat_id as i32,
|
||||
],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
if 0 != seconds && !is_sending_locations_before {
|
||||
msg = dc_msg_new(context, Viewtype::Text);
|
||||
(*msg).text =
|
||||
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
|
||||
(*msg).param.set_int(Param::Cmd, 8);
|
||||
dc_send_msg(context, chat_id, msg);
|
||||
} else if 0 == seconds && is_sending_locations_before {
|
||||
let stock_str = CString::new(context.stock_system_msg(
|
||||
StockMessage::MsgLocationDisabled,
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
))
|
||||
.unwrap();
|
||||
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
|
||||
}
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
if 0 != seconds {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0i32);
|
||||
dc_job_add(
|
||||
context,
|
||||
5007i32,
|
||||
chat_id as libc::c_int,
|
||||
Params::new(),
|
||||
seconds + 1i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* job to send locations out to all chats that want them
|
||||
******************************************************************************/
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: libc::c_int) {
|
||||
if 0 != flags & 0x1 || !dc_job_action_exists(context, 5005) {
|
||||
dc_job_add(context, 5005, 0, Params::new(), 60);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dc_is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool {
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;",
|
||||
params![if chat_id == 0 { 1 } else { 0 }, chat_id as i32, time()],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn dc_set_location(
|
||||
context: &Context,
|
||||
latitude: libc::c_double,
|
||||
longitude: libc::c_double,
|
||||
accuracy: libc::c_double,
|
||||
) -> libc::c_int {
|
||||
if latitude == 0.0 && longitude == 0.0 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.sql.query_map(
|
||||
"SELECT id FROM chats WHERE locations_send_until>?;",
|
||||
params![time()], |row| row.get::<_, i32>(0),
|
||||
|chats| {
|
||||
let mut continue_streaming = false;
|
||||
|
||||
for chat in chats {
|
||||
let chat_id = chat?;
|
||||
context.sql.execute(
|
||||
"INSERT INTO locations \
|
||||
(latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);",
|
||||
params![
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy,
|
||||
time(),
|
||||
chat_id,
|
||||
1,
|
||||
]
|
||||
)?;
|
||||
continue_streaming = true;
|
||||
}
|
||||
if continue_streaming {
|
||||
context.call_cb(Event::LOCATION_CHANGED, 1, 0);
|
||||
};
|
||||
unsafe { schedule_MAYBE_SEND_LOCATIONS(context, 0) };
|
||||
Ok(continue_streaming as libc::c_int)
|
||||
}
|
||||
).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn dc_get_locations(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
contact_id: uint32_t,
|
||||
timestamp_from: i64,
|
||||
mut timestamp_to: i64,
|
||||
) -> *mut dc_array_t {
|
||||
if timestamp_to == 0 {
|
||||
timestamp_to = time() + 10;
|
||||
}
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT l.id, l.latitude, l.longitude, l.accuracy, l.timestamp, l.independent, \
|
||||
m.id, l.from_id, l.chat_id, m.txt \
|
||||
FROM locations l LEFT JOIN msgs m ON l.id=m.location_id WHERE (? OR l.chat_id=?) \
|
||||
AND (? OR l.from_id=?) \
|
||||
AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \
|
||||
ORDER BY l.timestamp DESC, l.id DESC, m.id DESC;",
|
||||
params![
|
||||
if chat_id == 0 { 1 } else { 0 },
|
||||
chat_id as i32,
|
||||
if contact_id == 0 { 1 } else { 0 },
|
||||
contact_id as i32,
|
||||
timestamp_from,
|
||||
timestamp_to,
|
||||
],
|
||||
|row| {
|
||||
let msg_id = row.get(6)?;
|
||||
let txt: String = row.get(9)?;
|
||||
let marker = if msg_id != 0 && is_marker(&txt) {
|
||||
Some(txt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let loc = dc_location {
|
||||
location_id: row.get(0)?,
|
||||
latitude: row.get(1)?,
|
||||
longitude: row.get(2)?,
|
||||
accuracy: row.get(3)?,
|
||||
timestamp: row.get(4)?,
|
||||
independent: row.get(5)?,
|
||||
msg_id: msg_id,
|
||||
contact_id: row.get(7)?,
|
||||
chat_id: row.get(8)?,
|
||||
marker: marker,
|
||||
};
|
||||
Ok(loc)
|
||||
},
|
||||
|locations| {
|
||||
let mut ret = Vec::new();
|
||||
|
||||
for location in locations {
|
||||
ret.push(location?);
|
||||
}
|
||||
Ok(dc_array_t::from(ret).into_raw())
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|_| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
fn is_marker(txt: &str) -> bool {
|
||||
txt.len() == 1 && txt.chars().next().unwrap() != ' '
|
||||
}
|
||||
|
||||
pub fn dc_delete_all_locations(context: &Context) -> bool {
|
||||
if sql::execute(context, &context.sql, "DELETE FROM locations;", params![]).is_err() {
|
||||
return false;
|
||||
}
|
||||
context.call_cb(Event::LOCATION_CHANGED, 0, 0);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn dc_get_location_kml(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
last_added_location_id: *mut uint32_t,
|
||||
) -> *mut libc::c_char {
|
||||
let mut success: libc::c_int = 0;
|
||||
let now = time();
|
||||
let mut location_count: libc::c_int = 0;
|
||||
let mut ret = String::new();
|
||||
|
||||
let self_addr = context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Ok((locations_send_begin, locations_send_until, locations_last_sent)) = context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;",
|
||||
params![chat_id as i32], |row| {
|
||||
let send_begin: i64 = row.get(0)?;
|
||||
let send_until: i64 = row.get(1)?;
|
||||
let last_sent: i64 = row.get(2)?;
|
||||
|
||||
Ok((send_begin, send_until, last_sent))
|
||||
}
|
||||
) {
|
||||
if !(locations_send_begin == 0 || now > locations_send_until) {
|
||||
ret += &format!(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"{}\">\n",
|
||||
self_addr,
|
||||
);
|
||||
|
||||
context.sql.query_map(
|
||||
"SELECT id, latitude, longitude, accuracy, timestamp\
|
||||
FROM locations WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND (timestamp>=? OR timestamp=(SELECT MAX(timestamp) FROM locations WHERE from_id=?)) \
|
||||
AND independent=0 \
|
||||
GROUP BY timestamp \
|
||||
ORDER BY timestamp;",
|
||||
params![1, locations_send_begin, locations_last_sent, 1],
|
||||
|row| {
|
||||
let location_id: i32 = row.get(0)?;
|
||||
let latitude: f64 = row.get(1)?;
|
||||
let longitude: f64 = row.get(2)?;
|
||||
let accuracy: f64 = row.get(3)?;
|
||||
let timestamp = unsafe { get_kml_timestamp(row.get(4)?) };
|
||||
|
||||
Ok((location_id, latitude, longitude, accuracy, timestamp))
|
||||
},
|
||||
|rows| {
|
||||
for row in rows {
|
||||
let (location_id, latitude, longitude, accuracy, timestamp) = row?;
|
||||
ret += &format!(
|
||||
"<Placemark><Timestamp><when>{}</when></Timestamp><Point><coordinates accuracy=\"{}\">{},{}</coordinates></Point></Placemark>\n\x00",
|
||||
as_str(timestamp),
|
||||
accuracy,
|
||||
longitude,
|
||||
latitude
|
||||
);
|
||||
location_count += 1;
|
||||
if !last_added_location_id.is_null() {
|
||||
unsafe { *last_added_location_id = location_id as u32 };
|
||||
}
|
||||
unsafe { free(timestamp as *mut libc::c_void) };
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
).unwrap(); // TODO: better error handling
|
||||
}
|
||||
}
|
||||
|
||||
if location_count > 0 {
|
||||
ret += "</Document>\n</kml>";
|
||||
success = 1;
|
||||
}
|
||||
|
||||
if 0 != success {
|
||||
unsafe { ret.strdup() }
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* create kml-files
|
||||
******************************************************************************/
|
||||
unsafe fn get_kml_timestamp(utc: i64) -> *mut libc::c_char {
|
||||
// Returns a string formatted as YYYY-MM-DDTHH:MM:SSZ. The trailing `Z` indicates UTC.
|
||||
let res = chrono::NaiveDateTime::from_timestamp(utc, 0)
|
||||
.format("%Y-%m-%dT%H:%M:%SZ")
|
||||
.to_string();
|
||||
res.strdup()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_message_kml(
|
||||
timestamp: i64,
|
||||
latitude: libc::c_double,
|
||||
longitude: libc::c_double,
|
||||
) -> *mut libc::c_char {
|
||||
let timestamp_str = get_kml_timestamp(timestamp);
|
||||
let latitude_str = dc_ftoa(latitude);
|
||||
let longitude_str = dc_ftoa(longitude);
|
||||
|
||||
let ret = dc_mprintf(
|
||||
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
|
||||
<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n\
|
||||
<Document>\n\
|
||||
<Placemark>\
|
||||
<Timestamp><when>%s</when></Timestamp>\
|
||||
<Point><coordinates>%s,%s</coordinates></Point>\
|
||||
</Placemark>\n\
|
||||
</Document>\n\
|
||||
</kml>\x00" as *const u8 as *const libc::c_char,
|
||||
timestamp_str,
|
||||
longitude_str, // reverse order!
|
||||
latitude_str,
|
||||
);
|
||||
|
||||
free(latitude_str as *mut libc::c_void);
|
||||
free(longitude_str as *mut libc::c_void);
|
||||
free(timestamp_str as *mut libc::c_void);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn dc_set_kml_sent_timestamp(context: &Context, chat_id: u32, timestamp: i64) -> bool {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET locations_last_sent=? WHERE id=?;",
|
||||
params![timestamp, chat_id as i32],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn dc_set_msg_location_id(context: &Context, msg_id: u32, location_id: u32) -> bool {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET location_id=? WHERE id=?;",
|
||||
params![location_id, msg_id as i32],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_save_locations(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
contact_id: u32,
|
||||
locations_opt: &Option<Vec<dc_location>>,
|
||||
independent: libc::c_int,
|
||||
) -> u32 {
|
||||
if chat_id <= 9 || locations_opt.is_none() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let locations = locations_opt.as_ref().unwrap();
|
||||
context
|
||||
.sql
|
||||
.prepare2(
|
||||
"SELECT id FROM locations WHERE timestamp=? AND from_id=?",
|
||||
"INSERT INTO locations\
|
||||
(timestamp, from_id, chat_id, latitude, longitude, accuracy, independent) \
|
||||
VALUES (?,?,?,?,?,?,?);",
|
||||
|mut stmt_test, mut stmt_insert, conn| {
|
||||
let mut newest_timestamp = 0;
|
||||
let mut newest_location_id = 0;
|
||||
|
||||
for location in locations {
|
||||
let exists =
|
||||
stmt_test.exists(params![location.timestamp, contact_id as i32])?;
|
||||
|
||||
if 0 != independent || !exists {
|
||||
stmt_insert.execute(params![
|
||||
location.timestamp,
|
||||
contact_id as i32,
|
||||
chat_id as i32,
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
location.accuracy,
|
||||
independent,
|
||||
])?;
|
||||
|
||||
if location.timestamp > newest_timestamp {
|
||||
newest_timestamp = location.timestamp;
|
||||
newest_location_id = sql::get_rowid2_with_conn(
|
||||
context,
|
||||
conn,
|
||||
"locations",
|
||||
"timestamp",
|
||||
location.timestamp,
|
||||
"from_id",
|
||||
contact_id as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(newest_location_id)
|
||||
},
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_kml_parse(
|
||||
context: &Context,
|
||||
content: *const libc::c_char,
|
||||
content_bytes: size_t,
|
||||
) -> dc_kml_t {
|
||||
let mut kml = dc_kml_t::new();
|
||||
let mut content_nullterminated: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
|
||||
if content_bytes > (1 * 1024 * 1024) {
|
||||
warn!(
|
||||
context,
|
||||
0, "A kml-files with {} bytes is larger than reasonably expected.", content_bytes,
|
||||
);
|
||||
} else {
|
||||
content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int);
|
||||
if !content_nullterminated.is_null() {
|
||||
kml.locations = Some(Vec::with_capacity(100));
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut kml as *mut dc_kml_t as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(kml_starttag_cb),
|
||||
Some(kml_endtag_cb),
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(kml_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, content_nullterminated);
|
||||
}
|
||||
}
|
||||
|
||||
free(content_nullterminated as *mut libc::c_void);
|
||||
|
||||
kml
|
||||
}
|
||||
|
||||
unsafe fn kml_text_cb(userdata: *mut libc::c_void, text: *const libc::c_char, _len: libc::c_int) {
|
||||
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
|
||||
if 0 != (*kml).tag & (0x4 | 0x10) {
|
||||
let mut val: *mut libc::c_char = dc_strdup(text);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"\n\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"\r\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"\t\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b" \x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
if 0 != (*kml).tag & 0x4 && strlen(val) >= 19 {
|
||||
// YYYY-MM-DDTHH:MM:SSZ
|
||||
// 0 4 7 10 13 16 19
|
||||
let val_r = as_str(val);
|
||||
match chrono::NaiveDateTime::parse_from_str(val_r, "%Y-%m-%dT%H:%M:%SZ") {
|
||||
Ok(res) => {
|
||||
(*kml).curr.timestamp = res.timestamp();
|
||||
if (*kml).curr.timestamp > time() {
|
||||
(*kml).curr.timestamp = time();
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
(*kml).curr.timestamp = time();
|
||||
}
|
||||
}
|
||||
} else if 0 != (*kml).tag & 0x10 {
|
||||
let mut comma: *mut libc::c_char = strchr(val, ',' as i32);
|
||||
if !comma.is_null() {
|
||||
let longitude: *mut libc::c_char = val;
|
||||
let latitude: *mut libc::c_char = comma.offset(1isize);
|
||||
*comma = 0 as libc::c_char;
|
||||
comma = strchr(latitude, ',' as i32);
|
||||
if !comma.is_null() {
|
||||
*comma = 0 as libc::c_char
|
||||
}
|
||||
(*kml).curr.latitude = dc_atof(latitude);
|
||||
(*kml).curr.longitude = dc_atof(longitude)
|
||||
}
|
||||
}
|
||||
free(val as *mut libc::c_void);
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
|
||||
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
|
||||
if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
if 0 != (*kml).tag & 0x1
|
||||
&& 0 != (*kml).curr.timestamp
|
||||
&& 0. != (*kml).curr.latitude
|
||||
&& 0. != (*kml).curr.longitude
|
||||
{
|
||||
let location = (*kml).curr.clone();
|
||||
((*kml).locations.as_mut().unwrap()).push(location);
|
||||
}
|
||||
(*kml).tag = 0
|
||||
};
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* parse kml-files
|
||||
******************************************************************************/
|
||||
unsafe fn kml_starttag_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
tag: *const libc::c_char,
|
||||
attr: *mut *mut libc::c_char,
|
||||
) {
|
||||
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
|
||||
if strcmp(tag, b"document\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
let addr: *const libc::c_char =
|
||||
dc_attr_find(attr, b"addr\x00" as *const u8 as *const libc::c_char);
|
||||
if !addr.is_null() {
|
||||
(*kml).addr = dc_strdup(addr)
|
||||
}
|
||||
} else if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*kml).tag = 0x1;
|
||||
(*kml).curr.timestamp = 0;
|
||||
(*kml).curr.latitude = 0 as libc::c_double;
|
||||
(*kml).curr.longitude = 0.0f64;
|
||||
(*kml).curr.accuracy = 0.0f64
|
||||
} else if strcmp(tag, b"timestamp\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x1
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x2
|
||||
} else if strcmp(tag, b"when\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x2
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x2 | 0x4
|
||||
} else if strcmp(tag, b"point\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x1
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x8
|
||||
} else if strcmp(tag, b"coordinates\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x8
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x8 | 0x10;
|
||||
let accuracy: *const libc::c_char =
|
||||
dc_attr_find(attr, b"accuracy\x00" as *const u8 as *const libc::c_char);
|
||||
if !accuracy.is_null() {
|
||||
(*kml).curr.accuracy = dc_atof(accuracy)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
|
||||
free((*kml).addr as *mut libc::c_void);
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mut dc_job_t) {
|
||||
let now = time();
|
||||
let mut continue_streaming: libc::c_int = 1;
|
||||
info!(
|
||||
context,
|
||||
0, " ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
||||
);
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id, locations_send_begin, locations_last_sent \
|
||||
FROM chats \
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = 1;
|
||||
|
||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
||||
if now - locations_last_sent < (60 - 3) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
||||
}
|
||||
},
|
||||
|rows| {
|
||||
context.sql.prepare(
|
||||
"SELECT id \
|
||||
FROM locations \
|
||||
WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND timestamp>? \
|
||||
AND independent=0 \
|
||||
ORDER BY timestamp;",
|
||||
|mut stmt_locations, _| {
|
||||
for (chat_id, locations_send_begin, locations_last_sent) in
|
||||
rows.filter_map(|r| match r {
|
||||
Ok(Some(v)) => Some(v),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
// TODO: do I need to reset?
|
||||
if !stmt_locations
|
||||
.exists(params![1, locations_send_begin, locations_last_sent,])
|
||||
.unwrap_or_default()
|
||||
{
|
||||
// if there is no new location, there's nothing to send.
|
||||
// however, maybe we want to bypass this test eg. 15 minutes
|
||||
continue;
|
||||
}
|
||||
// pending locations are attached automatically to every message,
|
||||
// so also to this empty text message.
|
||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
||||
//
|
||||
// for optimisation and to avoid flooding the sending queue,
|
||||
// we could sending these messages only if we're really online.
|
||||
// the easiest way to determine this, is to check for an empty message queue.
|
||||
// (might not be 100%, however, as positions are sent combined later
|
||||
// and dc_set_location() is typically called periodically, this is ok)
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
(*msg).hidden = 1;
|
||||
(*msg).param.set_int(Param::Cmd, 9);
|
||||
dc_send_msg(context, chat_id as u32, msg);
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
.unwrap(); // TODO: Better error handling
|
||||
|
||||
if 0 != continue_streaming {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0x1);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut dc_job_t) {
|
||||
// this function is called when location-streaming _might_ have ended for a chat.
|
||||
// the function checks, if location-streaming is really ended;
|
||||
// if so, a device-message is added if not yet done.
|
||||
|
||||
let chat_id = (*job).foreign_id;
|
||||
|
||||
if let Ok((send_begin, send_until)) = context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)),
|
||||
) {
|
||||
if !(send_begin != 0 && time() <= send_until) {
|
||||
// still streaming -
|
||||
// may happen as several calls to dc_send_locations_to_chat()
|
||||
// do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs
|
||||
if !(send_begin == 0 && send_until == 0) {
|
||||
// not streaming, device-message already sent
|
||||
if context.sql.execute(
|
||||
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
).is_ok() {
|
||||
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
|
||||
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as usize,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
181
src/dc_lot.rs
Normal file
181
src/dc_lot.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
/* * Structure behind dc_lot_t */
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct dc_lot_t {
|
||||
pub text1_meaning: libc::c_int,
|
||||
pub text1: *mut libc::c_char,
|
||||
pub text2: *mut libc::c_char,
|
||||
pub timestamp: i64,
|
||||
pub state: libc::c_int,
|
||||
pub id: uint32_t,
|
||||
pub fingerprint: *mut libc::c_char,
|
||||
pub invitenumber: *mut libc::c_char,
|
||||
pub auth: *mut libc::c_char,
|
||||
}
|
||||
|
||||
/* *
|
||||
* @class dc_lot_t
|
||||
*
|
||||
* 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().
|
||||
*
|
||||
* NB: _Lot_ is used in the meaning _heap_ here.
|
||||
*/
|
||||
pub unsafe fn dc_lot_new() -> *mut dc_lot_t {
|
||||
let mut lot: *mut dc_lot_t;
|
||||
lot = calloc(1, ::std::mem::size_of::<dc_lot_t>()) as *mut dc_lot_t;
|
||||
assert!(!lot.is_null());
|
||||
|
||||
(*lot).text1_meaning = 0i32;
|
||||
|
||||
lot
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_empty(mut lot: *mut dc_lot_t) {
|
||||
if lot.is_null() {
|
||||
return;
|
||||
}
|
||||
free((*lot).text1 as *mut libc::c_void);
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32;
|
||||
free((*lot).text2 as *mut libc::c_void);
|
||||
(*lot).text2 = 0 as *mut libc::c_char;
|
||||
free((*lot).fingerprint as *mut libc::c_void);
|
||||
(*lot).fingerprint = 0 as *mut libc::c_char;
|
||||
free((*lot).invitenumber as *mut libc::c_void);
|
||||
(*lot).invitenumber = 0 as *mut libc::c_char;
|
||||
free((*lot).auth as *mut libc::c_void);
|
||||
(*lot).auth = 0 as *mut libc::c_char;
|
||||
(*lot).timestamp = 0;
|
||||
(*lot).state = 0i32;
|
||||
(*lot).id = 0i32 as uint32_t;
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_unref(set: *mut dc_lot_t) {
|
||||
if set.is_null() {
|
||||
return;
|
||||
}
|
||||
dc_lot_empty(set);
|
||||
free(set as *mut libc::c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_text1(lot: *const dc_lot_t) -> *mut libc::c_char {
|
||||
if lot.is_null() {
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
dc_strdup_keep_null((*lot).text1)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_text2(lot: *const dc_lot_t) -> *mut libc::c_char {
|
||||
if lot.is_null() {
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
dc_strdup_keep_null((*lot).text2)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_text1_meaning(lot: *const dc_lot_t) -> libc::c_int {
|
||||
if lot.is_null() {
|
||||
return 0i32;
|
||||
}
|
||||
|
||||
(*lot).text1_meaning
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_state(lot: *const dc_lot_t) -> libc::c_int {
|
||||
if lot.is_null() {
|
||||
return 0i32;
|
||||
}
|
||||
|
||||
(*lot).state
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_id(lot: *const dc_lot_t) -> uint32_t {
|
||||
if lot.is_null() {
|
||||
return 0i32 as uint32_t;
|
||||
}
|
||||
|
||||
(*lot).id
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
|
||||
if lot.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*lot).timestamp
|
||||
}
|
||||
|
||||
/* library-internal */
|
||||
/* in practice, the user additionally cuts the string himself pixel-accurate */
|
||||
pub unsafe fn dc_lot_fill(
|
||||
mut lot: *mut dc_lot_t,
|
||||
msg: *mut dc_msg_t,
|
||||
chat: *const Chat,
|
||||
contact: Option<&Contact>,
|
||||
context: &Context,
|
||||
) {
|
||||
if lot.is_null() || msg.is_null() {
|
||||
return;
|
||||
}
|
||||
if (*msg).state == 19i32 {
|
||||
(*lot).text1 = context.stock_str(StockMessage::Draft).strdup();
|
||||
(*lot).text1_meaning = 1i32
|
||||
} else if (*msg).from_id == 1i32 as libc::c_uint {
|
||||
if 0 != dc_msg_is_info(msg) || 0 != dc_chat_is_self_talk(chat) {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else {
|
||||
(*lot).text1 = context.stock_str(StockMessage::SelfMsg).strdup();
|
||||
(*lot).text1_meaning = 3i32
|
||||
}
|
||||
} else if chat.is_null() {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
|
||||
if 0 != dc_msg_is_info(msg) || contact.is_none() {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else {
|
||||
if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint {
|
||||
if let Some(contact) = contact {
|
||||
(*lot).text1 = contact.get_display_name().strdup();
|
||||
} else {
|
||||
(*lot).text1 = std::ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
if let Some(contact) = contact {
|
||||
(*lot).text1 = contact.get_first_name().strdup();
|
||||
} else {
|
||||
(*lot).text1 = std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
(*lot).text1_meaning = 2i32;
|
||||
}
|
||||
}
|
||||
|
||||
let message_text = (*msg).text.as_ref().unwrap();
|
||||
|
||||
(*lot).text2 = dc_msg_get_summarytext_by_raw(
|
||||
(*msg).type_0,
|
||||
message_text.strdup(),
|
||||
&mut (*msg).param,
|
||||
160,
|
||||
context,
|
||||
);
|
||||
|
||||
(*lot).timestamp = dc_msg_get_timestamp(msg);
|
||||
(*lot).state = (*msg).state;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
40
src/dc_move.rs
Normal file
40
src/dc_move.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::param::Params;
|
||||
|
||||
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
.unwrap_or_else(|| 1)
|
||||
== 0
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = dc_msg_new_load(context, msg_id);
|
||||
if dc_msg_is_setupmessage(msg) {
|
||||
// do not move setup messages;
|
||||
// there may be a non-delta device that wants to handle it
|
||||
dc_msg_unref(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if dc_is_mvbox(context, folder) {
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Stay);
|
||||
}
|
||||
|
||||
// 1 = dc message, 2 = reply to dc message
|
||||
if 0 != (*msg).is_dc_message {
|
||||
dc_job_add(context, 200, (*msg).id as libc::c_int, Params::new(), 0);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Moving);
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
1591
src/dc_msg.rs
Normal file
1591
src/dc_msg.rs
Normal file
File diff suppressed because it is too large
Load Diff
346
src/dc_qr.rs
Normal file
346
src/dc_qr.rs
Normal file
@@ -0,0 +1,346 @@
|
||||
use percent_encoding::percent_decode_str;
|
||||
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::key::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
// out-of-band verification
|
||||
// id=contact
|
||||
// text1=groupname
|
||||
// id=contact
|
||||
// id=contact
|
||||
// test1=formatted fingerprint
|
||||
// id=contact
|
||||
// text1=text
|
||||
// text1=URL
|
||||
// text1=error string
|
||||
pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc_lot_t {
|
||||
let mut current_block: u64;
|
||||
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
// must be normalized, if set
|
||||
let mut addr: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
// must be normalized, if set
|
||||
let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut invitenumber: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut qr_parsed: *mut dc_lot_t = dc_lot_new();
|
||||
let mut chat_id: uint32_t = 0i32 as uint32_t;
|
||||
let mut device_msg: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
(*qr_parsed).state = 0i32;
|
||||
if !qr.is_null() {
|
||||
info!(context, 0, "Scanned QR code: {}", as_str(qr),);
|
||||
/* split parameters from the qr code
|
||||
------------------------------------ */
|
||||
if strncasecmp(
|
||||
qr,
|
||||
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
payload =
|
||||
dc_strdup(&*qr.offset(strlen(
|
||||
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
|
||||
) as isize));
|
||||
let mut fragment: *mut libc::c_char = strchr(payload, '#' as i32);
|
||||
if !fragment.is_null() {
|
||||
*fragment = 0i32 as libc::c_char;
|
||||
fragment = fragment.offset(1isize);
|
||||
let param: Params = as_str(fragment).parse().expect("invalid params");
|
||||
addr = param
|
||||
.get(Param::Forwarded)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
if !addr.is_null() {
|
||||
if let Some(ref name_enc) = param.get(Param::SetLongitude) {
|
||||
let name_r = percent_decode_str(name_enc)
|
||||
.decode_utf8()
|
||||
.expect("invalid name");
|
||||
name = normalize_name(name_r).strdup();
|
||||
}
|
||||
invitenumber = param
|
||||
.get(Param::ProfileImage)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
auth = param
|
||||
.get(Param::Auth)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
grpid = param
|
||||
.get(Param::GroupId)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
if !grpid.is_null() {
|
||||
if let Some(grpname_enc) = param.get(Param::GroupName) {
|
||||
let grpname_r = percent_decode_str(grpname_enc)
|
||||
.decode_utf8()
|
||||
.expect("invalid groupname");
|
||||
grpname = grpname_r.strdup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fingerprint = dc_normalize_fingerprint_c(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"mailto:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"mailto:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
payload = dc_strdup(
|
||||
&*qr.offset(strlen(b"mailto:\x00" as *const u8 as *const libc::c_char) as isize),
|
||||
);
|
||||
let query: *mut libc::c_char = strchr(payload, '?' as i32);
|
||||
if !query.is_null() {
|
||||
*query = 0i32 as libc::c_char
|
||||
}
|
||||
addr = dc_strdup(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"SMTP:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
payload = dc_strdup(
|
||||
&*qr.offset(strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char) as isize),
|
||||
);
|
||||
let colon: *mut libc::c_char = strchr(payload, ':' as i32);
|
||||
if !colon.is_null() {
|
||||
*colon = 0i32 as libc::c_char
|
||||
}
|
||||
addr = dc_strdup(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"MATMSG:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"MATMSG:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
/* scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` - there may or may not be linebreaks after the fields */
|
||||
/* does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. we ignore this case. */
|
||||
let to: *mut libc::c_char = strstr(qr, b"TO:\x00" as *const u8 as *const libc::c_char);
|
||||
if !to.is_null() {
|
||||
addr = dc_strdup(&mut *to.offset(3isize));
|
||||
let semicolon: *mut libc::c_char = strchr(addr, ';' as i32);
|
||||
if !semicolon.is_null() {
|
||||
*semicolon = 0i32 as libc::c_char
|
||||
}
|
||||
current_block = 5023038348526654800;
|
||||
} else {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 =
|
||||
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
|
||||
current_block = 16562876845594826114;
|
||||
}
|
||||
} else {
|
||||
if strncasecmp(
|
||||
qr,
|
||||
b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
let lines = dc_split_into_lines(qr);
|
||||
for &key in &lines {
|
||||
dc_trim(key);
|
||||
let mut value: *mut libc::c_char = strchr(key, ':' as i32);
|
||||
if !value.is_null() {
|
||||
*value = 0i32 as libc::c_char;
|
||||
value = value.offset(1isize);
|
||||
let mut semicolon_0: *mut libc::c_char = strchr(key, ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
*semicolon_0 = 0i32 as libc::c_char
|
||||
}
|
||||
if strcasecmp(key, b"EMAIL\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
semicolon_0 = strchr(value, ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
*semicolon_0 = 0i32 as libc::c_char
|
||||
}
|
||||
addr = dc_strdup(value)
|
||||
} else if strcasecmp(key, b"N\x00" as *const u8 as *const libc::c_char)
|
||||
== 0i32
|
||||
{
|
||||
semicolon_0 = strchr(value, ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
semicolon_0 = strchr(semicolon_0.offset(1isize), ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
*semicolon_0 = 0i32 as libc::c_char
|
||||
}
|
||||
}
|
||||
name = dc_strdup(value);
|
||||
dc_str_replace(
|
||||
&mut name,
|
||||
b";\x00" as *const u8 as *const libc::c_char,
|
||||
b",\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
name = normalize_name(as_str(name)).strdup();
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
}
|
||||
current_block = 5023038348526654800;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
/* check the parameters
|
||||
---------------------- */
|
||||
if !addr.is_null() {
|
||||
/* urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases */
|
||||
let mut temp: *mut libc::c_char = dc_urldecode(addr);
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
temp = addr_normalize(as_str(addr)).strdup();
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
if !may_be_valid_addr(as_str(addr)) {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 = dc_strdup(
|
||||
b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 16562876845594826114;
|
||||
} else {
|
||||
current_block = 14116432890150942211;
|
||||
}
|
||||
} else {
|
||||
current_block = 14116432890150942211;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
if !fingerprint.is_null() {
|
||||
if strlen(fingerprint) != 40 {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 = dc_strdup(
|
||||
b"Bad fingerprint length in QR code.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
current_block = 16562876845594826114;
|
||||
} else {
|
||||
current_block = 5409161009579131794;
|
||||
}
|
||||
} else {
|
||||
current_block = 5409161009579131794;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
if !fingerprint.is_null() {
|
||||
let peerstate = Peerstate::from_fingerprint(
|
||||
context,
|
||||
&context.sql,
|
||||
as_str(fingerprint),
|
||||
);
|
||||
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
|
||||
if let Some(peerstate) = peerstate {
|
||||
(*qr_parsed).state = 210i32;
|
||||
let addr = peerstate
|
||||
.addr
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
"",
|
||||
addr,
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
(*qr_parsed).id,
|
||||
2i32,
|
||||
&mut chat_id,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
device_msg = dc_mprintf(
|
||||
b"%s verified.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
peerstate.addr,
|
||||
)
|
||||
} else {
|
||||
(*qr_parsed).text1 =
|
||||
dc_format_fingerprint_c(fingerprint);
|
||||
(*qr_parsed).state = 230i32
|
||||
}
|
||||
} else {
|
||||
if !grpid.is_null() && !grpname.is_null() {
|
||||
(*qr_parsed).state = 202i32;
|
||||
(*qr_parsed).text1 = dc_strdup(grpname);
|
||||
(*qr_parsed).text2 = dc_strdup(grpid)
|
||||
} else {
|
||||
(*qr_parsed).state = 200i32
|
||||
}
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
as_str(name),
|
||||
as_str(addr),
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
|
||||
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
|
||||
(*qr_parsed).auth = dc_strdup(auth)
|
||||
}
|
||||
} else if !addr.is_null() {
|
||||
(*qr_parsed).state = 320i32;
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
as_str(name),
|
||||
as_str(addr),
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
} else if strstr(
|
||||
qr,
|
||||
b"http://\x00" as *const u8 as *const libc::c_char,
|
||||
) == qr as *mut libc::c_char
|
||||
|| strstr(
|
||||
qr,
|
||||
b"https://\x00" as *const u8 as *const libc::c_char,
|
||||
) == qr as *mut libc::c_char
|
||||
{
|
||||
(*qr_parsed).state = 332i32;
|
||||
(*qr_parsed).text1 = dc_strdup(qr)
|
||||
} else {
|
||||
(*qr_parsed).state = 330i32;
|
||||
(*qr_parsed).text1 = dc_strdup(qr)
|
||||
}
|
||||
if !device_msg.is_null() {
|
||||
dc_add_device_msg(context, chat_id, device_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(addr as *mut libc::c_void);
|
||||
free(fingerprint as *mut libc::c_void);
|
||||
free(payload as *mut libc::c_void);
|
||||
free(name as *mut libc::c_void);
|
||||
free(invitenumber as *mut libc::c_void);
|
||||
free(auth as *mut libc::c_void);
|
||||
free(device_msg as *mut libc::c_void);
|
||||
free(grpname as *mut libc::c_void);
|
||||
free(grpid as *mut libc::c_void);
|
||||
|
||||
qr_parsed
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1089
src/dc_saxparser.rs
Normal file
1089
src/dc_saxparser.rs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,49 +1,64 @@
|
||||
use crate::dc_dehtml::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Simplify {
|
||||
pub is_forwarded: bool,
|
||||
}
|
||||
|
||||
/// Return index of footer line in vector of message lines, or vector length if
|
||||
/// no footer is found.
|
||||
///
|
||||
/// Also return whether not-standard (rfc3676, §4.3) footer is found.
|
||||
fn find_message_footer(lines: &[&str]) -> (usize, bool) {
|
||||
for ix in 0..lines.len() {
|
||||
let line = lines[ix];
|
||||
|
||||
// quoted-printable may encode `-- ` to `-- =20` which is converted
|
||||
// back to `-- `
|
||||
match line.as_ref() {
|
||||
"-- " | "-- " => return (ix, false),
|
||||
"--" | "---" | "----" => return (ix, true),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
return (lines.len(), false);
|
||||
pub is_cut_at_begin: bool,
|
||||
pub is_cut_at_end: bool,
|
||||
}
|
||||
|
||||
impl Simplify {
|
||||
pub fn new() -> Self {
|
||||
Simplify {
|
||||
is_forwarded: false,
|
||||
is_cut_at_begin: false,
|
||||
is_cut_at_end: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplify and normalise text: Remove quotes, signatures, unnecessary
|
||||
/// lineends etc.
|
||||
/// The data returned from simplify() must be free()'d when no longer used.
|
||||
pub fn simplify(&mut self, input: &str, is_html: bool, is_msgrmsg: bool) -> String {
|
||||
let mut out = if is_html {
|
||||
dc_dehtml(input)
|
||||
} else {
|
||||
input.to_string()
|
||||
};
|
||||
pub unsafe fn simplify(
|
||||
&mut self,
|
||||
in_unterminated: *const libc::c_char,
|
||||
in_bytes: libc::c_int,
|
||||
is_html: bool,
|
||||
is_msgrmsg: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
if in_bytes <= 0 {
|
||||
return "".strdup();
|
||||
}
|
||||
|
||||
out.retain(|c| c != '\r');
|
||||
out = self.simplify_plain_text(&out, is_msgrmsg);
|
||||
out.retain(|c| c != '\r');
|
||||
/* create a copy of the given buffer */
|
||||
let mut out: *mut libc::c_char;
|
||||
let mut temp: *mut libc::c_char;
|
||||
self.is_forwarded = false;
|
||||
self.is_cut_at_begin = false;
|
||||
self.is_cut_at_end = false;
|
||||
out = strndup(
|
||||
in_unterminated as *mut libc::c_char,
|
||||
in_bytes as libc::c_ulong,
|
||||
);
|
||||
if out.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
if is_html {
|
||||
temp = dc_dehtml(out);
|
||||
if !temp.is_null() {
|
||||
free(out as *mut libc::c_void);
|
||||
out = temp
|
||||
}
|
||||
}
|
||||
dc_remove_cr_chars(out);
|
||||
temp = self.simplify_plain_text(out, is_msgrmsg);
|
||||
if !temp.is_null() {
|
||||
free(out as *mut libc::c_void);
|
||||
out = temp
|
||||
}
|
||||
dc_remove_cr_chars(out);
|
||||
|
||||
out
|
||||
}
|
||||
@@ -52,75 +67,102 @@ impl Simplify {
|
||||
* Simplify Plain Text
|
||||
*/
|
||||
#[allow(non_snake_case)]
|
||||
fn simplify_plain_text(&mut self, buf_terminated: &str, is_msgrmsg: bool) -> String {
|
||||
unsafe fn simplify_plain_text(
|
||||
&mut self,
|
||||
buf_terminated: *const libc::c_char,
|
||||
is_msgrmsg: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
/* This function ...
|
||||
... removes all text after the line `-- ` (footer mark)
|
||||
... removes full quotes at the beginning and at the end of the text -
|
||||
these are all lines starting with the character `>`
|
||||
... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */
|
||||
/* split the given buffer into lines */
|
||||
let lines: Vec<_> = buf_terminated.split('\n').collect();
|
||||
let lines = dc_split_into_lines(buf_terminated);
|
||||
let mut l_first: usize = 0;
|
||||
let mut is_cut_at_begin = false;
|
||||
let (mut l_last, mut is_cut_at_end) = find_message_footer(&lines);
|
||||
|
||||
let mut l_last = lines.len();
|
||||
let mut line: *mut libc::c_char;
|
||||
let mut footer_mark: libc::c_int = 0i32;
|
||||
for l in l_first..l_last {
|
||||
line = lines[l];
|
||||
if strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
footer_mark = 1i32
|
||||
}
|
||||
if strcmp(line, b"--\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(line, b"---\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
footer_mark = 1i32;
|
||||
self.is_cut_at_end = true
|
||||
}
|
||||
if 0 != footer_mark {
|
||||
l_last = l;
|
||||
/* done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if l_last > l_first + 2 {
|
||||
let line0 = lines[l_first];
|
||||
let line1 = lines[l_first + 1];
|
||||
let line2 = lines[l_first + 2];
|
||||
if line0 == "---------- Forwarded message ----------"
|
||||
&& line1.starts_with("From: ")
|
||||
&& line2.is_empty()
|
||||
let line0: *mut libc::c_char = lines[l_first];
|
||||
let line1: *mut libc::c_char = lines[l_first + 1];
|
||||
let line2: *mut libc::c_char = lines[l_first + 2];
|
||||
if strcmp(
|
||||
line0,
|
||||
b"---------- Forwarded message ----------\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
&& strncmp(line1, b"From: \x00" as *const u8 as *const libc::c_char, 6) == 0i32
|
||||
&& *line2.offset(0isize) as libc::c_int == 0i32
|
||||
{
|
||||
self.is_forwarded = true;
|
||||
l_first += 3
|
||||
}
|
||||
}
|
||||
for l in l_first..l_last {
|
||||
let line = lines[l];
|
||||
if line == "-----"
|
||||
|| line == "_____"
|
||||
|| line == "====="
|
||||
|| line == "*****"
|
||||
|| line == "~~~~~"
|
||||
line = lines[l];
|
||||
if strncmp(line, b"-----\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|
||||
|| strncmp(line, b"_____\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|
||||
|| strncmp(line, b"=====\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|
||||
|| strncmp(line, b"*****\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|
||||
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|
||||
{
|
||||
l_last = l;
|
||||
is_cut_at_end = true;
|
||||
self.is_cut_at_end = true;
|
||||
/* done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !is_msgrmsg {
|
||||
if 0 == is_msgrmsg {
|
||||
let mut l_lastQuotedLine = None;
|
||||
for l in (l_first..l_last).rev() {
|
||||
let line = lines[l];
|
||||
line = lines[l];
|
||||
if is_plain_quote(line) {
|
||||
l_lastQuotedLine = Some(l)
|
||||
} else if !is_empty_line(line) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(last_quoted_line) = l_lastQuotedLine {
|
||||
l_last = last_quoted_line;
|
||||
is_cut_at_end = true;
|
||||
if l_lastQuotedLine.is_some() {
|
||||
l_last = l_lastQuotedLine.unwrap();
|
||||
self.is_cut_at_end = true;
|
||||
if l_last > 1 {
|
||||
if is_empty_line(lines[l_last - 1]) {
|
||||
l_last -= 1
|
||||
}
|
||||
}
|
||||
if l_last > 1 {
|
||||
let line = lines[l_last - 1];
|
||||
line = lines[l_last - 1];
|
||||
if is_quoted_headline(line) {
|
||||
l_last -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !is_msgrmsg {
|
||||
if 0 == is_msgrmsg {
|
||||
let mut l_lastQuotedLine_0 = None;
|
||||
let mut hasQuotedHeadline = 0;
|
||||
for l in l_first..l_last {
|
||||
let line = lines[l];
|
||||
line = lines[l];
|
||||
if is_plain_quote(line) {
|
||||
l_lastQuotedLine_0 = Some(l)
|
||||
} else if !is_empty_line(line) {
|
||||
@@ -135,21 +177,21 @@ impl Simplify {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(last_quoted_line) = l_lastQuotedLine_0 {
|
||||
l_first = last_quoted_line + 1;
|
||||
is_cut_at_begin = true
|
||||
if l_lastQuotedLine_0.is_some() {
|
||||
l_first = l_lastQuotedLine_0.unwrap() + 1;
|
||||
self.is_cut_at_begin = true
|
||||
}
|
||||
}
|
||||
/* re-create buffer from the remaining lines */
|
||||
let mut ret = String::new();
|
||||
if is_cut_at_begin {
|
||||
if self.is_cut_at_begin {
|
||||
ret += "[...]";
|
||||
}
|
||||
/* we write empty lines only in case and non-empty line follows */
|
||||
let mut pending_linebreaks: libc::c_int = 0i32;
|
||||
let mut content_lines_added: libc::c_int = 0i32;
|
||||
for l in l_first..l_last {
|
||||
let line = lines[l];
|
||||
line = lines[l];
|
||||
if is_empty_line(line) {
|
||||
pending_linebreaks += 1
|
||||
} else {
|
||||
@@ -163,105 +205,144 @@ impl Simplify {
|
||||
}
|
||||
}
|
||||
// the incoming message might contain invalid UTF8
|
||||
ret += line;
|
||||
ret += &to_string_lossy(line);
|
||||
content_lines_added += 1;
|
||||
pending_linebreaks = 1i32
|
||||
}
|
||||
}
|
||||
if is_cut_at_end && (!is_cut_at_begin || 0 != content_lines_added) {
|
||||
if self.is_cut_at_end && (!self.is_cut_at_begin || 0 != content_lines_added) {
|
||||
ret += " [...]";
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
|
||||
ret
|
||||
ret.strdup()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools
|
||||
*/
|
||||
fn is_empty_line(buf: &str) -> bool {
|
||||
// XXX: can it be simplified to buf.chars().all(|c| c.is_whitespace())?
|
||||
//
|
||||
// Strictly speaking, it is not equivalent (^A is not whitespace, but less than ' '),
|
||||
// but having control sequences in email body?!
|
||||
//
|
||||
// See discussion at: https://github.com/deltachat/deltachat-core-rust/pull/402#discussion_r317062392
|
||||
for c in buf.chars() {
|
||||
if c > ' ' {
|
||||
unsafe fn is_empty_line(buf: *const libc::c_char) -> bool {
|
||||
/* force unsigned - otherwise the `> ' '` comparison will fail */
|
||||
let mut p1: *const libc::c_uchar = buf as *const libc::c_uchar;
|
||||
while 0 != *p1 {
|
||||
if *p1 as libc::c_int > ' ' as i32 {
|
||||
return false;
|
||||
}
|
||||
p1 = p1.offset(1isize)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn is_quoted_headline(buf: &str) -> bool {
|
||||
unsafe fn is_quoted_headline(buf: *const libc::c_char) -> bool {
|
||||
/* This function may be called for the line _directly_ before a quote.
|
||||
The function checks if the line contains sth. like "On 01.02.2016, xy@z wrote:" in various languages.
|
||||
- Currently, we simply check if the last character is a ':'.
|
||||
- Checking for the existence of an email address may fail (headlines may show the user's name instead of the address) */
|
||||
let buf_len: libc::c_int = strlen(buf) as libc::c_int;
|
||||
if buf_len > 80i32 {
|
||||
return false;
|
||||
}
|
||||
if buf_len > 0i32 && *buf.offset((buf_len - 1i32) as isize) as libc::c_int == ':' as i32 {
|
||||
return true;
|
||||
}
|
||||
|
||||
buf.len() <= 80 && buf.ends_with(':')
|
||||
false
|
||||
}
|
||||
|
||||
fn is_plain_quote(buf: &str) -> bool {
|
||||
buf.starts_with(">")
|
||||
unsafe fn is_plain_quote(buf: *const libc::c_char) -> bool {
|
||||
if *buf.offset(0isize) as libc::c_int == '>' as i32 {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn test_simplify_trim() {
|
||||
let mut simplify = Simplify::new();
|
||||
let html = "\r\r\nline1<br>\r\n\r\n\r\rline2\n\r";
|
||||
let plain = simplify.simplify(html, true, false);
|
||||
unsafe {
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"\r\r\nline1<br>\r\n\r\n\r\rline2\n\r\x00" as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(plain, "line1\nline2");
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"line1\nline2",
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_parse_href() {
|
||||
let mut simplify = Simplify::new();
|
||||
let html = "<a href=url>text</a";
|
||||
let plain = simplify.simplify(html, true, false);
|
||||
unsafe {
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<a href=url>text</a\x00" as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(plain, "[text](url)");
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"[text](url)",
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_bold_text() {
|
||||
let mut simplify = Simplify::new();
|
||||
let html = "<!DOCTYPE name [<!DOCTYPE ...>]><!-- comment -->text <b><?php echo ... ?>bold</b><![CDATA[<>]]>";
|
||||
let plain = simplify.simplify(html, true, false);
|
||||
unsafe {
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<!DOCTYPE name [<!DOCTYPE ...>]><!-- comment -->text <b><?php echo ... ?>bold</b><![CDATA[<>]]>\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(plain, "text *bold*<>");
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"text *bold*<>",
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_html_encoded() {
|
||||
let mut simplify = Simplify::new();
|
||||
let html =
|
||||
"<>"'& äÄöÖüÜß fooÆçÇ ♦‎‏‌&noent;‍";
|
||||
unsafe {
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<>"'& äÄöÖüÜß fooÆçÇ ♦&noent;‎‏‌‍\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
let plain = simplify.simplify(html, true, false);
|
||||
assert_eq!(
|
||||
strcmp(plain,
|
||||
b"<>\"\'& \xc3\xa4\xc3\x84\xc3\xb6\xc3\x96\xc3\xbc\xc3\x9c\xc3\x9f foo\xc3\x86\xc3\xa7\xc3\x87 \xe2\x99\xa6&noent;\x00"
|
||||
as *const u8 as *const libc::c_char),
|
||||
0,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
plain,
|
||||
"<>\"\'& äÄöÖüÜß fooÆçÇ \u{2666}\u{200e}\u{200f}\u{200c}&noent;\u{200d}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_utilities() {
|
||||
assert!(is_empty_line(" \t"));
|
||||
assert!(is_empty_line(""));
|
||||
assert!(is_empty_line(" \r"));
|
||||
assert!(!is_empty_line(" x"));
|
||||
assert!(is_plain_quote("> hello world"));
|
||||
assert!(is_plain_quote(">>"));
|
||||
assert!(!is_plain_quote("Life is pain"));
|
||||
assert!(!is_plain_quote(""));
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr;
|
||||
|
||||
use charset::Charset;
|
||||
use mmime::mailmime_decode::*;
|
||||
@@ -11,7 +10,7 @@ use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[inline]
|
||||
fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
pub fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
if c < std::u8::MAX as libc::c_int {
|
||||
(c as u8 as char).is_ascii_alphanumeric() as libc::c_int
|
||||
} else {
|
||||
@@ -19,6 +18,48 @@ fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut pstr: *const libc::c_char = to_encode;
|
||||
if to_encode.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
let buf: *mut libc::c_char =
|
||||
malloc(strlen(to_encode).wrapping_mul(3).wrapping_add(1)) as *mut libc::c_char;
|
||||
let mut pbuf: *mut libc::c_char = buf;
|
||||
assert!(!buf.is_null());
|
||||
|
||||
while 0 != *pstr {
|
||||
if 0 != isalnum(*pstr as libc::c_int)
|
||||
|| *pstr as libc::c_int == '-' as i32
|
||||
|| *pstr as libc::c_int == '_' as i32
|
||||
|| *pstr as libc::c_int == '.' as i32
|
||||
|| *pstr as libc::c_int == '~' as i32
|
||||
{
|
||||
let fresh0 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh0 = *pstr
|
||||
} else if *pstr as libc::c_int == ' ' as i32 {
|
||||
let fresh1 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh1 = '+' as i32 as libc::c_char
|
||||
} else {
|
||||
let fresh2 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh2 = '%' as i32 as libc::c_char;
|
||||
let fresh3 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh3 = int_2_uppercase_hex((*pstr as libc::c_int >> 4i32) as libc::c_char);
|
||||
let fresh4 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh4 = int_2_uppercase_hex((*pstr as libc::c_int & 15i32) as libc::c_char)
|
||||
}
|
||||
pstr = pstr.offset(1isize)
|
||||
}
|
||||
*pbuf = '\u{0}' as i32 as libc::c_char;
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* URL encoding and decoding, RFC 3986
|
||||
******************************************************************************/
|
||||
@@ -80,7 +121,7 @@ fn hex_2_int(ch: libc::c_char) -> libc::c_char {
|
||||
|
||||
pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut ok_to_continue = true;
|
||||
let mut ret_str: *mut libc::c_char = ptr::null_mut();
|
||||
let mut ret_str: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut cur: *const libc::c_char = to_encode;
|
||||
let mmapstr: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
if to_encode.is_null() || mmapstr.is_null() {
|
||||
@@ -271,9 +312,9 @@ unsafe fn to_be_quoted(word: *const libc::c_char, size: size_t) -> bool {
|
||||
|
||||
pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_char {
|
||||
if in_0.is_null() {
|
||||
return ptr::null_mut();
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
let mut out: *mut libc::c_char = ptr::null_mut();
|
||||
let mut out: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut cur_token: size_t = 0i32 as size_t;
|
||||
let r: libc::c_int = mailmime_encoded_phrase_parse(
|
||||
b"iso-8859-1\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -290,8 +331,7 @@ pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_encode_modified_utf7(
|
||||
pub unsafe fn dc_encode_modified_utf7(
|
||||
mut to_encode: *const libc::c_char,
|
||||
change_spaces: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
@@ -435,15 +475,13 @@ unsafe fn dc_encode_modified_utf7(
|
||||
******************************************************************************/
|
||||
|
||||
// UTF7 modified base64 alphabet
|
||||
#[cfg(test)]
|
||||
static mut BASE64CHARS: [libc::c_char; 65] = [
|
||||
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
|
||||
89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
|
||||
115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 44, 0,
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_decode_modified_utf7(
|
||||
pub unsafe fn dc_decode_modified_utf7(
|
||||
to_decode: *const libc::c_char,
|
||||
change_spaces: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
@@ -617,8 +655,8 @@ pub unsafe fn dc_encode_ext_header(to_encode: *const libc::c_char) -> *mut libc:
|
||||
}
|
||||
|
||||
pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut decoded: *mut libc::c_char = ptr::null_mut();
|
||||
let mut charset: *mut libc::c_char = ptr::null_mut();
|
||||
let mut decoded: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut charset: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut p2: *const libc::c_char;
|
||||
if !to_decode.is_null() {
|
||||
// get char set
|
||||
@@ -653,11 +691,11 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc:
|
||||
}
|
||||
}
|
||||
free(charset as *mut libc::c_void);
|
||||
if !decoded.is_null() {
|
||||
return if !decoded.is_null() {
|
||||
decoded
|
||||
} else {
|
||||
dc_strdup(to_decode)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
@@ -672,7 +710,6 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
@@ -852,15 +889,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dc_urlencode_urldecode() {
|
||||
unsafe {
|
||||
let buf1 = percent_encode(b"Bj\xc3\xb6rn Petersen", NON_ALPHANUMERIC)
|
||||
.to_string()
|
||||
.strdup();
|
||||
let buf1 =
|
||||
dc_urlencode(b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(buf1 as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"Bj%C3%B6rn%20Petersen"
|
||||
"Bj%C3%B6rn+Petersen"
|
||||
);
|
||||
|
||||
let buf2 = dc_urldecode(buf1);
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::sql;
|
||||
|
||||
// Token namespaces
|
||||
#[allow(non_camel_case_types)]
|
||||
type dc_tokennamespc_t = usize;
|
||||
pub type dc_tokennamespc_t = usize;
|
||||
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
|
||||
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
|
||||
|
||||
@@ -14,14 +14,17 @@ pub fn dc_token_save(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: u32,
|
||||
token: &str,
|
||||
token: *const libc::c_char,
|
||||
) -> bool {
|
||||
if token.is_null() {
|
||||
return false;
|
||||
}
|
||||
// foreign_id may be 0
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);",
|
||||
params![namespc as i32, foreign_id as i32, token, time()],
|
||||
params![namespc as i32, foreign_id as i32, as_str(token), time()],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
@@ -30,21 +33,33 @@ pub fn dc_token_lookup(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: u32,
|
||||
) -> Option<String> {
|
||||
context.sql.query_row_col::<_, String>(
|
||||
context,
|
||||
"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;",
|
||||
params![namespc as i32, foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
) -> *mut libc::c_char {
|
||||
context
|
||||
.sql
|
||||
.query_row_col::<_, String>(
|
||||
context,
|
||||
"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;",
|
||||
params![namespc as i32, foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.map(|s| unsafe { s.strdup() })
|
||||
.unwrap_or_else(|| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
pub fn dc_token_exists(context: &Context, namespc: dc_tokennamespc_t, token: &str) -> bool {
|
||||
pub fn dc_token_exists(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
token: *const libc::c_char,
|
||||
) -> bool {
|
||||
if token.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM tokens WHERE namespc=? AND token=?;",
|
||||
params![namespc as i32, token],
|
||||
params![namespc as i32, as_str(token)],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
784
src/dc_tools.rs
784
src/dc_tools.rs
File diff suppressed because it is too large
Load Diff
16
src/error.rs
16
src/error.rs
@@ -18,10 +18,6 @@ pub enum Error {
|
||||
Io(std::io::Error),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Message(String),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Image(image_meta::ImageError),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Utf8(std::str::Utf8Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -50,18 +46,6 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::str::Utf8Error> for Error {
|
||||
fn from(err: std::str::Utf8Error) -> Error {
|
||||
Error::Utf8(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<image_meta::ImageError> for Error {
|
||||
fn from(err: image_meta::ImageError) -> Error {
|
||||
Error::Image(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
($e:expr) => {
|
||||
|
||||
142
src/imap.rs
142
src/imap.rs
@@ -1,9 +1,6 @@
|
||||
use std::ffi::CString;
|
||||
use std::net;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Condvar, Mutex, RwLock,
|
||||
};
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use crate::constants::*;
|
||||
@@ -13,18 +10,19 @@ use crate::dc_tools::CStringExt;
|
||||
use crate::oauth2::dc_get_oauth2_access_token;
|
||||
use crate::types::*;
|
||||
|
||||
const DC_IMAP_SEEN: usize = 0x0001;
|
||||
const DC_REGENERATE: usize = 0x01;
|
||||
pub const DC_IMAP_SEEN: usize = 0x0001;
|
||||
pub const DC_REGENERATE: usize = 0x01;
|
||||
|
||||
const DC_SUCCESS: usize = 3;
|
||||
const DC_ALREADY_DONE: usize = 2;
|
||||
const DC_RETRY_LATER: usize = 1;
|
||||
const DC_FAILED: usize = 0;
|
||||
pub const DC_SUCCESS: usize = 3;
|
||||
pub const DC_ALREADY_DONE: usize = 2;
|
||||
pub const DC_RETRY_LATER: usize = 1;
|
||||
pub const DC_FAILED: usize = 0;
|
||||
|
||||
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
|
||||
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
|
||||
const FETCH_FLAGS: &str = "(FLAGS)";
|
||||
const PREFETCH_FLAGS: &'static str = "(UID ENVELOPE)";
|
||||
const BODY_FLAGS: &'static str = "(FLAGS BODY.PEEK[])";
|
||||
const FETCH_FLAGS: &'static str = "(FLAGS)";
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Imap {
|
||||
config: Arc<RwLock<ImapConfig>>,
|
||||
watch: Arc<(Mutex<bool>, Condvar)>,
|
||||
@@ -37,8 +35,6 @@ pub struct Imap {
|
||||
session: Arc<Mutex<Option<Session>>>,
|
||||
stream: Arc<RwLock<Option<net::TcpStream>>>,
|
||||
connected: Arc<Mutex<bool>>,
|
||||
|
||||
should_reconnect: AtomicBool,
|
||||
}
|
||||
|
||||
struct OAuth2 {
|
||||
@@ -59,13 +55,13 @@ impl imap::Authenticator for OAuth2 {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FolderMeaning {
|
||||
pub enum FolderMeaning {
|
||||
Unknown,
|
||||
SentObjects,
|
||||
Other,
|
||||
}
|
||||
|
||||
enum Client {
|
||||
pub enum Client {
|
||||
Secure(
|
||||
imap::Client<native_tls::TlsStream<net::TcpStream>>,
|
||||
net::TcpStream,
|
||||
@@ -73,12 +69,12 @@ enum Client {
|
||||
Insecure(imap::Client<net::TcpStream>, net::TcpStream),
|
||||
}
|
||||
|
||||
enum Session {
|
||||
pub enum Session {
|
||||
Secure(imap::Session<native_tls::TlsStream<net::TcpStream>>),
|
||||
Insecure(imap::Session<net::TcpStream>),
|
||||
}
|
||||
|
||||
enum IdleHandle<'a> {
|
||||
pub enum IdleHandle<'a> {
|
||||
Secure(imap::extensions::idle::Handle<'a, native_tls::TlsStream<net::TcpStream>>),
|
||||
Insecure(imap::extensions::idle::Handle<'a, net::TcpStream>),
|
||||
}
|
||||
@@ -203,8 +199,8 @@ impl Session {
|
||||
|
||||
pub fn create<S: AsRef<str>>(&mut self, mailbox_name: S) -> imap::error::Result<()> {
|
||||
match self {
|
||||
Session::Secure(i) => i.create(mailbox_name),
|
||||
Session::Insecure(i) => i.create(mailbox_name),
|
||||
Session::Secure(i) => i.subscribe(mailbox_name),
|
||||
Session::Insecure(i) => i.subscribe(mailbox_name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,8 +260,8 @@ impl Session {
|
||||
|
||||
pub fn idle(&mut self) -> imap::error::Result<IdleHandle> {
|
||||
match self {
|
||||
Session::Secure(i) => i.idle().map(IdleHandle::Secure),
|
||||
Session::Insecure(i) => i.idle().map(IdleHandle::Insecure),
|
||||
Session::Secure(i) => i.idle().map(|h| IdleHandle::Secure(h)),
|
||||
Session::Insecure(i) => i.idle().map(|h| IdleHandle::Insecure(h)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,7 +303,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
struct ImapConfig {
|
||||
pub struct ImapConfig {
|
||||
pub addr: String,
|
||||
pub imap_server: String,
|
||||
pub imap_port: u16,
|
||||
@@ -317,6 +313,7 @@ struct ImapConfig {
|
||||
pub selected_folder: Option<String>,
|
||||
pub selected_mailbox: Option<imap::types::Mailbox>,
|
||||
pub selected_folder_needs_expunge: bool,
|
||||
pub should_reconnect: bool,
|
||||
pub can_idle: bool,
|
||||
pub has_xlist: bool,
|
||||
pub imap_delimiter: char,
|
||||
@@ -335,6 +332,7 @@ impl Default for ImapConfig {
|
||||
selected_folder: None,
|
||||
selected_mailbox: None,
|
||||
selected_folder_needs_expunge: false,
|
||||
should_reconnect: false,
|
||||
can_idle: false,
|
||||
has_xlist: false,
|
||||
imap_delimiter: '.',
|
||||
@@ -362,7 +360,6 @@ impl Imap {
|
||||
precheck_imf,
|
||||
receive_imf,
|
||||
connected: Arc::new(Mutex::new(false)),
|
||||
should_reconnect: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +368,7 @@ impl Imap {
|
||||
}
|
||||
|
||||
pub fn should_reconnect(&self) -> bool {
|
||||
self.should_reconnect.load(Ordering::Relaxed)
|
||||
self.config.read().unwrap().should_reconnect
|
||||
}
|
||||
|
||||
fn setup_handle_if_needed(&self, context: &Context) -> bool {
|
||||
@@ -384,7 +381,7 @@ impl Imap {
|
||||
}
|
||||
|
||||
if self.is_connected() && self.stream.read().unwrap().is_some() {
|
||||
self.should_reconnect.store(false, Ordering::Relaxed);
|
||||
self.config.write().unwrap().should_reconnect = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -454,7 +451,7 @@ impl Imap {
|
||||
}
|
||||
};
|
||||
|
||||
self.should_reconnect.store(false, Ordering::Relaxed);
|
||||
self.config.write().unwrap().should_reconnect = false;
|
||||
|
||||
match login_res {
|
||||
Ok((session, stream)) => {
|
||||
@@ -479,19 +476,25 @@ impl Imap {
|
||||
0, "IMAP unsetup_handle step 1 (closing down stream)."
|
||||
);
|
||||
let stream = self.stream.write().unwrap().take();
|
||||
if let Some(stream) = stream {
|
||||
if let Err(err) = stream.shutdown(net::Shutdown::Both) {
|
||||
eprintln!("failed to shutdown connection: {:?}", err);
|
||||
if stream.is_some() {
|
||||
match stream.unwrap().shutdown(net::Shutdown::Both) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
eprintln!("failed to shutdown connection: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
);
|
||||
if let Some(mut session) = self.session.lock().unwrap().take() {
|
||||
if let Err(err) = session.close() {
|
||||
eprintln!("failed to close connection: {:?}", err);
|
||||
let session = self.session.lock().unwrap().take();
|
||||
if session.is_some() {
|
||||
match session.unwrap().close() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
eprintln!("failed to close connection: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,7 +539,7 @@ impl Imap {
|
||||
let mut config = self.config.write().unwrap();
|
||||
config.addr = addr.to_string();
|
||||
config.imap_server = imap_server.to_string();
|
||||
config.imap_port = imap_port;
|
||||
config.imap_port = imap_port.into();
|
||||
config.imap_user = imap_user.to_string();
|
||||
config.imap_pw = imap_pw.to_string();
|
||||
config.server_flags = server_flags;
|
||||
@@ -547,12 +550,14 @@ impl Imap {
|
||||
return false;
|
||||
}
|
||||
|
||||
let (teardown, can_idle, has_xlist) = match &mut *self.session.lock().unwrap() {
|
||||
let teardown: bool;
|
||||
|
||||
match &mut *self.session.lock().unwrap() {
|
||||
Some(ref mut session) => {
|
||||
if let Ok(caps) = session.capabilities() {
|
||||
if !context.sql.is_open() {
|
||||
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
(true, false, false)
|
||||
teardown = true;
|
||||
} else {
|
||||
let can_idle = caps.has("IDLE");
|
||||
let has_xlist = caps.has("XLIST");
|
||||
@@ -569,23 +574,24 @@ impl Imap {
|
||||
lp.mail_user,
|
||||
caps_list,
|
||||
);
|
||||
(false, can_idle, has_xlist)
|
||||
self.config.write().unwrap().can_idle = can_idle;
|
||||
self.config.write().unwrap().has_xlist = has_xlist;
|
||||
*self.connected.lock().unwrap() = true;
|
||||
teardown = false;
|
||||
}
|
||||
} else {
|
||||
(true, false, false)
|
||||
teardown = true;
|
||||
}
|
||||
}
|
||||
None => (true, false, false),
|
||||
};
|
||||
|
||||
None => {
|
||||
teardown = true;
|
||||
}
|
||||
}
|
||||
if teardown {
|
||||
self.unsetup_handle(context);
|
||||
self.free_connect_params();
|
||||
false
|
||||
} else {
|
||||
self.config.write().unwrap().can_idle = can_idle;
|
||||
self.config.write().unwrap().has_xlist = has_xlist;
|
||||
*self.connected.lock().unwrap() = true;
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -683,8 +689,9 @@ impl Imap {
|
||||
err
|
||||
);
|
||||
|
||||
self.config.write().unwrap().selected_folder = None;
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
let mut config = self.config.write().unwrap();
|
||||
config.selected_folder = None;
|
||||
config.should_reconnect = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -770,7 +777,7 @@ impl Imap {
|
||||
match session.fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(list) => list,
|
||||
Err(_err) => {
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
self.config.write().unwrap().should_reconnect = true;
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
@@ -815,7 +822,7 @@ impl Imap {
|
||||
match session.uid_fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(list) => list,
|
||||
Err(err) => {
|
||||
warn!(context, 0, "failed to fetch uids: {}", err);
|
||||
eprintln!("fetch err: {:?}", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -921,13 +928,15 @@ impl Imap {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut retry_later = false;
|
||||
|
||||
let set = format!("{}", server_uid);
|
||||
|
||||
let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_fetch(set, BODY_FLAGS) {
|
||||
Ok(msgs) => msgs,
|
||||
Err(err) => {
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
self.config.write().unwrap().should_reconnect = true;
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
@@ -937,11 +946,17 @@ impl Imap {
|
||||
self.should_reconnect(),
|
||||
err
|
||||
);
|
||||
return 0;
|
||||
|
||||
if self.should_reconnect() {
|
||||
// maybe we should also retry on other errors, however, we should check this carefully, as this may result in a dead lock!
|
||||
retry_later = true;
|
||||
}
|
||||
|
||||
return if retry_later { 0 } else { 1 };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return 1;
|
||||
return if retry_later { 0 } else { 1 };
|
||||
};
|
||||
|
||||
if msgs.is_empty() {
|
||||
@@ -989,7 +1004,11 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
1
|
||||
if retry_later {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn idle(&self, context: &Context) {
|
||||
@@ -1054,7 +1073,7 @@ impl Imap {
|
||||
context,
|
||||
0, "IMAP-IDLE wait cancelled, we will reconnect soon."
|
||||
);
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
self.config.write().unwrap().should_reconnect = true;
|
||||
}
|
||||
_ => {
|
||||
warn!(context, 0, "IMAP-IDLE returns unknown value: {}", err);
|
||||
@@ -1557,23 +1576,20 @@ impl Imap {
|
||||
info!(context, 0, "MVBOX-folder created.",);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("create error: {:?}", err);
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})",
|
||||
err
|
||||
0, "Cannot create MVBOX-folder, using trying INBOX subfolder."
|
||||
);
|
||||
|
||||
match session.create(&fallback_folder) {
|
||||
Ok(_) => {
|
||||
mvbox_folder = Some(fallback_folder);
|
||||
info!(
|
||||
context,
|
||||
0, "MVBOX-folder created as INBOX subfolder. ({})", err
|
||||
);
|
||||
info!(context, 0, "MVBOX-folder created as INBOX subfolder.",);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "Cannot create MVBOX-folder. ({})", err);
|
||||
eprintln!("create error: {:?}", err);
|
||||
warn!(context, 0, "Cannot create MVBOX-folder.",);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1131
src/job.rs
1131
src/job.rs
File diff suppressed because it is too large
Load Diff
@@ -1,181 +0,0 @@
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use crate::configure::*;
|
||||
use crate::context::Context;
|
||||
use crate::imap::Imap;
|
||||
|
||||
pub struct JobThread {
|
||||
pub name: &'static str,
|
||||
pub folder_config_name: &'static str,
|
||||
pub imap: Imap,
|
||||
pub state: Arc<(Mutex<JobState>, Condvar)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct JobState {
|
||||
idle: bool,
|
||||
jobs_needed: i32,
|
||||
suspended: bool,
|
||||
using_handle: bool,
|
||||
}
|
||||
|
||||
impl JobThread {
|
||||
pub fn new(name: &'static str, folder_config_name: &'static str, imap: Imap) -> Self {
|
||||
JobThread {
|
||||
name,
|
||||
folder_config_name,
|
||||
imap,
|
||||
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suspend(&self, context: &Context) {
|
||||
info!(context, 0, "Suspending {}-thread.", self.name,);
|
||||
{
|
||||
self.state.0.lock().unwrap().suspended = true;
|
||||
}
|
||||
self.interrupt_idle(context);
|
||||
loop {
|
||||
let using_handle = self.state.0.lock().unwrap().using_handle;
|
||||
if !using_handle {
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsuspend(&self, context: &Context) {
|
||||
info!(context, 0, "Unsuspending {}-thread.", self.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.suspended = false;
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
pub fn interrupt_idle(&self, context: &Context) {
|
||||
{
|
||||
self.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, 0, "Interrupting {}-IDLE...", self.name);
|
||||
|
||||
self.imap.interrupt_idle();
|
||||
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
pub fn fetch(&mut self, context: &Context, use_network: bool) {
|
||||
{
|
||||
let &(ref lock, _) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if state.suspended {
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = true;
|
||||
}
|
||||
|
||||
if use_network {
|
||||
let start = std::time::Instant::now();
|
||||
if self.connect_to_imap(context) {
|
||||
info!(context, 0, "{}-fetch started...", self.name);
|
||||
self.imap.fetch(context);
|
||||
|
||||
if self.imap.should_reconnect() {
|
||||
info!(context, 0, "{}-fetch aborted, starting over...", self.name,);
|
||||
self.imap.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
self.name,
|
||||
start.elapsed().as_millis(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
|
||||
fn connect_to_imap(&self, context: &Context) -> bool {
|
||||
if self.imap.is_connected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
|
||||
|
||||
if ret_connected {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
self.imap.configure_folders(context, 0x1);
|
||||
}
|
||||
|
||||
if let Some(mvbox_name) = context.sql.get_config(context, self.folder_config_name) {
|
||||
self.imap.set_watch_folder(mvbox_name);
|
||||
} else {
|
||||
self.imap.disconnect(context);
|
||||
ret_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
ret_connected
|
||||
}
|
||||
|
||||
pub fn idle(&self, context: &Context, use_network: bool) {
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
self.name,
|
||||
);
|
||||
state.jobs_needed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if state.suspended {
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = true;
|
||||
|
||||
if !use_network {
|
||||
state.using_handle = false;
|
||||
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.connect_to_imap(context);
|
||||
info!(context, 0, "{}-IDLE started...", self.name,);
|
||||
self.imap.idle(context);
|
||||
info!(context, 0, "{}-IDLE ended.", self.name);
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ impl Key {
|
||||
}
|
||||
|
||||
pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option<Self> {
|
||||
if bytes.is_empty() {
|
||||
if 0 == bytes.len() {
|
||||
return None;
|
||||
}
|
||||
let res: Result<Key, _> = match key_type {
|
||||
@@ -283,7 +283,7 @@ impl Key {
|
||||
Key::Public(_) => None,
|
||||
Key::Secret(k) => {
|
||||
let pub_key = k.public_key();
|
||||
pub_key.sign(k, || "".into()).map(Key::Public).ok()
|
||||
pub_key.sign(k, || "".into()).map(|k| Key::Public(k)).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
53
src/lib.rs
53
src/lib.rs
@@ -1,9 +1,4 @@
|
||||
#![deny(clippy::correctness)]
|
||||
// TODO: make all of these errors, such that clippy actually passes.
|
||||
#![warn(clippy::all, clippy::perf, clippy::not_unsafe_ptr_arg_deref)]
|
||||
// This is nice, but for now just annoying.
|
||||
#![allow(clippy::unreadable_literal)]
|
||||
#![feature(ptr_wrapping_offset_from)]
|
||||
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
@@ -22,46 +17,50 @@ mod log;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
|
||||
mod aheader;
|
||||
pub mod chat;
|
||||
pub mod aheader;
|
||||
pub mod chatlist;
|
||||
pub mod config;
|
||||
pub mod configure;
|
||||
pub mod constants;
|
||||
pub mod contact;
|
||||
pub mod context;
|
||||
mod e2ee;
|
||||
mod imap;
|
||||
pub mod job;
|
||||
mod job_thread;
|
||||
pub mod imap;
|
||||
pub mod key;
|
||||
pub mod keyring;
|
||||
pub mod location;
|
||||
pub mod lot;
|
||||
pub mod message;
|
||||
pub mod oauth2;
|
||||
mod param;
|
||||
pub mod param;
|
||||
pub mod peerstate;
|
||||
pub mod pgp;
|
||||
pub mod qr;
|
||||
mod smtp;
|
||||
pub mod smtp;
|
||||
pub mod sql;
|
||||
mod stock;
|
||||
pub mod stock;
|
||||
pub mod types;
|
||||
pub mod x;
|
||||
|
||||
pub mod dc_array;
|
||||
mod dc_dehtml;
|
||||
pub mod dc_chat;
|
||||
pub mod dc_configure;
|
||||
pub mod dc_dehtml;
|
||||
pub mod dc_e2ee;
|
||||
pub mod dc_imex;
|
||||
mod dc_loginparam;
|
||||
mod dc_mimefactory;
|
||||
pub mod dc_job;
|
||||
pub mod dc_jobthread;
|
||||
pub mod dc_location;
|
||||
pub mod dc_loginparam;
|
||||
pub mod dc_lot;
|
||||
pub mod dc_mimefactory;
|
||||
pub mod dc_mimeparser;
|
||||
pub mod dc_move;
|
||||
pub mod dc_msg;
|
||||
pub mod dc_qr;
|
||||
pub mod dc_receive_imf;
|
||||
pub mod dc_saxparser;
|
||||
pub mod dc_securejoin;
|
||||
mod dc_simplify;
|
||||
mod dc_strencode;
|
||||
mod dc_token;
|
||||
pub mod dc_simplify;
|
||||
pub mod dc_strencode;
|
||||
pub mod dc_token;
|
||||
pub mod dc_tools;
|
||||
|
||||
pub use self::constants::*;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
pub mod test_utils;
|
||||
|
||||
696
src/location.rs
696
src/location.rs
@@ -1,696 +0,0 @@
|
||||
use bitflags::bitflags;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::chat;
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::job::*;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
|
||||
// location handling
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Location {
|
||||
pub location_id: u32,
|
||||
pub latitude: f64,
|
||||
pub longitude: f64,
|
||||
pub accuracy: f64,
|
||||
pub timestamp: i64,
|
||||
pub contact_id: u32,
|
||||
pub msg_id: u32,
|
||||
pub chat_id: u32,
|
||||
pub marker: Option<String>,
|
||||
pub independent: u32,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Kml {
|
||||
pub addr: Option<String>,
|
||||
pub locations: Vec<Location>,
|
||||
tag: KmlTag,
|
||||
pub curr: Location,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default)]
|
||||
struct KmlTag: i32 {
|
||||
const UNDEFINED = 0x00;
|
||||
const PLACEMARK = 0x01;
|
||||
const TIMESTAMP = 0x02;
|
||||
const WHEN = 0x04;
|
||||
const POINT = 0x08;
|
||||
const COORDINATES = 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
impl Kml {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn parse(context: &Context, content: impl AsRef<str>) -> Result<Self, Error> {
|
||||
ensure!(
|
||||
content.as_ref().len() <= (1 * 1024 * 1024),
|
||||
"A kml-files with {} bytes is larger than reasonably expected.",
|
||||
content.as_ref().len()
|
||||
);
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(content.as_ref());
|
||||
reader.trim_text(true);
|
||||
|
||||
let mut kml = Kml::new();
|
||||
kml.locations = Vec::with_capacity(100);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => kml.starttag_cb(e, &reader),
|
||||
Ok(quick_xml::events::Event::End(ref e)) => kml.endtag_cb(e),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => kml.text_cb(e, &reader),
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Location parsing: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
Ok(kml)
|
||||
}
|
||||
|
||||
fn text_cb<B: std::io::BufRead>(&mut self, event: &BytesText, reader: &quick_xml::Reader<B>) {
|
||||
if self.tag.contains(KmlTag::WHEN) || self.tag.contains(KmlTag::COORDINATES) {
|
||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
||||
|
||||
let val = val
|
||||
.replace("\n", "")
|
||||
.replace("\r", "")
|
||||
.replace("\t", "")
|
||||
.replace(" ", "");
|
||||
|
||||
if self.tag.contains(KmlTag::WHEN) && val.len() >= 19 {
|
||||
// YYYY-MM-DDTHH:MM:SSZ
|
||||
// 0 4 7 10 13 16 19
|
||||
match chrono::NaiveDateTime::parse_from_str(&val, "%Y-%m-%dT%H:%M:%SZ") {
|
||||
Ok(res) => {
|
||||
self.curr.timestamp = res.timestamp();
|
||||
if self.curr.timestamp > time() {
|
||||
self.curr.timestamp = time();
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
self.curr.timestamp = time();
|
||||
}
|
||||
}
|
||||
} else if self.tag.contains(KmlTag::COORDINATES) {
|
||||
let parts = val.splitn(2, ',').collect::<Vec<_>>();
|
||||
if parts.len() == 2 {
|
||||
self.curr.longitude = parts[0].parse().unwrap_or_default();
|
||||
self.curr.latitude = parts[1].parse().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn endtag_cb(&mut self, event: &BytesEnd) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "placemark" {
|
||||
if self.tag.contains(KmlTag::PLACEMARK)
|
||||
&& 0 != self.curr.timestamp
|
||||
&& 0. != self.curr.latitude
|
||||
&& 0. != self.curr.longitude
|
||||
{
|
||||
self.locations
|
||||
.push(std::mem::replace(&mut self.curr, Location::new()));
|
||||
}
|
||||
self.tag = KmlTag::UNDEFINED;
|
||||
};
|
||||
}
|
||||
|
||||
fn starttag_cb<B: std::io::BufRead>(
|
||||
&mut self,
|
||||
event: &BytesStart,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
if tag == "document" {
|
||||
if let Some(addr) = event.attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "addr")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
self.addr = addr.unwrap().unescape_and_decode_value(reader).ok();
|
||||
}
|
||||
} else if tag == "placemark" {
|
||||
self.tag = KmlTag::PLACEMARK;
|
||||
self.curr.timestamp = 0;
|
||||
self.curr.latitude = 0.0;
|
||||
self.curr.longitude = 0.0;
|
||||
self.curr.accuracy = 0.0
|
||||
} else if tag == "timestamp" && self.tag.contains(KmlTag::PLACEMARK) {
|
||||
self.tag = KmlTag::PLACEMARK | KmlTag::TIMESTAMP
|
||||
} else if tag == "when" && self.tag.contains(KmlTag::TIMESTAMP) {
|
||||
self.tag = KmlTag::PLACEMARK | KmlTag::TIMESTAMP | KmlTag::WHEN
|
||||
} else if tag == "point" && self.tag.contains(KmlTag::PLACEMARK) {
|
||||
self.tag = KmlTag::PLACEMARK | KmlTag::POINT
|
||||
} else if tag == "coordinates" && self.tag.contains(KmlTag::POINT) {
|
||||
self.tag = KmlTag::PLACEMARK | KmlTag::POINT | KmlTag::COORDINATES;
|
||||
if let Some(acc) = event.attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "accuracy")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
let v = acc
|
||||
.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default();
|
||||
|
||||
self.curr.accuracy = v.trim().parse().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// location streaming
|
||||
pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
let now = time();
|
||||
let mut msg: Message;
|
||||
let is_sending_locations_before: bool;
|
||||
if !(seconds < 0 || chat_id <= 9i32 as libc::c_uint) {
|
||||
is_sending_locations_before = is_sending_locations_to_chat(context, chat_id);
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats \
|
||||
SET locations_send_begin=?, \
|
||||
locations_send_until=? \
|
||||
WHERE id=?",
|
||||
params![
|
||||
if 0 != seconds { now } else { 0 },
|
||||
if 0 != seconds { now + seconds } else { 0 },
|
||||
chat_id as i32,
|
||||
],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
if 0 != seconds && !is_sending_locations_before {
|
||||
msg = dc_msg_new(context, Viewtype::Text);
|
||||
msg.text =
|
||||
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
|
||||
msg.param.set_int(Param::Cmd, 8);
|
||||
unsafe { chat::send_msg(context, chat_id, &mut msg).unwrap() };
|
||||
} else if 0 == seconds && is_sending_locations_before {
|
||||
let stock_str =
|
||||
context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
chat::add_device_msg(context, chat_id, stock_str);
|
||||
}
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
if 0 != seconds {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0i32);
|
||||
job_add(
|
||||
context,
|
||||
Action::MaybeSendLocationsEnded,
|
||||
chat_id as libc::c_int,
|
||||
Params::new(),
|
||||
seconds + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: i32) {
|
||||
if 0 != flags & 0x1 || !job_action_exists(context, Action::MaybeSendLocations) {
|
||||
job_add(context, Action::MaybeSendLocations, 0, Params::new(), 60);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn is_sending_locations_to_chat(context: &Context, chat_id: u32) -> bool {
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM chats WHERE (? OR id=?) AND locations_send_until>?;",
|
||||
params![if chat_id == 0 { 1 } else { 0 }, chat_id as i32, time()],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> libc::c_int {
|
||||
if latitude == 0.0 && longitude == 0.0 {
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.sql.query_map(
|
||||
"SELECT id FROM chats WHERE locations_send_until>?;",
|
||||
params![time()], |row| row.get::<_, i32>(0),
|
||||
|chats| {
|
||||
let mut continue_streaming = false;
|
||||
|
||||
for chat in chats {
|
||||
let chat_id = chat?;
|
||||
context.sql.execute(
|
||||
"INSERT INTO locations \
|
||||
(latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);",
|
||||
params![
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy,
|
||||
time(),
|
||||
chat_id,
|
||||
1,
|
||||
]
|
||||
)?;
|
||||
continue_streaming = true;
|
||||
}
|
||||
if continue_streaming {
|
||||
context.call_cb(Event::LOCATION_CHANGED, 1, 0);
|
||||
};
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0);
|
||||
Ok(continue_streaming as libc::c_int)
|
||||
}
|
||||
).unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn get_range(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
contact_id: u32,
|
||||
timestamp_from: i64,
|
||||
mut timestamp_to: i64,
|
||||
) -> Vec<Location> {
|
||||
if timestamp_to == 0 {
|
||||
timestamp_to = time() + 10;
|
||||
}
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT l.id, l.latitude, l.longitude, l.accuracy, l.timestamp, l.independent, \
|
||||
m.id, l.from_id, l.chat_id, m.txt \
|
||||
FROM locations l LEFT JOIN msgs m ON l.id=m.location_id WHERE (? OR l.chat_id=?) \
|
||||
AND (? OR l.from_id=?) \
|
||||
AND (l.independent=1 OR (l.timestamp>=? AND l.timestamp<=?)) \
|
||||
ORDER BY l.timestamp DESC, l.id DESC, m.id DESC;",
|
||||
params![
|
||||
if chat_id == 0 { 1 } else { 0 },
|
||||
chat_id as i32,
|
||||
if contact_id == 0 { 1 } else { 0 },
|
||||
contact_id as i32,
|
||||
timestamp_from,
|
||||
timestamp_to,
|
||||
],
|
||||
|row| {
|
||||
let msg_id = row.get(6)?;
|
||||
let txt: String = row.get(9)?;
|
||||
let marker = if msg_id != 0 && is_marker(&txt) {
|
||||
Some(txt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let loc = Location {
|
||||
location_id: row.get(0)?,
|
||||
latitude: row.get(1)?,
|
||||
longitude: row.get(2)?,
|
||||
accuracy: row.get(3)?,
|
||||
timestamp: row.get(4)?,
|
||||
independent: row.get(5)?,
|
||||
msg_id,
|
||||
contact_id: row.get(7)?,
|
||||
chat_id: row.get(8)?,
|
||||
marker,
|
||||
};
|
||||
Ok(loc)
|
||||
},
|
||||
|locations| {
|
||||
let mut ret = Vec::new();
|
||||
|
||||
for location in locations {
|
||||
ret.push(location?);
|
||||
}
|
||||
Ok(ret)
|
||||
},
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn is_marker(txt: &str) -> bool {
|
||||
txt.len() == 1 && txt.chars().next().unwrap() != ' '
|
||||
}
|
||||
|
||||
pub fn delete_all(context: &Context) -> Result<(), Error> {
|
||||
sql::execute(context, &context.sql, "DELETE FROM locations;", params![])?;
|
||||
context.call_cb(Event::LOCATION_CHANGED, 0, 0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_kml(context: &Context, chat_id: u32) -> Result<(String, u32), Error> {
|
||||
let now = time();
|
||||
let mut location_count = 0;
|
||||
let mut ret = String::new();
|
||||
let mut last_added_location_id = 0;
|
||||
|
||||
let self_addr = context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
|
||||
let (locations_send_begin, locations_send_until, locations_last_sent) = context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until, locations_last_sent FROM chats WHERE id=?;",
|
||||
params![chat_id as i32], |row| {
|
||||
let send_begin: i64 = row.get(0)?;
|
||||
let send_until: i64 = row.get(1)?;
|
||||
let last_sent: i64 = row.get(2)?;
|
||||
|
||||
Ok((send_begin, send_until, last_sent))
|
||||
})?;
|
||||
|
||||
if !(locations_send_begin == 0 || now > locations_send_until) {
|
||||
ret += &format!(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"{}\">\n",
|
||||
self_addr,
|
||||
);
|
||||
|
||||
context.sql.query_map(
|
||||
"SELECT id, latitude, longitude, accuracy, timestamp\
|
||||
FROM locations WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND (timestamp>=? OR timestamp=(SELECT MAX(timestamp) FROM locations WHERE from_id=?)) \
|
||||
AND independent=0 \
|
||||
GROUP BY timestamp \
|
||||
ORDER BY timestamp;",
|
||||
params![1, locations_send_begin, locations_last_sent, 1],
|
||||
|row| {
|
||||
let location_id: i32 = row.get(0)?;
|
||||
let latitude: f64 = row.get(1)?;
|
||||
let longitude: f64 = row.get(2)?;
|
||||
let accuracy: f64 = row.get(3)?;
|
||||
let timestamp = get_kml_timestamp(row.get(4)?);
|
||||
|
||||
Ok((location_id, latitude, longitude, accuracy, timestamp))
|
||||
},
|
||||
|rows| {
|
||||
for row in rows {
|
||||
let (location_id, latitude, longitude, accuracy, timestamp) = row?;
|
||||
ret += &format!(
|
||||
"<Placemark><Timestamp><when>{}</when></Timestamp><Point><coordinates accuracy=\"{}\">{},{}</coordinates></Point></Placemark>\n\x00",
|
||||
timestamp,
|
||||
accuracy,
|
||||
longitude,
|
||||
latitude
|
||||
);
|
||||
location_count += 1;
|
||||
last_added_location_id = location_id as u32;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
)?;
|
||||
}
|
||||
|
||||
ensure!(location_count > 0, "No locations processed");
|
||||
ret += "</Document>\n</kml>";
|
||||
|
||||
Ok((ret, last_added_location_id))
|
||||
}
|
||||
|
||||
fn get_kml_timestamp(utc: i64) -> String {
|
||||
// Returns a string formatted as YYYY-MM-DDTHH:MM:SSZ. The trailing `Z` indicates UTC.
|
||||
chrono::NaiveDateTime::from_timestamp(utc, 0)
|
||||
.format("%Y-%m-%dT%H:%M:%SZ")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub fn get_message_kml(timestamp: i64, latitude: f64, longitude: f64) -> String {
|
||||
format!(
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
|
||||
<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n\
|
||||
<Document>\n\
|
||||
<Placemark>\
|
||||
<Timestamp><when>{}</when></Timestamp>\
|
||||
<Point><coordinates>{:.2},{:.2}</coordinates></Point>\
|
||||
</Placemark>\n\
|
||||
</Document>\n\
|
||||
</kml>",
|
||||
get_kml_timestamp(timestamp),
|
||||
longitude,
|
||||
latitude,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn set_kml_sent_timestamp(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
timestamp: i64,
|
||||
) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET locations_last_sent=? WHERE id=?;",
|
||||
params![timestamp, chat_id as i32],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_msg_location_id(context: &Context, msg_id: u32, location_id: u32) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET location_id=? WHERE id=?;",
|
||||
params![location_id, msg_id as i32],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
contact_id: u32,
|
||||
locations: &[Location],
|
||||
independent: i32,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(chat_id > 9, "Invalid chat id");
|
||||
context.sql.prepare2(
|
||||
"SELECT id FROM locations WHERE timestamp=? AND from_id=?",
|
||||
"INSERT INTO locations\
|
||||
(timestamp, from_id, chat_id, latitude, longitude, accuracy, independent) \
|
||||
VALUES (?,?,?,?,?,?,?);",
|
||||
|mut stmt_test, mut stmt_insert, conn| {
|
||||
let mut newest_timestamp = 0;
|
||||
let mut newest_location_id = 0;
|
||||
|
||||
for location in locations {
|
||||
let exists = stmt_test.exists(params![location.timestamp, contact_id as i32])?;
|
||||
|
||||
if 0 != independent || !exists {
|
||||
stmt_insert.execute(params![
|
||||
location.timestamp,
|
||||
contact_id as i32,
|
||||
chat_id as i32,
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
location.accuracy,
|
||||
independent,
|
||||
])?;
|
||||
|
||||
if location.timestamp > newest_timestamp {
|
||||
newest_timestamp = location.timestamp;
|
||||
newest_location_id = sql::get_rowid2_with_conn(
|
||||
context,
|
||||
conn,
|
||||
"locations",
|
||||
"timestamp",
|
||||
location.timestamp,
|
||||
"from_id",
|
||||
contact_id as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(newest_location_id)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
|
||||
let now = time();
|
||||
let mut continue_streaming: libc::c_int = 1;
|
||||
info!(
|
||||
context,
|
||||
0, " ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
||||
);
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id, locations_send_begin, locations_last_sent \
|
||||
FROM chats \
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = 1;
|
||||
|
||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
||||
if now - locations_last_sent < (60 - 3) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
||||
}
|
||||
},
|
||||
|rows| {
|
||||
context.sql.prepare(
|
||||
"SELECT id \
|
||||
FROM locations \
|
||||
WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND timestamp>? \
|
||||
AND independent=0 \
|
||||
ORDER BY timestamp;",
|
||||
|mut stmt_locations, _| {
|
||||
for (chat_id, locations_send_begin, locations_last_sent) in
|
||||
rows.filter_map(|r| match r {
|
||||
Ok(Some(v)) => Some(v),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
// TODO: do I need to reset?
|
||||
if !stmt_locations
|
||||
.exists(params![1, locations_send_begin, locations_last_sent,])
|
||||
.unwrap_or_default()
|
||||
{
|
||||
// if there is no new location, there's nothing to send.
|
||||
// however, maybe we want to bypass this test eg. 15 minutes
|
||||
continue;
|
||||
}
|
||||
// pending locations are attached automatically to every message,
|
||||
// so also to this empty text message.
|
||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
||||
//
|
||||
// for optimisation and to avoid flooding the sending queue,
|
||||
// we could sending these messages only if we're really online.
|
||||
// the easiest way to determine this, is to check for an empty message queue.
|
||||
// (might not be 100%, however, as positions are sent combined later
|
||||
// and dc_set_location() is typically called periodically, this is ok)
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::Cmd, 9);
|
||||
// TODO: handle cleanup on error
|
||||
unsafe { chat::send_msg(context, chat_id as u32, &mut msg).unwrap() };
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
.unwrap(); // TODO: Better error handling
|
||||
|
||||
if 0 != continue_streaming {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0x1);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) {
|
||||
// this function is called when location-streaming _might_ have ended for a chat.
|
||||
// the function checks, if location-streaming is really ended;
|
||||
// if so, a device-message is added if not yet done.
|
||||
|
||||
let chat_id = job.foreign_id;
|
||||
|
||||
if let Ok((send_begin, send_until)) = context.sql.query_row(
|
||||
"SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)),
|
||||
) {
|
||||
if !(send_begin != 0 && time() <= send_until) {
|
||||
// still streaming -
|
||||
// may happen as several calls to dc_send_locations_to_chat()
|
||||
// do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs
|
||||
if !(send_begin == 0 && send_until == 0) {
|
||||
// not streaming, device-message already sent
|
||||
if context.sql.execute(
|
||||
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
).is_ok() {
|
||||
let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
chat::add_device_msg(context, chat_id, stock_str);
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as usize,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::dummy_context;
|
||||
|
||||
#[test]
|
||||
fn test_kml_parse() {
|
||||
let context = dummy_context();
|
||||
|
||||
let xml =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</WHEN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></PlaceMARK>\n</Document>\n</kml>";
|
||||
|
||||
let kml = Kml::parse(&context.ctx, &xml).expect("parsing failed");
|
||||
|
||||
assert!(kml.addr.is_some());
|
||||
assert_eq!(kml.addr.as_ref().unwrap(), "user@example.org",);
|
||||
|
||||
let locations_ref = &kml.locations;
|
||||
assert_eq!(locations_ref.len(), 2);
|
||||
|
||||
assert!(locations_ref[0].latitude > 53.6f64);
|
||||
assert!(locations_ref[0].latitude < 53.8f64);
|
||||
assert!(locations_ref[0].longitude > 9.3f64);
|
||||
assert!(locations_ref[0].longitude < 9.5f64);
|
||||
assert!(locations_ref[0].accuracy > 31.9f64);
|
||||
assert!(locations_ref[0].accuracy < 32.1f64);
|
||||
assert_eq!(locations_ref[0].timestamp, 1551906597);
|
||||
|
||||
assert!(locations_ref[1].latitude > 63.6f64);
|
||||
assert!(locations_ref[1].latitude < 63.8f64);
|
||||
assert!(locations_ref[1].longitude > 19.3f64);
|
||||
assert!(locations_ref[1].longitude < 19.5f64);
|
||||
assert!(locations_ref[1].accuracy > 2.4f64);
|
||||
assert!(locations_ref[1].accuracy < 2.6f64);
|
||||
assert_eq!(locations_ref[1].timestamp, 1544739072);
|
||||
}
|
||||
}
|
||||
109
src/lot.rs
109
src/lot.rs
@@ -1,109 +0,0 @@
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
|
||||
/// 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<String>,
|
||||
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_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
pub fn get_text2(&self) -> Option<&str> {
|
||||
self.text2.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
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(i32)]
|
||||
#[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
|
||||
QrFprMissmatch = 220,
|
||||
/// test1=formatted fingerprint
|
||||
QrFprWithoutAddr = 230,
|
||||
/// id=contact
|
||||
QrAddr = 320,
|
||||
/// text1=text
|
||||
QrText = 330,
|
||||
/// text1=URL
|
||||
QrUrl = 332,
|
||||
/// text1=error string
|
||||
QrError = 400,
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
1330
src/message.rs
1330
src/message.rs
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ const OAUTH2_YANDEX: Oauth2 = Oauth2 {
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct Oauth2 {
|
||||
pub struct Oauth2 {
|
||||
client_id: &'static str,
|
||||
get_code: &'static str,
|
||||
init_token: &'static str,
|
||||
|
||||
10
src/param.rs
10
src/param.rs
@@ -113,7 +113,7 @@ impl str::FromStr for Params {
|
||||
continue;
|
||||
}
|
||||
// TODO: probably nicer using a regex
|
||||
ensure!(pair.len() > 1, "Invalid key pair: '{}'", pair);
|
||||
ensure!(pair.len() > 2, "Invalid key pair: '{}'", pair);
|
||||
let mut split = pair.splitn(2, '=');
|
||||
let key = split.next();
|
||||
let value = split.next();
|
||||
@@ -235,12 +235,4 @@ mod tests {
|
||||
assert!(p1.is_empty());
|
||||
assert_eq!(p1.len(), 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regression() {
|
||||
let p1: Params = "a=cli%40deltachat.de\nn=\ni=TbnwJ6lSvD5\ns=0ejvbdFSQxB"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(p1.get(Param::Forwarded).unwrap(), "cli%40deltachat.de");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::fmt;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::key::*;
|
||||
use crate::sql::{self, Sql};
|
||||
|
||||
@@ -85,7 +85,7 @@ pub enum DegradeEvent {
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum VerifiedKey {
|
||||
pub enum VerifiedKey {
|
||||
Gossip,
|
||||
Public,
|
||||
None,
|
||||
@@ -466,7 +466,7 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
|
||||
if self.to_save == Some(ToSave::All) || create {
|
||||
reset_gossiped_timestamp(self.context, 0);
|
||||
dc_reset_gossiped_timestamp(self.context, 0);
|
||||
}
|
||||
|
||||
success
|
||||
|
||||
15
src/pgp.rs
15
src/pgp.rs
@@ -2,7 +2,6 @@ use std::collections::HashSet;
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Cursor;
|
||||
use std::ptr;
|
||||
|
||||
use pgp::composed::{
|
||||
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
|
||||
@@ -30,19 +29,19 @@ pub unsafe fn dc_split_armored_data(
|
||||
let mut line: *mut libc::c_char = buf;
|
||||
let mut p1: *mut libc::c_char = buf;
|
||||
let mut p2: *mut libc::c_char;
|
||||
let mut headerline: *mut libc::c_char = ptr::null_mut();
|
||||
let mut base64: *mut libc::c_char = ptr::null_mut();
|
||||
let mut headerline: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut base64: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if !ret_headerline.is_null() {
|
||||
*ret_headerline = ptr::null()
|
||||
*ret_headerline = 0 as *const libc::c_char
|
||||
}
|
||||
if !ret_setupcodebegin.is_null() {
|
||||
*ret_setupcodebegin = ptr::null_mut();
|
||||
*ret_setupcodebegin = 0 as *const libc::c_char
|
||||
}
|
||||
if !ret_preferencrypt.is_null() {
|
||||
*ret_preferencrypt = ptr::null();
|
||||
*ret_preferencrypt = 0 as *const libc::c_char
|
||||
}
|
||||
if !ret_base64.is_null() {
|
||||
*ret_base64 = ptr::null();
|
||||
*ret_base64 = 0 as *const libc::c_char
|
||||
}
|
||||
if !(buf.is_null() || ret_headerline.is_null()) {
|
||||
dc_remove_cr_chars(buf);
|
||||
@@ -144,7 +143,7 @@ pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
|
||||
.key_type(PgpKeyType::Rsa(2048))
|
||||
.can_create_certificates(true)
|
||||
.can_sign(true)
|
||||
.primary_user_id(user_id)
|
||||
.primary_user_id(user_id.into())
|
||||
.passphrase(None)
|
||||
.preferred_symmetric_algorithms(smallvec![
|
||||
SymmetricKeyAlgorithm::AES256,
|
||||
|
||||
469
src/qr.rs
469
src/qr.rs
@@ -1,469 +0,0 @@
|
||||
use lazy_static::lazy_static;
|
||||
use percent_encoding::percent_decode_str;
|
||||
|
||||
use crate::chat;
|
||||
use crate::constants::Blocked;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::key::dc_format_fingerprint;
|
||||
use crate::key::*;
|
||||
use crate::lot::{Lot, LotState};
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
|
||||
const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase
|
||||
const MAILTO_SCHEME: &str = "mailto:";
|
||||
const MATMSG_SCHEME: &str = "MATMSG:";
|
||||
const VCARD_SCHEME: &str = "BEGIN:VCARD";
|
||||
const SMTP_SCHEME: &str = "SMTP:";
|
||||
const HTTP_SCHEME: &str = "http://";
|
||||
const HTTPS_SCHEME: &str = "https://";
|
||||
|
||||
// Make it easy to convert errors into the final `Lot`.
|
||||
impl Into<Lot> for Error {
|
||||
fn into(self) -> Lot {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrError;
|
||||
l.text1 = Some(self.to_string());
|
||||
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
/// Check a scanned QR code.
|
||||
/// The function should be called after a QR code is scanned.
|
||||
/// The function takes the raw text scanned and checks what can be done with it.
|
||||
pub fn check_qr(context: &Context, qr: impl AsRef<str>) -> Lot {
|
||||
let qr = qr.as_ref();
|
||||
|
||||
info!(context, 0, "Scanned QR code: {}", qr);
|
||||
|
||||
if qr.starts_with(OPENPGP4FPR_SCHEME) {
|
||||
decode_openpgp(context, qr)
|
||||
} else if qr.starts_with(MAILTO_SCHEME) {
|
||||
decode_mailto(context, qr)
|
||||
} else if qr.starts_with(SMTP_SCHEME) {
|
||||
decode_smtp(context, qr)
|
||||
} else if qr.starts_with(MATMSG_SCHEME) {
|
||||
decode_matmsg(context, qr)
|
||||
} else if qr.starts_with(VCARD_SCHEME) {
|
||||
decode_vcard(context, qr)
|
||||
} else if qr.starts_with(HTTP_SCHEME) || qr.starts_with(HTTPS_SCHEME) {
|
||||
Lot::from_url(qr)
|
||||
} else {
|
||||
Lot::from_text(qr)
|
||||
}
|
||||
}
|
||||
|
||||
/// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH`
|
||||
/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH`
|
||||
fn decode_openpgp(context: &Context, qr: &str) -> Lot {
|
||||
let payload = &qr[OPENPGP4FPR_SCHEME.len()..];
|
||||
|
||||
let (fingerprint, fragment) = match payload.find('#').map(|offset| {
|
||||
let (fp, rest) = payload.split_at(offset);
|
||||
// need to remove the # from the fragment
|
||||
(fp, &rest[1..])
|
||||
}) {
|
||||
Some(pair) => pair,
|
||||
None => return format_err!("Invalid OPENPGP4FPR found").into(),
|
||||
};
|
||||
|
||||
dbg!(fingerprint);
|
||||
dbg!(fragment);
|
||||
|
||||
// replace & with \n to match expected param format
|
||||
let fragment = fragment.replace('&', "\n");
|
||||
dbg!(&fragment);
|
||||
|
||||
// Then parse the parameters
|
||||
let param: Params = match fragment.parse() {
|
||||
Ok(params) => params,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
dbg!(¶m);
|
||||
|
||||
let addr = if let Some(addr) = param.get(Param::Forwarded) {
|
||||
match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
}
|
||||
} else {
|
||||
return format_err!("Missing address").into();
|
||||
};
|
||||
|
||||
// what is up with that param name?
|
||||
let name = if let Some(encoded_name) = param.get(Param::SetLongitude) {
|
||||
match percent_decode_str(encoded_name).decode_utf8() {
|
||||
Ok(name) => name.to_string(),
|
||||
Err(err) => return format_err!("Invalid name: {}", err).into(),
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let invitenumber = param.get(Param::ProfileImage).map(|s| s.to_string());
|
||||
let auth = param.get(Param::Auth).map(|s| s.to_string());
|
||||
let grpid = param.get(Param::GroupId).map(|s| s.to_string());
|
||||
|
||||
let grpname = if grpid.is_some() {
|
||||
if let Some(encoded_name) = param.get(Param::GroupName) {
|
||||
match percent_decode_str(encoded_name).decode_utf8() {
|
||||
Ok(name) => Some(name.to_string()),
|
||||
Err(err) => return format_err!("Invalid group name: {}", err).into(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let fingerprint = dc_normalize_fingerprint(fingerprint);
|
||||
|
||||
// ensure valid fingerprint
|
||||
if fingerprint.len() != 40 {
|
||||
return format_err!("Bad fingerprint length in QR code").into();
|
||||
}
|
||||
|
||||
println!(
|
||||
"{:?} {:?} {:?} {:?} {:?} {:?} {:?}",
|
||||
addr, name, invitenumber, auth, grpid, grpname, fingerprint
|
||||
);
|
||||
|
||||
let mut lot = Lot::new();
|
||||
|
||||
// retrieve known state for this fingerprint
|
||||
let peerstate = Peerstate::from_fingerprint(context, &context.sql, &fingerprint);
|
||||
|
||||
if invitenumber.is_none() || auth.is_none() {
|
||||
if let Some(peerstate) = peerstate {
|
||||
lot.state = LotState::QrFprOk;
|
||||
let addr = peerstate
|
||||
.addr
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
|
||||
lot.id = Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
let (id, _) = chat::create_or_lookup_by_contact_id(context, lot.id, Blocked::Deaddrop)
|
||||
.unwrap_or_default();
|
||||
|
||||
chat::add_device_msg(
|
||||
context,
|
||||
id,
|
||||
format!("{} verified.", peerstate.addr.unwrap_or_default()),
|
||||
);
|
||||
} else {
|
||||
lot.state = LotState::QrFprWithoutAddr;
|
||||
lot.text1 = Some(dc_format_fingerprint(&fingerprint));
|
||||
}
|
||||
} else {
|
||||
if grpid.is_some() && grpname.is_some() {
|
||||
lot.state = LotState::QrAskVerifyGroup;
|
||||
lot.text1 = grpname;
|
||||
lot.text2 = grpid
|
||||
} else {
|
||||
lot.state = LotState::QrAskVerifyContact;
|
||||
}
|
||||
lot.id = Contact::add_or_lookup(context, &name, &addr, Origin::UnhandledQrScan)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
lot.fingerprint = Some(fingerprint);
|
||||
lot.invitenumber = invitenumber;
|
||||
lot.auth = auth;
|
||||
}
|
||||
|
||||
lot
|
||||
}
|
||||
|
||||
/// Extract address for the mailto scheme.
|
||||
///
|
||||
/// Scheme: `mailto:addr...?subject=...&body=..`
|
||||
fn decode_mailto(context: &Context, qr: &str) -> Lot {
|
||||
let payload = &qr[MAILTO_SCHEME.len()..];
|
||||
|
||||
let addr = if let Some(query_index) = payload.find('?') {
|
||||
&payload[..query_index]
|
||||
} else {
|
||||
return format_err!("Invalid mailto found").into();
|
||||
};
|
||||
|
||||
let addr = match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
let name = "".to_string();
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
/// Extract address for the smtp scheme.
|
||||
///
|
||||
/// Scheme: `SMTP:addr...:subject...:body...`
|
||||
fn decode_smtp(context: &Context, qr: &str) -> Lot {
|
||||
let payload = &qr[SMTP_SCHEME.len()..];
|
||||
|
||||
let addr = if let Some(query_index) = payload.find(':') {
|
||||
&payload[..query_index]
|
||||
} else {
|
||||
return format_err!("Invalid SMTP found").into();
|
||||
};
|
||||
|
||||
let addr = match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
let name = "".to_string();
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
/// Extract address for the matmsg scheme.
|
||||
///
|
||||
/// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;`
|
||||
///
|
||||
/// There may or may not be linebreaks after the fields.
|
||||
fn decode_matmsg(context: &Context, qr: &str) -> Lot {
|
||||
// Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field.
|
||||
// we ignore this case.
|
||||
let addr = if let Some(to_index) = qr.find("TO:") {
|
||||
let addr = qr[to_index + 3..].trim();
|
||||
if let Some(semi_index) = addr.find(';') {
|
||||
addr[..semi_index].trim()
|
||||
} else {
|
||||
addr
|
||||
}
|
||||
} else {
|
||||
return format_err!("Invalid MATMSG found").into();
|
||||
};
|
||||
|
||||
let addr = match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
let name = "".to_string();
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref VCARD_NAME_RE: regex::Regex =
|
||||
regex::Regex::new(r"(?m)^N:([^;]*);([^;\n]*)").unwrap();
|
||||
static ref VCARD_EMAIL_RE: regex::Regex =
|
||||
regex::Regex::new(r"(?m)^EMAIL([^:\n]*):([^;\n]*)").unwrap();
|
||||
}
|
||||
|
||||
/// Extract address for the matmsg scheme.
|
||||
///
|
||||
/// Scheme: `VCARD:BEGIN\nN:last name;first name;...;\nEMAIL;<type>:addr...;
|
||||
fn decode_vcard(context: &Context, qr: &str) -> Lot {
|
||||
let name = VCARD_NAME_RE
|
||||
.captures(qr)
|
||||
.map(|caps| {
|
||||
let last_name = &caps[1];
|
||||
let first_name = &caps[2];
|
||||
|
||||
format!("{} {}", first_name.trim(), last_name.trim())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let addr = if let Some(caps) = VCARD_EMAIL_RE.captures(qr) {
|
||||
match normalize_address(caps[2].trim()) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
}
|
||||
} else {
|
||||
return format_err!("Bad e-mail address").into();
|
||||
};
|
||||
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
impl Lot {
|
||||
pub fn from_text(text: impl AsRef<str>) -> Self {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrText;
|
||||
l.text1 = Some(text.as_ref().to_string());
|
||||
|
||||
l
|
||||
}
|
||||
|
||||
pub fn from_url(url: impl AsRef<str>) -> Self {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrUrl;
|
||||
l.text1 = Some(url.as_ref().to_string());
|
||||
|
||||
l
|
||||
}
|
||||
|
||||
pub fn from_address(context: &Context, name: String, addr: String) -> Self {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrAddr;
|
||||
l.id = match Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan) {
|
||||
Ok((id, _)) => id,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
/// URL decodes a given address, does basic email validation on the result.
|
||||
fn normalize_address(addr: &str) -> Result<String, Error> {
|
||||
// urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases
|
||||
let new_addr = percent_decode_str(addr).decode_utf8()?;
|
||||
let new_addr = addr_normalize(&new_addr);
|
||||
|
||||
ensure!(may_be_valid_addr(&new_addr), "Bad e-mail address");
|
||||
|
||||
Ok(new_addr.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::test_utils::dummy_context;
|
||||
|
||||
#[test]
|
||||
fn test_decode_http() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "http://www.hello.com");
|
||||
|
||||
assert_eq!(res.get_state(), LotState::QrUrl);
|
||||
assert_eq!(res.get_id(), 0);
|
||||
assert_eq!(res.get_text1().unwrap(), "http://www.hello.com");
|
||||
assert!(res.get_text2().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_https() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "https://www.hello.com");
|
||||
|
||||
assert_eq!(res.get_state(), LotState::QrUrl);
|
||||
assert_eq!(res.get_id(), 0);
|
||||
assert_eq!(res.get_text1().unwrap(), "https://www.hello.com");
|
||||
assert!(res.get_text2().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_text() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "I am so cool");
|
||||
|
||||
assert_eq!(res.get_state(), LotState::QrText);
|
||||
assert_eq!(res.get_id(), 0);
|
||||
assert_eq!(res.get_text1().unwrap(), "I am so cool");
|
||||
assert!(res.get_text2().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_vcard() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD"
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
assert_eq!(contact.get_name(), "First Last");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_matmsg() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"MATMSG:TO:\n\nstress@test.local ; \n\nSUB:\n\nSubject here\n\nBODY:\n\nhelloworld\n;;",
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_mailto() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"mailto:stress@test.local?subject=hello&body=world",
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_smtp() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld");
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_openpgp_group() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&g=testtesttest&x=h-0oKQf2CDK&i=9JEXlxAqGM0&s=0V7LzL9cxRL"
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAskVerifyGroup);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "cli@deltachat.de");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_openpgp_secure_join() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&n=&i=TbnwJ6lSvD5&s=0ejvbdFSQxB"
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAskVerifyContact);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "cli@deltachat.de");
|
||||
}
|
||||
}
|
||||
12
src/sql.rs
12
src/sql.rs
@@ -286,7 +286,7 @@ impl Sql {
|
||||
|
||||
fn table_exists(conn: &Connection, name: impl AsRef<str>) -> Result<bool> {
|
||||
let mut exists = false;
|
||||
conn.pragma(None, "table_info", &name.as_ref().to_string(), |_row| {
|
||||
conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| {
|
||||
// will only be executed if the info was found
|
||||
exists = true;
|
||||
Ok(())
|
||||
@@ -830,7 +830,7 @@ where
|
||||
&err,
|
||||
querystr.as_ref()
|
||||
);
|
||||
Err(err)
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1003,10 +1003,10 @@ pub fn housekeeping(context: &Context) {
|
||||
let name_f = entry.file_name();
|
||||
let name_s = name_f.to_string_lossy();
|
||||
|
||||
if is_file_in_use(&files_in_use, None, &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s)
|
||||
if is_file_in_use(&mut files_in_use, None, &name_s)
|
||||
|| is_file_in_use(&mut files_in_use, Some(".increation"), &name_s)
|
||||
|| is_file_in_use(&mut files_in_use, Some(".waveform"), &name_s)
|
||||
|| is_file_in_use(&mut files_in_use, Some("-preview.jpg"), &name_s)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -323,7 +323,7 @@ mod tests {
|
||||
StockMessage::MsgAddMember,
|
||||
"alice@example.com",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF
|
||||
DC_CONTACT_ID_SELF as u32
|
||||
),
|
||||
"Member alice@example.com added by me."
|
||||
)
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
StockMessage::MsgAddMember,
|
||||
"alice@example.com",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF
|
||||
DC_CONTACT_ID_SELF as u32
|
||||
),
|
||||
"Member Alice (alice@example.com) added by me."
|
||||
);
|
||||
@@ -373,7 +373,7 @@ mod tests {
|
||||
StockMessage::MsgGrpName,
|
||||
"Some chat",
|
||||
"Other chat",
|
||||
DC_CONTACT_ID_SELF
|
||||
DC_CONTACT_ID_SELF as u32
|
||||
),
|
||||
"Group name changed from \"Some chat\" to \"Other chat\" by me."
|
||||
)
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
import os
|
||||
import re
|
||||
|
||||
if __name__ == "__main__":
|
||||
filestats = []
|
||||
for fn in Path(".").glob("**/*.rs"):
|
||||
s = fn.read_text()
|
||||
s = re.sub(r"(?m)///.*$", "", s) # remove comments
|
||||
unsafe = s.count("unsafe")
|
||||
free = s.count("free(")
|
||||
gotoblocks = s.count("current_block =")
|
||||
filestats.append((fn, unsafe, free, gotoblocks))
|
||||
for fn in os.listdir():
|
||||
if fn.endswith(".rs"):
|
||||
s = open(fn).read()
|
||||
s = re.sub(r'(?m)///.*$', '', s) # remove comments
|
||||
unsafe = s.count("unsafe")
|
||||
free = s.count("free(")
|
||||
gotoblocks = s.count("current_block =")
|
||||
filestats.append((fn, unsafe, free, gotoblocks))
|
||||
|
||||
sum_unsafe, sum_free, sum_gotoblocks = 0, 0, 0
|
||||
|
||||
for fn, unsafe, free, gotoblocks in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
|
||||
print("{0: <30} unsafe: {1: >3} free: {2: >3} goto-blocks: {3: >3}".format(str(fn), unsafe, free, gotoblocks))
|
||||
print("{0: <30} unsafe: {1: >3} free: {2: >3} goto-blocks: {3: >3}".format(fn, unsafe, free, gotoblocks))
|
||||
sum_unsafe += unsafe
|
||||
sum_free += free
|
||||
sum_gotoblocks += gotoblocks
|
||||
|
||||
@@ -34,9 +34,14 @@ pub type dc_precheck_imf_t =
|
||||
pub type dc_set_config_t = fn(_: &Context, _: &str, _: Option<&str>) -> ();
|
||||
pub type dc_get_config_t = fn(_: &Context, _: &str) -> Option<String>;
|
||||
|
||||
pub type sqlite_int64 = i64;
|
||||
pub type sqlite3_int64 = sqlite_int64;
|
||||
|
||||
pub type int32_t = i32;
|
||||
pub type int64_t = i64;
|
||||
pub type uintptr_t = libc::uintptr_t;
|
||||
pub type size_t = libc::size_t;
|
||||
pub type ssize_t = libc::ssize_t;
|
||||
pub type uint32_t = libc::c_uint;
|
||||
pub type uint8_t = libc::c_uchar;
|
||||
pub type uint16_t = libc::c_ushort;
|
||||
|
||||
21
src/x.rs
21
src/x.rs
@@ -54,6 +54,27 @@ pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn strncasecmp(
|
||||
s1: *const libc::c_char,
|
||||
s2: *const libc::c_char,
|
||||
n: libc::size_t,
|
||||
) -> libc::c_int {
|
||||
let s1 = std::ffi::CStr::from_ptr(s1)
|
||||
.to_string_lossy()
|
||||
.to_lowercase();
|
||||
let s2 = std::ffi::CStr::from_ptr(s2)
|
||||
.to_string_lossy()
|
||||
.to_lowercase();
|
||||
let m1 = std::cmp::min(n, s1.len());
|
||||
let m2 = std::cmp::min(n, s2.len());
|
||||
|
||||
if s1[..m1] == s2[..m2] {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
253
tests/stress.rs
253
tests/stress.rs
@@ -2,16 +2,23 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use mmime::mailimf_types::*;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_lot::*;
|
||||
use deltachat::dc_mimeparser::*;
|
||||
use deltachat::dc_qr::*;
|
||||
use deltachat::dc_saxparser::*;
|
||||
use deltachat::dc_securejoin::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::key::*;
|
||||
use deltachat::keyring::*;
|
||||
@@ -32,6 +39,22 @@ static mut S_EM_SETUPFILE: *const libc::c_char =
|
||||
as *const u8 as *const libc::c_char;
|
||||
|
||||
unsafe fn stress_functions(context: &Context) {
|
||||
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
dc_saxparser_init(&mut saxparser, 0 as *mut libc::c_void);
|
||||
dc_saxparser_parse(
|
||||
&mut saxparser,
|
||||
b"<tag attr=val=\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_saxparser_parse(
|
||||
&mut saxparser,
|
||||
b"<tag attr=\"val\"=\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
|
||||
if 0 != dc_is_open(context) {
|
||||
if dc_file_exist(context, "$BLOBDIR/foobar")
|
||||
|| dc_file_exist(context, "$BLOBDIR/dada")
|
||||
@@ -61,15 +84,21 @@ unsafe fn stress_functions(context: &Context) {
|
||||
context.get_blobdir(),
|
||||
b"foobar\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
assert!(dc_is_blobdir_path(context, as_str(abs_path)));
|
||||
assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo",));
|
||||
assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo",));
|
||||
assert!(dc_is_blobdir_path(context, abs_path));
|
||||
assert!(dc_is_blobdir_path(
|
||||
context,
|
||||
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
assert!(!dc_is_blobdir_path(
|
||||
context,
|
||||
b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
assert!(dc_file_exist(context, as_path(abs_path)));
|
||||
free(abs_path as *mut libc::c_void);
|
||||
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
|
||||
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
|
||||
|
||||
let mut buf: *mut libc::c_void = ptr::null_mut();
|
||||
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
|
||||
let mut buf_bytes: size_t = 0;
|
||||
|
||||
assert_eq!(
|
||||
@@ -162,10 +191,10 @@ unsafe fn stress_functions(context: &Context) {
|
||||
assert!(res.contains(" configured_server_flags "));
|
||||
|
||||
let mut buf_0: *mut libc::c_char;
|
||||
let mut headerline: *const libc::c_char = ptr::null();
|
||||
let mut setupcodebegin: *const libc::c_char = ptr::null();
|
||||
let mut preferencrypt: *const libc::c_char = ptr::null();
|
||||
let mut base64: *const libc::c_char = ptr::null();
|
||||
let mut headerline: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut setupcodebegin: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut preferencrypt: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut base64: *const libc::c_char = 0 as *const libc::c_char;
|
||||
buf_0 = strdup(
|
||||
b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\ndata\n-----END PGP MESSAGE-----\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
@@ -174,7 +203,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
assert!(ok);
|
||||
@@ -199,7 +228,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
@@ -226,7 +255,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
@@ -251,7 +280,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
@@ -264,7 +293,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
assert!(ok);
|
||||
@@ -297,7 +326,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut preferencrypt,
|
||||
&mut base64,
|
||||
);
|
||||
@@ -353,16 +382,16 @@ unsafe fn stress_functions(context: &Context) {
|
||||
);
|
||||
free(norm as *mut libc::c_void);
|
||||
let mut buf_1: *mut libc::c_char;
|
||||
let mut headerline_0: *const libc::c_char = ptr::null();
|
||||
let mut setupcodebegin_0: *const libc::c_char = ptr::null();
|
||||
let mut preferencrypt_0: *const libc::c_char = ptr::null();
|
||||
let mut headerline_0: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut setupcodebegin_0: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut preferencrypt_0: *const libc::c_char = 0 as *const libc::c_char;
|
||||
buf_1 = strdup(S_EM_SETUPFILE);
|
||||
assert!(dc_split_armored_data(
|
||||
buf_1,
|
||||
&mut headerline_0,
|
||||
&mut setupcodebegin_0,
|
||||
&mut preferencrypt_0,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
));
|
||||
assert!(!headerline_0.is_null());
|
||||
assert_eq!(
|
||||
@@ -388,7 +417,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
&mut headerline_0,
|
||||
&mut setupcodebegin_0,
|
||||
&mut preferencrypt_0,
|
||||
ptr::null_mut(),
|
||||
0 as *mut *const libc::c_char,
|
||||
));
|
||||
assert!(!headerline_0.is_null());
|
||||
assert_eq!(
|
||||
@@ -408,55 +437,63 @@ unsafe fn stress_functions(context: &Context) {
|
||||
0
|
||||
);
|
||||
free(buf_1 as *mut libc::c_void);
|
||||
if 0 != dc_is_configured(context) {
|
||||
let setupcode = dc_create_setup_code(context);
|
||||
let setupcode_c = CString::yolo(setupcode.clone());
|
||||
let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
|
||||
let setupfile_c = CString::yolo(setupfile);
|
||||
let payload: *mut libc::c_char;
|
||||
let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
|
||||
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
|
||||
assert!(payload.is_null());
|
||||
assert!(!dc_split_armored_data(
|
||||
payload,
|
||||
&mut headerline_2,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
));
|
||||
assert!(!headerline_2.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
headerline_2,
|
||||
b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
0
|
||||
);
|
||||
free(payload as *mut libc::c_void);
|
||||
}
|
||||
|
||||
// Cant check, no configured context
|
||||
// assert!(dc_is_configured(context) != 0, "Missing configured context");
|
||||
if 0 != dc_is_configured(context) {
|
||||
let qr: *mut libc::c_char = dc_get_securejoin_qr(context, 0i32 as uint32_t);
|
||||
assert!(
|
||||
!(strlen(qr) > 55
|
||||
&& strncmp(
|
||||
qr,
|
||||
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
|
||||
12,
|
||||
) == 0i32
|
||||
&& strncmp(
|
||||
&mut *qr.offset(52isize),
|
||||
b"#a=\x00" as *const u8 as *const libc::c_char,
|
||||
3,
|
||||
) == 0i32)
|
||||
);
|
||||
let mut res: *mut dc_lot_t = dc_check_qr(context, qr);
|
||||
assert!(res.is_null());
|
||||
assert!(!((*res).state == 200i32 || (*res).state == 220i32 || (*res).state == 230i32));
|
||||
|
||||
// let setupcode = dc_create_setup_code(context);
|
||||
// let setupcode_c = CString::yolo(setupcode.clone());
|
||||
// let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
|
||||
// let setupfile_c = CString::yolo(setupfile);
|
||||
// let mut headerline_2: *const libc::c_char = ptr::null();
|
||||
// let payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
|
||||
|
||||
// assert!(payload.is_null());
|
||||
// assert!(!dc_split_armored_data(
|
||||
// payload,
|
||||
// &mut headerline_2,
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ));
|
||||
// assert!(!headerline_2.is_null());
|
||||
// assert_eq!(
|
||||
// strcmp(
|
||||
// headerline_2,
|
||||
// b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
// ),
|
||||
// 0
|
||||
// );
|
||||
// free(payload as *mut libc::c_void);
|
||||
|
||||
// Cant check, no configured context
|
||||
// assert!(dc_is_configured(context) != 0, "missing configured context");
|
||||
|
||||
// let qr = dc_get_securejoin_qr(context, 0);
|
||||
// assert!(!qr.is_null(), "Invalid qr code generated");
|
||||
// let qr_r = as_str(qr);
|
||||
|
||||
// assert!(qr_r.len() > 55);
|
||||
// assert!(qr_r.starts_with("OPENPGP4FPR:"));
|
||||
|
||||
// let res = dc_check_qr(context, qr);
|
||||
// let s = res.get_state();
|
||||
|
||||
// assert!(
|
||||
// s == QrState::AskVerifyContact
|
||||
// || s == QrState::FprMissmatch
|
||||
// || s == QrState::FprWithoutAddr
|
||||
// );
|
||||
|
||||
// free(qr.cast());
|
||||
dc_lot_unref(res);
|
||||
free(qr as *mut libc::c_void);
|
||||
res =
|
||||
dc_check_qr(context,
|
||||
b"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD\x00"
|
||||
as *const u8 as *const libc::c_char);
|
||||
assert!(res.is_null());
|
||||
assert!(!((*res).state == 320i32));
|
||||
assert!(!((*res).id != 0i32 as libc::c_uint));
|
||||
dc_lot_unref(res);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -652,6 +689,74 @@ unsafe fn create_test_context() -> TestContext {
|
||||
TestContext { ctx: ctx, dir: dir }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_kml_parse() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
|
||||
let xml: *const libc::c_char =
|
||||
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</wHeN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></Placemark>\n</Document>\n</kml>\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
|
||||
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
|
||||
|
||||
assert!(!kml.addr.is_null());
|
||||
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
|
||||
|
||||
let locations_ref = &kml.locations.as_ref().unwrap();
|
||||
assert_eq!(locations_ref.len(), 2);
|
||||
|
||||
assert!(locations_ref[0].latitude > 53.6f64);
|
||||
assert!(locations_ref[0].latitude < 53.8f64);
|
||||
assert!(locations_ref[0].longitude > 9.3f64);
|
||||
assert!(locations_ref[0].longitude < 9.5f64);
|
||||
assert!(locations_ref[0].accuracy > 31.9f64);
|
||||
assert!(locations_ref[0].accuracy < 32.1f64);
|
||||
assert_eq!(locations_ref[0].timestamp, 1551906597);
|
||||
|
||||
assert!(locations_ref[1].latitude > 63.6f64);
|
||||
assert!(locations_ref[1].latitude < 63.8f64);
|
||||
assert!(locations_ref[1].longitude > 19.3f64);
|
||||
assert!(locations_ref[1].longitude < 19.5f64);
|
||||
assert!(locations_ref[1].accuracy > 2.4f64);
|
||||
assert!(locations_ref[1].accuracy < 2.6f64);
|
||||
assert_eq!(locations_ref[1].timestamp, 1544739072);
|
||||
|
||||
dc_kml_unref(&mut kml);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_mimeparser_with_context() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
|
||||
let mut mimeparser = dc_mimeparser_new(&context.ctx);
|
||||
let raw: *const libc::c_char =
|
||||
b"Content-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
|
||||
dc_mimeparser_parse(&mut mimeparser, raw, strlen(raw));
|
||||
assert_eq!(
|
||||
as_str(mimeparser.subject as *const libc::c_char),
|
||||
"inner-subject",
|
||||
);
|
||||
|
||||
let mut of: *mut mailimf_optional_field =
|
||||
dc_mimeparser_lookup_optional_field(&mimeparser, "X-Special-A");
|
||||
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "special-a",);
|
||||
|
||||
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Foo");
|
||||
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "Bar",);
|
||||
|
||||
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version");
|
||||
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "1.0",);
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
|
||||
dc_mimeparser_unref(&mut mimeparser);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_get_oauth2_url() {
|
||||
let ctx = unsafe { create_test_context() };
|
||||
@@ -715,15 +820,17 @@ fn test_chat() {
|
||||
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
||||
assert_ne!(contact1, 0);
|
||||
|
||||
let chat_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
|
||||
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
assert!(chat_id > 9, "chat_id too small {}", chat_id);
|
||||
let chat = Chat::load_from_db(&context.ctx, chat_id).unwrap();
|
||||
let chat = dc_chat_new(&context.ctx);
|
||||
assert!(dc_chat_load_from_db(chat, chat_id));
|
||||
|
||||
let chat2_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
|
||||
let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
assert_eq!(chat2_id, chat_id);
|
||||
let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap();
|
||||
let chat2 = dc_chat_new(&context.ctx);
|
||||
assert!(dc_chat_load_from_db(chat2, chat2_id));
|
||||
|
||||
assert_eq!(chat2.name, chat.name);
|
||||
assert_eq!(as_str((*chat2).name), as_str((*chat).name));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user