mirror of
https://github.com/chatmail/core.git
synced 2026-07-05 06:54:57 +03:00
Compare commits
86 Commits
safer_job
...
fix_upload
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea28ba6fbd | ||
|
|
3bfaaf10e3 | ||
|
|
73da9b21a3 | ||
|
|
5f33a8c54c | ||
|
|
1d09d2f0d1 | ||
|
|
5b993f601f | ||
|
|
77be6c4294 | ||
|
|
1497f263dc | ||
|
|
d6e2798f5a | ||
|
|
8e41037eb1 | ||
|
|
0bcd3bc94e | ||
|
|
fb5d53226d | ||
|
|
b5a9cc1380 | ||
|
|
1ba7368c6d | ||
|
|
c1a78d9c3e | ||
|
|
ad36671adb | ||
|
|
8902d0843b | ||
|
|
010ac6a6ac | ||
|
|
c71589a710 | ||
|
|
9b70ea0595 | ||
|
|
a6928c29e8 | ||
|
|
3d9129c198 | ||
|
|
cf8f37449c | ||
|
|
886870964b | ||
|
|
6a5e107e64 | ||
|
|
723624b3f7 | ||
|
|
634dfaa1ca | ||
|
|
2d00495d29 | ||
|
|
ea964fd733 | ||
|
|
dd5803e576 | ||
|
|
85e16f6e82 | ||
|
|
0a9f61783d | ||
|
|
19a0071585 | ||
|
|
2fe07e86c7 | ||
|
|
22c1b34ebf | ||
|
|
eca11a74d7 | ||
|
|
b3df24d188 | ||
|
|
6fcd6419bd | ||
|
|
edb0fa17af | ||
|
|
86eb9cc058 | ||
|
|
47c0526026 | ||
|
|
f5416b1c2c | ||
|
|
a9f42f7a9e | ||
|
|
7650e1c7df | ||
|
|
0de76d6d3f | ||
|
|
db4f972315 | ||
|
|
8e4a01c98d | ||
|
|
21976b14a6 | ||
|
|
d3048aa06f | ||
|
|
a1a85270e6 | ||
|
|
2f6438f75a | ||
|
|
ff0fdd5837 | ||
|
|
25b0a26ff9 | ||
|
|
3f8abd2218 | ||
|
|
4ec214b3fc | ||
|
|
cefbd64f37 | ||
|
|
d25d839d6a | ||
|
|
23d49560bf | ||
|
|
bb16849eef | ||
|
|
ae17971599 | ||
|
|
f13b068479 | ||
|
|
850941bf8b | ||
|
|
11dd156594 | ||
|
|
78587ee6b1 | ||
|
|
3eeb184278 | ||
|
|
7632eb1ce0 | ||
|
|
3eea175d36 | ||
|
|
8f24ff74dd | ||
|
|
6fab7d0a27 | ||
|
|
7cc52e0a55 | ||
|
|
aa0801014a | ||
|
|
9e379338bc | ||
|
|
ba62d13a14 | ||
|
|
d7d7147549 | ||
|
|
8a73f84003 | ||
|
|
118599b4cc | ||
|
|
1d32e010ae | ||
|
|
91481caf89 | ||
|
|
491826556b | ||
|
|
6463019faf | ||
|
|
e3b2a7a69b | ||
|
|
51b54fce64 | ||
|
|
184c58bf36 | ||
|
|
39abb0b0ad | ||
|
|
cb6c8ac78b | ||
|
|
a906faeb35 |
@@ -1,5 +1,4 @@
|
||||
version: 2.1
|
||||
|
||||
executors:
|
||||
default:
|
||||
docker:
|
||||
@@ -153,11 +152,12 @@ 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
|
||||
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -18,3 +18,7 @@ __pycache__
|
||||
python/src/deltachat/capi*.so
|
||||
|
||||
python/liveconfig*
|
||||
|
||||
# ignore doxgen generated files
|
||||
deltachat-ffi/html
|
||||
deltachat-ffi/xml
|
||||
|
||||
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -463,6 +463,7 @@ version = "1.0.0-alpha.4"
|
||||
dependencies = [
|
||||
"backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
||||
@@ -49,6 +49,7 @@ 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"
|
||||
|
||||
@@ -34,7 +34,8 @@ echo -----------------------
|
||||
# Bundle external shared libraries into the wheels
|
||||
pushd $WHEELHOUSEDIR
|
||||
|
||||
pip install devpi-client
|
||||
pip3 install -U setuptools
|
||||
pip3 install devpi-client
|
||||
devpi use https://m.devpi.net
|
||||
devpi login dc --password $DEVPI_LOGIN
|
||||
|
||||
|
||||
2423
deltachat-ffi/Doxyfile
Normal file
2423
deltachat-ffi/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
BIN
deltachat-ffi/Doxyfile-logo.png
Normal file
BIN
deltachat-ffi/Doxyfile-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.9 KiB |
7
deltachat-ffi/Doxyfile.css
Normal file
7
deltachat-ffi/Doxyfile.css
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
/* 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 +1,10 @@
|
||||
# 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
@@ -1,20 +1,22 @@
|
||||
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_configure::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_msg::*;
|
||||
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;
|
||||
@@ -93,7 +95,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 = 0 as *mut libc::c_char;
|
||||
let mut data: *mut libc::c_char = ptr::null_mut();
|
||||
let mut data_bytes: size_t = 0;
|
||||
if !(dc_read_file(
|
||||
context,
|
||||
@@ -127,7 +129,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 = 0 as *mut libc::c_char;
|
||||
let mut suffix: *mut libc::c_char = ptr::null_mut();
|
||||
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 */
|
||||
@@ -210,7 +212,7 @@ 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: *mut dc_msg_t) {
|
||||
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
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();
|
||||
@@ -260,7 +262,7 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
|
||||
free(msgtext as *mut libc::c_void);
|
||||
}
|
||||
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) {
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
|
||||
let mut lines_out = 0;
|
||||
for &msg_id in msglist {
|
||||
if msg_id == 9 as libc::c_uint {
|
||||
@@ -279,9 +281,8 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) {
|
||||
);
|
||||
lines_out += 1
|
||||
}
|
||||
let msg = dc_get_msg(context, msg_id);
|
||||
log_msg(context, "Msg", msg);
|
||||
dc_msg_unref(msg);
|
||||
let msg = dc_get_msg(context, msg_id)?;
|
||||
log_msg(context, "Msg", &msg);
|
||||
}
|
||||
}
|
||||
if lines_out > 0 {
|
||||
@@ -290,6 +291,7 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) {
|
||||
0, "--------------------------------------------------------------------------------"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
@@ -496,9 +498,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: *mut dc_msg_t = dc_get_msg(context, msg_id);
|
||||
if dc_msg_is_setupmessage(msg) {
|
||||
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
|
||||
let msg = 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,
|
||||
@@ -508,7 +510,6 @@ 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!(
|
||||
@@ -526,17 +527,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
"export-backup" => {
|
||||
dc_imex(context, 11, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
dc_imex(context, 11, context.get_blobdir(), ptr::null());
|
||||
}
|
||||
"import-backup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
|
||||
dc_imex(context, 12, arg1_c, 0 as *const libc::c_char);
|
||||
dc_imex(context, 12, arg1_c, ptr::null());
|
||||
}
|
||||
"export-keys" => {
|
||||
dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
dc_imex(context, 1, context.get_blobdir(), ptr::null());
|
||||
}
|
||||
"import-keys" => {
|
||||
dc_imex(context, 2, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
dc_imex(context, 2, context.get_blobdir(), ptr::null());
|
||||
}
|
||||
"export-setup" => {
|
||||
let setup_code = dc_create_setup_code(context);
|
||||
@@ -652,7 +653,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
}
|
||||
}
|
||||
if dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
if location::is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
info!(context, 0, "Location streaming enabled.");
|
||||
}
|
||||
println!("{} chats", cnt);
|
||||
@@ -688,12 +689,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
""
|
||||
},
|
||||
);
|
||||
log_msglist(context, &msglist);
|
||||
let draft = chat::get_draft(context, sel_chat.get_id());
|
||||
if !draft.is_null() {
|
||||
log_msg(context, "Draft", draft);
|
||||
dc_msg_unref(draft);
|
||||
log_msglist(context, &msglist)?;
|
||||
if let Ok(draft) = chat::get_draft(context, sel_chat.get_id()) {
|
||||
log_msg(context, "Draft", &draft);
|
||||
}
|
||||
|
||||
println!(
|
||||
"{} messages.",
|
||||
chat::get_msg_cnt(context, sel_chat.get_id())
|
||||
@@ -779,14 +779,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
println!(
|
||||
"{} contacts\nLocation streaming: {}",
|
||||
contacts.len(),
|
||||
dc_is_sending_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id()),
|
||||
location::is_sending_locations_to_chat(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id()
|
||||
),
|
||||
);
|
||||
}
|
||||
"getlocations" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let contact_id = arg1.parse().unwrap_or_default();
|
||||
let locations = dc_get_locations(
|
||||
let locations = location::get_range(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id,
|
||||
@@ -820,7 +823,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(!arg1.is_empty(), "No timeout given.");
|
||||
|
||||
let seconds = arg1.parse()?;
|
||||
dc_send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds);
|
||||
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(),
|
||||
@@ -835,7 +838,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let latitude = arg1.parse()?;
|
||||
let longitude = arg2.parse()?;
|
||||
|
||||
let continue_streaming = dc_set_location(context, latitude, longitude, 0.);
|
||||
let continue_streaming = location::set(context, latitude, longitude, 0.);
|
||||
if 0 != continue_streaming {
|
||||
println!("Success, streaming should be continued.");
|
||||
} else {
|
||||
@@ -843,7 +846,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
"dellocations" => {
|
||||
dc_delete_all_locations(context);
|
||||
location::delete_all(context)?;
|
||||
}
|
||||
"send" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
@@ -861,7 +864,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
|
||||
|
||||
let msg_0 = dc_msg_new(
|
||||
let mut msg = dc_msg_new(
|
||||
context,
|
||||
if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
@@ -869,10 +872,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
Viewtype::File
|
||||
},
|
||||
);
|
||||
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
|
||||
dc_msg_set_text(msg_0, arg2_c);
|
||||
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), msg_0)?;
|
||||
dc_msg_unref(msg_0);
|
||||
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)?;
|
||||
}
|
||||
"listmsgs" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <query> missing.");
|
||||
@@ -885,24 +887,23 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
|
||||
let msglist = dc_search_msgs(context, chat, arg1_c);
|
||||
|
||||
log_msglist(context, &msglist);
|
||||
log_msglist(context, &msglist)?;
|
||||
println!("{} messages.", msglist.len());
|
||||
}
|
||||
"draft" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
if !arg1.is_empty() {
|
||||
let draft_0 = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(draft_0, arg1_c);
|
||||
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), draft_0);
|
||||
dc_msg_unref(draft_0);
|
||||
println!("Draft saved.");
|
||||
} else {
|
||||
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(),
|
||||
0 as *mut dc_msg_t,
|
||||
Some(&mut draft),
|
||||
);
|
||||
println!("Draft saved.");
|
||||
} else {
|
||||
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), None);
|
||||
println!("Draft deleted.");
|
||||
}
|
||||
}
|
||||
@@ -949,7 +950,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"listfresh" => {
|
||||
let msglist = dc_get_fresh_msgs(context);
|
||||
|
||||
log_msglist(context, &msglist);
|
||||
log_msglist(context, &msglist)?;
|
||||
print!("{} fresh messages.", msglist.len());
|
||||
}
|
||||
"forward" => {
|
||||
|
||||
@@ -14,13 +14,14 @@ 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_securejoin::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::job::*;
|
||||
@@ -384,11 +385,7 @@ 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),
|
||||
0 as *mut libc::c_void,
|
||||
Some("CLI".into()),
|
||||
);
|
||||
let mut context = dc_context_new(Some(receive_event), ptr::null_mut(), Some("CLI".into()));
|
||||
|
||||
unsafe { dc_cmdline_skip_auth() };
|
||||
|
||||
@@ -495,7 +492,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
}
|
||||
"configure" => {
|
||||
start_threads(ctx.clone());
|
||||
dc_configure(&ctx.read().unwrap());
|
||||
configure(&ctx.read().unwrap());
|
||||
}
|
||||
"oauth2" => {
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||
|
||||
@@ -8,10 +8,10 @@ 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::dc_configure::*;
|
||||
use deltachat::job::{
|
||||
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||
};
|
||||
@@ -87,7 +87,7 @@ fn main() {
|
||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||
dc_configure(&ctx);
|
||||
configure(&ctx);
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
|
||||
@@ -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.message.Message` objects.
|
||||
:returns: list of :class:`deltachat.chatting.Contact` objects.
|
||||
"""
|
||||
flags = 0
|
||||
query = as_dc_charpointer(query)
|
||||
|
||||
521
src/chat.rs
521
src/chat.rs
@@ -5,10 +5,10 @@ use crate::chatlist::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::job::*;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::sql::{self, Sql};
|
||||
use crate::stock::StockMessage;
|
||||
@@ -272,7 +272,7 @@ impl<'a> Chat<'a> {
|
||||
unsafe fn prepare_msg_raw(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
msg: *mut dc_msg_t,
|
||||
msg: &mut Message,
|
||||
timestamp: i64,
|
||||
) -> Result<u32, Error> {
|
||||
let mut do_guarantee_e2ee: libc::c_int;
|
||||
@@ -303,11 +303,8 @@ impl<'a> Chat<'a> {
|
||||
"Cannot send message; self not in group.",
|
||||
);
|
||||
} else {
|
||||
let from = context.sql.get_config(context, "configured_addr");
|
||||
if from.is_none() {
|
||||
error!(context, 0, "Cannot send message, not configured.",);
|
||||
} else {
|
||||
let from_c = CString::yolo(from.unwrap());
|
||||
if let Some(from) = context.sql.get_config(context, "configured_addr") {
|
||||
let from_c = CString::yolo(from);
|
||||
new_rfc724_mid = dc_create_outgoing_rfc724_mid(
|
||||
if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup {
|
||||
self.grpid.strdup()
|
||||
@@ -351,11 +348,7 @@ impl<'a> Chat<'a> {
|
||||
.get_config_int(context, "e2ee_enabled")
|
||||
.unwrap_or_else(|| 1);
|
||||
if 0 != e2ee_enabled
|
||||
&& (*msg)
|
||||
.param
|
||||
.get_int(Param::ForcePlaintext)
|
||||
.unwrap_or_default()
|
||||
== 0
|
||||
&& msg.param.get_int(Param::ForcePlaintext).unwrap_or_default() == 0
|
||||
{
|
||||
let mut can_encrypt = 1;
|
||||
let mut all_mutual = 1;
|
||||
@@ -409,9 +402,9 @@ impl<'a> Chat<'a> {
|
||||
}
|
||||
}
|
||||
if 0 != do_guarantee_e2ee {
|
||||
(*msg).param.set_int(Param::GuranteeE2ee, 1);
|
||||
msg.param.set_int(Param::GuranteeE2ee, 1);
|
||||
}
|
||||
(*msg).param.remove(Param::ErroneousE2ee);
|
||||
msg.param.remove(Param::ErroneousE2ee);
|
||||
if !self.is_self_talk()
|
||||
&& self
|
||||
.get_parent_mime_headers(
|
||||
@@ -466,7 +459,7 @@ impl<'a> Chat<'a> {
|
||||
|
||||
// add independent location to database
|
||||
|
||||
if (*msg).param.exists(Param::SetLatitude) {
|
||||
if msg.param.exists(Param::SetLatitude) {
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
@@ -475,16 +468,10 @@ impl<'a> Chat<'a> {
|
||||
VALUES (?,?,?, ?,?,1);",
|
||||
params![
|
||||
timestamp,
|
||||
DC_CONTACT_ID_SELF as i32,
|
||||
DC_CONTACT_ID_SELF,
|
||||
self.id as i32,
|
||||
(*msg)
|
||||
.param
|
||||
.get_float(Param::SetLatitude)
|
||||
.unwrap_or_default(),
|
||||
(*msg)
|
||||
.param
|
||||
.get_float(Param::SetLongitude)
|
||||
.unwrap_or_default(),
|
||||
msg.param.get_float(Param::SetLatitude).unwrap_or_default(),
|
||||
msg.param.get_float(Param::SetLongitude).unwrap_or_default(),
|
||||
],
|
||||
)
|
||||
.is_ok()
|
||||
@@ -513,11 +500,11 @@ impl<'a> Chat<'a> {
|
||||
1i32,
|
||||
to_id as i32,
|
||||
timestamp,
|
||||
(*msg).type_0,
|
||||
(*msg).state,
|
||||
(*msg).text,
|
||||
(*msg).param.to_string(),
|
||||
(*msg).hidden,
|
||||
msg.type_0,
|
||||
msg.state,
|
||||
msg.text,
|
||||
msg.param.to_string(),
|
||||
msg.hidden,
|
||||
to_string(new_in_reply_to),
|
||||
to_string(new_references),
|
||||
location_id as i32,
|
||||
@@ -539,6 +526,8 @@ impl<'a> Chat<'a> {
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(context, 0, "Cannot send message, not configured.",);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,27 +563,20 @@ impl<'a> Chat<'a> {
|
||||
pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result<u32, Error> {
|
||||
let mut chat_id = 0;
|
||||
let mut send_event = false;
|
||||
let msg = unsafe { dc_msg_new_untyped(context) };
|
||||
|
||||
if dc_msg_load_from_db(msg, context, msg_id) {
|
||||
if let Ok(chat) = Chat::load_from_db(context, unsafe { (*msg).chat_id }) {
|
||||
if chat.id > DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if let Ok(msg) = dc_msg_load_from_db(context, msg_id) {
|
||||
if let Ok(chat) = Chat::load_from_db(context, msg.chat_id) {
|
||||
if chat.id > DC_CHAT_ID_LAST_SPECIAL {
|
||||
chat_id = chat.id;
|
||||
if chat.blocked != Blocked::Not {
|
||||
unblock(context, chat.id);
|
||||
send_event = true;
|
||||
}
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
unsafe { (*msg).from_id },
|
||||
Origin::CreateChat,
|
||||
);
|
||||
Contact::scaleup_origin_by_id(context, msg.from_id, Origin::CreateChat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { dc_msg_unref(msg) };
|
||||
|
||||
if send_event {
|
||||
context.call_cb(Event::MSGS_CHANGED, 0, 0);
|
||||
}
|
||||
@@ -620,8 +602,7 @@ pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, E
|
||||
chat_id
|
||||
}
|
||||
Err(err) => {
|
||||
if !Contact::real_exists_by_id(context, contact_id)
|
||||
&& contact_id != DC_CONTACT_ID_SELF as u32
|
||||
if !Contact::real_exists_by_id(context, contact_id) && contact_id != DC_CONTACT_ID_SELF
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
@@ -721,20 +702,19 @@ pub fn get_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, Erro
|
||||
pub fn prepare_msg<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
mut msg: *mut dc_msg_t<'a>,
|
||||
msg: &mut Message<'a>,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(!msg.is_null(), "No message provided");
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL as u32,
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"Cannot prepare message for special chat"
|
||||
);
|
||||
|
||||
unsafe { (*msg).state = MessageState::OutPreparing };
|
||||
msg.state = MessageState::OutPreparing;
|
||||
let msg_id = prepare_msg_common(context, chat_id, msg)?;
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
unsafe { (*msg).chat_id as uintptr_t },
|
||||
unsafe { (*msg).id as uintptr_t },
|
||||
msg.chat_id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
|
||||
Ok(msg_id)
|
||||
@@ -755,12 +735,8 @@ pub fn msgtype_has_file(msgtype: Viewtype) -> bool {
|
||||
fn prepare_msg_common<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
msg: *mut dc_msg_t<'a>,
|
||||
msg: &mut Message<'a>,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(!msg.is_null(), "No message provided");
|
||||
|
||||
let msg = unsafe { &mut *msg };
|
||||
|
||||
msg.id = 0;
|
||||
msg.context = context;
|
||||
|
||||
@@ -881,39 +857,37 @@ pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
pub unsafe fn send_msg<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
msg: *mut dc_msg_t<'a>,
|
||||
msg: &mut Message<'a>,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(!msg.is_null(), "Invalid message");
|
||||
|
||||
if (*msg).state != MessageState::OutPreparing {
|
||||
if msg.state != MessageState::OutPreparing {
|
||||
// automatically prepare normal messages
|
||||
prepare_msg_common(context, chat_id, msg)?;
|
||||
} else {
|
||||
// update message state of separately prepared messages
|
||||
ensure!(
|
||||
chat_id == 0 || chat_id == (*msg).chat_id,
|
||||
chat_id == 0 || chat_id == msg.chat_id,
|
||||
"Inconsistent chat ID"
|
||||
);
|
||||
dc_update_msg_state(context, (*msg).id, MessageState::OutPending);
|
||||
dc_update_msg_state(context, msg.id, MessageState::OutPending);
|
||||
}
|
||||
|
||||
ensure!(
|
||||
job_send_msg(context, (*msg).id) != 0,
|
||||
job_send_msg(context, msg.id) != 0,
|
||||
"Failed to initiate send job"
|
||||
);
|
||||
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
(*msg).chat_id as uintptr_t,
|
||||
(*msg).id as uintptr_t,
|
||||
msg.chat_id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
|
||||
if (*msg).param.exists(Param::SetLatitude) {
|
||||
context.call_cb(Event::LOCATION_CHANGED, DC_CONTACT_ID_SELF, 0);
|
||||
if msg.param.exists(Param::SetLatitude) {
|
||||
context.call_cb(Event::LOCATION_CHANGED, DC_CONTACT_ID_SELF as usize, 0);
|
||||
}
|
||||
|
||||
if 0 == chat_id {
|
||||
let forwards = (*msg).param.get(Param::PrepForwards);
|
||||
let forwards = msg.param.get(Param::PrepForwards);
|
||||
if let Some(forwards) = forwards {
|
||||
for forward in forwards.split(' ') {
|
||||
let id: i32 = forward.parse().unwrap_or_default();
|
||||
@@ -921,20 +895,18 @@ pub unsafe fn send_msg<'a>(
|
||||
// avoid hanging if user tampers with db
|
||||
break;
|
||||
} else {
|
||||
let copy = dc_get_msg(context, id as u32);
|
||||
if !copy.is_null() {
|
||||
if let Ok(mut copy) = dc_get_msg(context, id as u32) {
|
||||
// TODO: handle cleanup and return early instead
|
||||
send_msg(context, 0, copy).unwrap();
|
||||
send_msg(context, 0, &mut copy).unwrap();
|
||||
}
|
||||
dc_msg_unref(copy);
|
||||
}
|
||||
}
|
||||
(*msg).param.remove(Param::PrepForwards);
|
||||
msg.param.remove(Param::PrepForwards);
|
||||
dc_msg_save_param_to_disk(msg);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((*msg).id)
|
||||
Ok(msg.id)
|
||||
}
|
||||
|
||||
pub unsafe fn send_text_msg(
|
||||
@@ -943,18 +915,19 @@ pub unsafe fn send_text_msg(
|
||||
text_to_send: String,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL as u32,
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"bad chat_id = {} <= 9",
|
||||
chat_id
|
||||
);
|
||||
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
(*msg).text = Some(text_to_send);
|
||||
send_msg(context, chat_id, msg)
|
||||
msg.text = Some(text_to_send);
|
||||
send_msg(context, chat_id, &mut msg)
|
||||
}
|
||||
|
||||
pub unsafe fn set_draft(context: &Context, chat_id: u32, msg: *mut dc_msg_t) {
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
// passing `None` as message jsut deletes the draft
|
||||
pub unsafe fn set_draft(context: &Context, chat_id: u32, msg: Option<&mut Message>) {
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return;
|
||||
}
|
||||
if set_draft_raw(context, chat_id, msg) {
|
||||
@@ -962,31 +935,32 @@ pub unsafe fn set_draft(context: &Context, chat_id: u32, msg: *mut dc_msg_t) {
|
||||
};
|
||||
}
|
||||
|
||||
// similar to as dc_set_draft() but does not emit an event
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) -> bool {
|
||||
unsafe fn set_draft_raw(context: &Context, chat_id: u32, mut msg: Option<&mut Message>) -> bool {
|
||||
let mut OK_TO_CONTINUE = true;
|
||||
// similar to as dc_set_draft() but does not emit an event
|
||||
let prev_draft_msg_id: u32;
|
||||
|
||||
let mut sth_changed = false;
|
||||
|
||||
prev_draft_msg_id = get_draft_msg_id(context, chat_id);
|
||||
let prev_draft_msg_id = get_draft_msg_id(context, chat_id);
|
||||
if 0 != prev_draft_msg_id {
|
||||
dc_delete_msg_from_db(context, prev_draft_msg_id);
|
||||
sth_changed = true;
|
||||
}
|
||||
// save new draft
|
||||
if !msg.is_null() {
|
||||
if (*msg).type_0 == Viewtype::Text {
|
||||
OK_TO_CONTINUE = (*msg).text.as_ref().map_or(false, |s| !s.is_empty());
|
||||
} else if msgtype_has_file((*msg).type_0) {
|
||||
if let Some(path_filename) = (*msg).param.get(Param::File) {
|
||||
|
||||
if let Some(ref mut msg) = msg {
|
||||
// save new draft
|
||||
if msg.type_0 == Viewtype::Text {
|
||||
OK_TO_CONTINUE = msg.text.as_ref().map_or(false, |s| !s.is_empty());
|
||||
} else if msgtype_has_file(msg.type_0) {
|
||||
if let Some(path_filename) = msg.param.get(Param::File) {
|
||||
let mut path_filename = path_filename.to_string();
|
||||
if 0 != dc_msg_is_increation(msg) && !dc_is_blobdir_path(context, &path_filename) {
|
||||
OK_TO_CONTINUE = false;
|
||||
} else if !dc_make_rel_and_copy(context, &mut path_filename) {
|
||||
OK_TO_CONTINUE = false;
|
||||
} else {
|
||||
(*msg).param.set(Param::File, path_filename);
|
||||
msg.param.set(Param::File, path_filename);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1002,10 +976,10 @@ unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) ->
|
||||
chat_id as i32,
|
||||
1,
|
||||
time(),
|
||||
(*msg).type_0,
|
||||
msg.type_0,
|
||||
MessageState::OutDraft,
|
||||
(*msg).text.as_ref().map(String::as_str).unwrap_or(""),
|
||||
(*msg).param.to_string(),
|
||||
msg.text.as_ref().map(String::as_str).unwrap_or(""),
|
||||
msg.param.to_string(),
|
||||
1,
|
||||
],
|
||||
)
|
||||
@@ -1015,7 +989,6 @@ unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sth_changed
|
||||
}
|
||||
|
||||
@@ -1031,21 +1004,12 @@ fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 {
|
||||
.unwrap_or_default() as u32
|
||||
}
|
||||
|
||||
pub unsafe fn get_draft(context: &Context, chat_id: u32) -> *mut dc_msg_t {
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
pub unsafe fn get_draft(context: &Context, chat_id: u32) -> Result<Message, Error> {
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
|
||||
let draft_msg_id = get_draft_msg_id(context, chat_id);
|
||||
if draft_msg_id == 0 {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let draft_msg = dc_msg_new_untyped(context);
|
||||
if !dc_msg_load_from_db(draft_msg, context, draft_msg_id) {
|
||||
dc_msg_unref(draft_msg);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
ensure!(draft_msg_id != 0, "Invalid draft message ID");
|
||||
|
||||
draft_msg
|
||||
dc_msg_load_from_db(context, draft_msg_id)
|
||||
}
|
||||
|
||||
pub fn get_chat_msgs(context: &Context, chat_id: u32, flags: u32, marker1before: u32) -> Vec<u32> {
|
||||
@@ -1059,13 +1023,13 @@ pub fn get_chat_msgs(context: &Context, chat_id: u32, flags: u32, marker1before:
|
||||
for row in rows {
|
||||
let (curr_id, ts) = row?;
|
||||
if curr_id as u32 == marker1before {
|
||||
ret.push(DC_MSG_ID_MARKER1 as u32);
|
||||
ret.push(DC_MSG_ID_MARKER1);
|
||||
}
|
||||
if 0 != flags & 0x1 {
|
||||
let curr_local_timestamp = ts + cnv_to_local;
|
||||
let curr_day = (curr_local_timestamp / 86400) as libc::c_int;
|
||||
if curr_day != last_day {
|
||||
ret.push(DC_MSG_ID_LAST_SPECIAL as u32);
|
||||
ret.push(DC_MSG_ID_LAST_SPECIAL);
|
||||
last_day = curr_day;
|
||||
}
|
||||
}
|
||||
@@ -1235,15 +1199,15 @@ pub unsafe fn get_next_media(
|
||||
msg_type3: Viewtype,
|
||||
) -> u32 {
|
||||
let mut ret = 0;
|
||||
let msg: *mut dc_msg_t = dc_msg_new_untyped(context);
|
||||
if dc_msg_load_from_db(msg, context, curr_msg_id) {
|
||||
|
||||
if let Ok(msg) = dc_msg_load_from_db(context, curr_msg_id) {
|
||||
let list = get_chat_media(
|
||||
context,
|
||||
(*msg).chat_id,
|
||||
msg.chat_id,
|
||||
if msg_type != Viewtype::Unknown {
|
||||
msg_type
|
||||
} else {
|
||||
(*msg).type_0
|
||||
msg.type_0
|
||||
},
|
||||
msg_type2,
|
||||
msg_type3,
|
||||
@@ -1263,14 +1227,12 @@ pub unsafe fn get_next_media(
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_msg_unref(msg);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn archive(context: &Context, chat_id: u32, archive: bool) -> Result<(), Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL as u32,
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"bad chat_id = {} <= 9",
|
||||
chat_id
|
||||
);
|
||||
@@ -1302,7 +1264,7 @@ pub fn archive(context: &Context, chat_id: u32, archive: bool) -> Result<(), Err
|
||||
|
||||
pub fn delete(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL as u32,
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"bad chat_id = {} <= 9",
|
||||
chat_id
|
||||
);
|
||||
@@ -1400,10 +1362,9 @@ pub unsafe fn create_group_chat(
|
||||
|
||||
if chat_id != 0 {
|
||||
if 0 != add_to_chat_contacts_table(context, chat_id, 1) {
|
||||
let draft_msg = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(draft_msg, draft_txt.as_ptr());
|
||||
set_draft_raw(context, chat_id, draft_msg);
|
||||
dc_msg_unref(draft_msg);
|
||||
let mut draft_msg = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(&mut draft_msg, draft_txt.as_ptr());
|
||||
set_draft_raw(context, chat_id, Some(&mut draft_msg));
|
||||
}
|
||||
|
||||
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
||||
@@ -1443,7 +1404,7 @@ pub unsafe fn add_contact_to_chat_ex(
|
||||
let mut success: libc::c_int = 0;
|
||||
let contact = Contact::get_by_id(context, contact_id);
|
||||
|
||||
if contact.is_err() || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if contact.is_err() || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return 0;
|
||||
}
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
@@ -1452,92 +1413,88 @@ pub unsafe fn add_contact_to_chat_ex(
|
||||
let contact = contact.unwrap();
|
||||
|
||||
/*this also makes sure, not contacts are added to special or normal chats*/
|
||||
let chat = Chat::load_from_db(context, chat_id);
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
|
||||
if !(!real_group_exists(context, chat_id)
|
||||
|| !Contact::real_exists_by_id(context, contact_id) && contact_id != DC_CONTACT_ID_SELF)
|
||||
{
|
||||
if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) {
|
||||
log_event!(
|
||||
context,
|
||||
Event::ERROR_SELF_NOT_IN_GROUP,
|
||||
0,
|
||||
"Cannot add contact to group; self not in group.",
|
||||
);
|
||||
} else {
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if 0 != flags & 0x1
|
||||
&& chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1
|
||||
{
|
||||
chat.param.remove(Param::Unpromoted);
|
||||
chat.update_param().unwrap();
|
||||
}
|
||||
let self_addr = context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
if contact.get_addr() != &self_addr {
|
||||
// ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly.
|
||||
// if SELF is not in the group, members cannot be added at all.
|
||||
|
||||
if !(!real_group_exists(context, chat_id)
|
||||
|| !Contact::real_exists_by_id(context, contact_id)
|
||||
&& contact_id != DC_CONTACT_ID_SELF as u32
|
||||
|| chat.is_err())
|
||||
{
|
||||
let mut chat = chat.unwrap();
|
||||
|
||||
if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) {
|
||||
log_event!(
|
||||
context,
|
||||
Event::ERROR_SELF_NOT_IN_GROUP,
|
||||
0,
|
||||
"Cannot add contact to group; self not in group.",
|
||||
);
|
||||
} else {
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if 0 != flags & 0x1 && chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 {
|
||||
chat.param.remove(Param::Unpromoted);
|
||||
chat.update_param().unwrap();
|
||||
}
|
||||
let self_addr = context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
if contact.get_addr() != &self_addr {
|
||||
// ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly.
|
||||
// if SELF is not in the group, members cannot be added at all.
|
||||
|
||||
if 0 != is_contact_in_chat(context, chat_id, contact_id) {
|
||||
if 0 == flags & 0x1 {
|
||||
success = 1;
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
} else {
|
||||
// else continue and send status mail
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
if contact.is_verified() != VerifiedStatus::BidirectVerified {
|
||||
error!(
|
||||
if 0 != is_contact_in_chat(context, chat_id, contact_id) {
|
||||
if 0 == flags & 0x1 {
|
||||
success = 1;
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
} else {
|
||||
// else continue and send status mail
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
if contact.is_verified() != VerifiedStatus::BidirectVerified {
|
||||
error!(
|
||||
context, 0,
|
||||
"Only bidirectional verified contacts can be added to verified groups."
|
||||
);
|
||||
OK_TO_CONTINUE = false;
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
}
|
||||
if OK_TO_CONTINUE {
|
||||
if 0 == add_to_chat_contacts_table(context, chat_id, contact_id) {
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if OK_TO_CONTINUE {
|
||||
if 0 == add_to_chat_contacts_table(context, chat_id, contact_id) {
|
||||
OK_TO_CONTINUE = false;
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
contact.get_addr(),
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32,
|
||||
));
|
||||
msg.param.set_int(Param::Cmd, 4);
|
||||
msg.param.set(Param::Arg, contact.get_addr());
|
||||
msg.param.set_int(Param::Arg2, flags);
|
||||
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
}
|
||||
context.call_cb(Event::MSGS_CHANGED, chat_id as uintptr_t, 0 as uintptr_t);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
if OK_TO_CONTINUE {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
(*msg).text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
contact.get_addr(),
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32,
|
||||
));
|
||||
(*msg).param.set_int(Param::Cmd, 4);
|
||||
(*msg).param.set(Param::Arg, contact.get_addr());
|
||||
(*msg).param.set_int(Param::Arg2, flags);
|
||||
(*msg).id = send_msg(context, chat_id, msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
(*msg).id as uintptr_t,
|
||||
);
|
||||
}
|
||||
context.call_cb(Event::MSGS_CHANGED, chat_id as uintptr_t, 0 as uintptr_t);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
};
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
fn real_group_exists(context: &Context, chat_id: u32) -> bool {
|
||||
// check if a group or a verified group exists under the given ID
|
||||
if !context.sql.is_open() || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if !context.sql.is_open() || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1590,78 +1547,72 @@ pub unsafe fn remove_contact_from_chat(
|
||||
contact_id: u32,
|
||||
) -> Result<(), Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL as u32,
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
"bad chat_id = {} <= 9",
|
||||
chat_id
|
||||
);
|
||||
ensure!(
|
||||
contact_id != DC_CONTACT_ID_SELF as u32,
|
||||
"Cannot remove self"
|
||||
);
|
||||
ensure!(contact_id != DC_CONTACT_ID_SELF, "Cannot remove self");
|
||||
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
let mut success = false;
|
||||
|
||||
/* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */
|
||||
/* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */
|
||||
let chat = Chat::load_from_db(context, chat_id);
|
||||
|
||||
if !(!real_group_exists(context, chat_id) || chat.is_err()) {
|
||||
let chat = chat.unwrap();
|
||||
if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) {
|
||||
log_event!(
|
||||
context,
|
||||
Event::ERROR_SELF_NOT_IN_GROUP,
|
||||
0,
|
||||
"Cannot remove contact from chat; self not in group.",
|
||||
);
|
||||
} else {
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
if contact.id == DC_CONTACT_ID_SELF as u32 {
|
||||
set_group_explicitly_left(context, chat.grpid).unwrap();
|
||||
(*msg).text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgGroupLeft,
|
||||
"",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32,
|
||||
));
|
||||
} else {
|
||||
(*msg).text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgDelMember,
|
||||
contact.get_addr(),
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32,
|
||||
));
|
||||
if let Ok(chat) = Chat::load_from_db(context, chat_id) {
|
||||
if real_group_exists(context, chat_id) {
|
||||
if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) {
|
||||
log_event!(
|
||||
context,
|
||||
Event::ERROR_SELF_NOT_IN_GROUP,
|
||||
0,
|
||||
"Cannot remove contact from chat; self not in group.",
|
||||
);
|
||||
} else {
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
msg.type_0 = Viewtype::Text;
|
||||
if contact.id == DC_CONTACT_ID_SELF {
|
||||
set_group_explicitly_left(context, chat.grpid).unwrap();
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgGroupLeft,
|
||||
"",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF,
|
||||
));
|
||||
} else {
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgDelMember,
|
||||
contact.get_addr(),
|
||||
"",
|
||||
DC_CONTACT_ID_SELF,
|
||||
));
|
||||
}
|
||||
msg.param.set_int(Param::Cmd, 5);
|
||||
msg.param.set(Param::Arg, contact.get_addr());
|
||||
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
}
|
||||
(*msg).param.set_int(Param::Cmd, 5);
|
||||
(*msg).param.set(Param::Arg, contact.get_addr());
|
||||
(*msg).id = send_msg(context, chat_id, msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
(*msg).id as uintptr_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?;",
|
||||
params![chat_id as i32, contact_id as i32],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t);
|
||||
success = true;
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?;",
|
||||
params![chat_id as i32, contact_id as i32],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t);
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
|
||||
if !success {
|
||||
bail!("Failed to remove contact");
|
||||
}
|
||||
@@ -1698,7 +1649,7 @@ pub unsafe fn set_chat_name(
|
||||
let mut success = false;
|
||||
|
||||
ensure!(!new_name.as_ref().is_empty(), "Invalid name");
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, "Invalid chat ID");
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
|
||||
|
||||
let chat = Chat::load_from_db(context, chat_id)?;
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
@@ -1728,22 +1679,22 @@ pub unsafe fn set_chat_name(
|
||||
.is_ok()
|
||||
{
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
(*msg).text = Some(context.stock_system_msg(
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgGrpName,
|
||||
&chat.name,
|
||||
new_name.as_ref(),
|
||||
DC_CONTACT_ID_SELF as u32,
|
||||
DC_CONTACT_ID_SELF,
|
||||
));
|
||||
(*msg).param.set_int(Param::Cmd, 2);
|
||||
msg.param.set_int(Param::Cmd, 2);
|
||||
if !chat.name.is_empty() {
|
||||
(*msg).param.set(Param::Arg, &chat.name);
|
||||
msg.param.set(Param::Arg, &chat.name);
|
||||
}
|
||||
(*msg).id = send_msg(context, chat_id, msg).unwrap_or_default();
|
||||
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
(*msg).id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
}
|
||||
context.call_cb(
|
||||
@@ -1756,8 +1707,6 @@ pub unsafe fn set_chat_name(
|
||||
}
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
|
||||
if !success {
|
||||
bail!("Failed to set name");
|
||||
}
|
||||
@@ -1771,7 +1720,7 @@ pub unsafe fn set_chat_profile_image(
|
||||
chat_id: u32,
|
||||
new_image: impl AsRef<str>,
|
||||
) -> Result<(), Error> {
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, "Invalid chat ID");
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
|
||||
|
||||
let mut OK_TO_CONTINUE = true;
|
||||
let mut success = false;
|
||||
@@ -1806,12 +1755,12 @@ pub unsafe fn set_chat_profile_image(
|
||||
}
|
||||
if chat.update_param().is_ok() {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
(*msg).param.set_int(Param::Cmd, 3);
|
||||
msg.param.set_int(Param::Cmd, 3);
|
||||
if let Some(ref new_image_rel) = new_image_rel {
|
||||
(*msg).param.set(Param::Arg, new_image_rel);
|
||||
msg.param.set(Param::Arg, new_image_rel);
|
||||
}
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
(*msg).text = Some(context.stock_system_msg(
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
if new_image_rel.is_some() {
|
||||
StockMessage::MsgGrpImgChanged
|
||||
} else {
|
||||
@@ -1819,13 +1768,13 @@ pub unsafe fn set_chat_profile_image(
|
||||
},
|
||||
"",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32,
|
||||
DC_CONTACT_ID_SELF,
|
||||
));
|
||||
(*msg).id = send_msg(context, chat_id, msg).unwrap_or_default();
|
||||
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
(*msg).id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
}
|
||||
context.call_cb(
|
||||
@@ -1838,8 +1787,6 @@ pub unsafe fn set_chat_profile_image(
|
||||
}
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
|
||||
if !success {
|
||||
bail!("Failed to set profile image");
|
||||
}
|
||||
@@ -1853,11 +1800,10 @@ pub unsafe fn forward_msgs(
|
||||
msg_cnt: libc::c_int,
|
||||
chat_id: u32,
|
||||
) {
|
||||
if msg_ids.is_null() || msg_cnt <= 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if msg_ids.is_null() || msg_cnt <= 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = dc_msg_new_untyped(context);
|
||||
let mut created_db_entries = Vec::new();
|
||||
let mut curr_timestamp: i64;
|
||||
|
||||
@@ -1887,45 +1833,45 @@ pub unsafe fn forward_msgs(
|
||||
|
||||
for id in ids {
|
||||
let src_msg_id = id;
|
||||
if !dc_msg_load_from_db(msg, context, src_msg_id as u32) {
|
||||
let msg = dc_msg_load_from_db(context, src_msg_id as u32);
|
||||
if msg.is_err() {
|
||||
break;
|
||||
}
|
||||
let original_param = (*msg).param.clone();
|
||||
if (*msg).from_id != DC_CONTACT_ID_SELF as u32 {
|
||||
(*msg).param.set_int(Param::Forwarded, 1);
|
||||
let mut msg = msg.unwrap();
|
||||
let original_param = msg.param.clone();
|
||||
if msg.from_id != DC_CONTACT_ID_SELF {
|
||||
msg.param.set_int(Param::Forwarded, 1);
|
||||
}
|
||||
(*msg).param.remove(Param::GuranteeE2ee);
|
||||
(*msg).param.remove(Param::ForcePlaintext);
|
||||
(*msg).param.remove(Param::Cmd);
|
||||
msg.param.remove(Param::GuranteeE2ee);
|
||||
msg.param.remove(Param::ForcePlaintext);
|
||||
msg.param.remove(Param::Cmd);
|
||||
|
||||
let new_msg_id: u32;
|
||||
if (*msg).state == MessageState::OutPreparing {
|
||||
if msg.state == MessageState::OutPreparing {
|
||||
let fresh9 = curr_timestamp;
|
||||
curr_timestamp = curr_timestamp + 1;
|
||||
new_msg_id = chat
|
||||
.prepare_msg_raw(context, msg, fresh9)
|
||||
.prepare_msg_raw(context, &mut msg, fresh9)
|
||||
.unwrap_or_default();
|
||||
let save_param = (*msg).param.clone();
|
||||
(*msg).param = original_param;
|
||||
(*msg).id = src_msg_id as u32;
|
||||
let save_param = msg.param.clone();
|
||||
msg.param = original_param;
|
||||
msg.id = src_msg_id as u32;
|
||||
|
||||
if let Some(old_fwd) = (*msg).param.get(Param::PrepForwards) {
|
||||
if let Some(old_fwd) = msg.param.get(Param::PrepForwards) {
|
||||
let new_fwd = format!("{} {}", old_fwd, new_msg_id);
|
||||
(*msg).param.set(Param::PrepForwards, new_fwd);
|
||||
msg.param.set(Param::PrepForwards, new_fwd);
|
||||
} else {
|
||||
(*msg)
|
||||
.param
|
||||
.set(Param::PrepForwards, new_msg_id.to_string());
|
||||
msg.param.set(Param::PrepForwards, new_msg_id.to_string());
|
||||
}
|
||||
|
||||
dc_msg_save_param_to_disk(msg);
|
||||
(*msg).param = save_param;
|
||||
dc_msg_save_param_to_disk(&mut msg);
|
||||
msg.param = save_param;
|
||||
} else {
|
||||
(*msg).state = MessageState::OutPending;
|
||||
msg.state = MessageState::OutPending;
|
||||
let fresh10 = curr_timestamp;
|
||||
curr_timestamp = curr_timestamp + 1;
|
||||
new_msg_id = chat
|
||||
.prepare_msg_raw(context, msg, fresh10)
|
||||
.prepare_msg_raw(context, &mut msg, fresh10)
|
||||
.unwrap_or_default();
|
||||
job_send_msg(context, new_msg_id);
|
||||
}
|
||||
@@ -1941,7 +1887,6 @@ pub unsafe fn forward_msgs(
|
||||
created_db_entries[i + 1] as uintptr_t,
|
||||
);
|
||||
}
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
|
||||
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> libc::c_int {
|
||||
|
||||
@@ -2,9 +2,9 @@ use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_msg::*;
|
||||
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 +195,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 as u32, 0));
|
||||
ids.push((DC_CHAT_ID_ALLDONE_HINT, 0));
|
||||
}
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK as u32, 0));
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
|
||||
}
|
||||
|
||||
Ok(Chatlist { context, ids })
|
||||
@@ -247,7 +247,7 @@ 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 unsafe fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
|
||||
pub fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
|
||||
// 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".
|
||||
@@ -275,29 +275,35 @@ impl<'a> Chatlist<'a> {
|
||||
let mut lastcontact = None;
|
||||
|
||||
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 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 (*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();
|
||||
Some(lastmsg)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
lastmsg
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
None
|
||||
};
|
||||
|
||||
if chat.id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
|
||||
if chat.id == DC_CHAT_ID_ARCHIVED_LINK {
|
||||
ret.text2 = None;
|
||||
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 {
|
||||
} 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());
|
||||
} else {
|
||||
ret.fill(lastmsg, chat, lastcontact.as_ref(), self.context);
|
||||
ret.fill(
|
||||
&mut lastmsg.unwrap(),
|
||||
chat,
|
||||
lastcontact.as_ref(),
|
||||
self.context,
|
||||
);
|
||||
}
|
||||
|
||||
dc_msg_unref(lastmsg);
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
224
src/configure/auto_mozilla.rs
Normal file
224
src/configure/auto_mozilla.rs
Normal file
@@ -0,0 +1,224 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
213
src/configure/auto_outlook.rs
Normal file
213
src/configure/auto_outlook.rs
Normal file
@@ -0,0 +1,213 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::context::Context;
|
||||
@@ -12,8 +10,11 @@ use crate::job::*;
|
||||
use crate::oauth2::*;
|
||||
use crate::param::Params;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
use std::ptr;
|
||||
|
||||
mod auto_outlook;
|
||||
use auto_outlook::outlk_autodiscover;
|
||||
mod auto_mozilla;
|
||||
use auto_mozilla::moz_autoconfigure;
|
||||
|
||||
macro_rules! progress {
|
||||
($context:tt, $progress:expr) => {
|
||||
@@ -29,47 +30,8 @@ macro_rules! progress {
|
||||
};
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* Configure folders
|
||||
******************************************************************************/
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
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)]
|
||||
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)]
|
||||
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 dc_configure(context: &Context) {
|
||||
pub unsafe fn configure(context: &Context) {
|
||||
if 0 != dc_has_ongoing(context) {
|
||||
warn!(
|
||||
context,
|
||||
@@ -187,12 +149,9 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
ok_to_continue0 = true;
|
||||
}
|
||||
if ok_to_continue0 {
|
||||
let parsed: Result<EmailAddress, _> = param.addr.parse();
|
||||
let mut ok_to_continue7 = false;
|
||||
if parsed.is_err() {
|
||||
error!(context, 0, "Bad email-address.");
|
||||
} else {
|
||||
let parsed = parsed.unwrap();
|
||||
if let Ok(parsed) = param.addr.parse() {
|
||||
let parsed: EmailAddress = parsed;
|
||||
let param_domain = parsed.domain;
|
||||
let param_addr_urlencoded =
|
||||
utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string();
|
||||
@@ -647,6 +606,8 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(context, 0, "Bad email-address.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -692,413 +653,6 @@ 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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] = 0 as *mut libc::c_char;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
pub unsafe fn dc_alloc_ongoing(context: &Context) -> libc::c_int {
|
||||
if 0 != dc_has_ongoing(context) {
|
||||
warn!(
|
||||
@@ -1139,3 +693,20 @@ 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,19 +65,19 @@ const DC_IMEX_EXPORT_BACKUP: usize = 11;
|
||||
const DC_IMEX_IMPORT_BACKUP: usize = 12;
|
||||
|
||||
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
|
||||
pub(crate) const DC_CHAT_ID_DEADDROP: usize = 1;
|
||||
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 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: usize = 3;
|
||||
pub const DC_CHAT_ID_TRASH: u32 = 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: usize = 4;
|
||||
const DC_CHAT_ID_MSGS_IN_CREATION: u32 = 4;
|
||||
/// virtual chat showing all messages flagged with msgs.starred=2
|
||||
const DC_CHAT_ID_STARRED: usize = 5;
|
||||
const DC_CHAT_ID_STARRED: u32 = 5;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: usize = 6;
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: u32 = 6;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7;
|
||||
pub const DC_CHAT_ID_ALLDONE_HINT: u32 = 7;
|
||||
/// larger chat IDs are "real" chats, their messages are "real" messages.
|
||||
pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9;
|
||||
pub const DC_CHAT_ID_LAST_SPECIAL: u32 = 9;
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
@@ -106,38 +106,29 @@ impl Default for Chattype {
|
||||
}
|
||||
}
|
||||
|
||||
pub const DC_MSG_ID_MARKER1: usize = 1;
|
||||
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;
|
||||
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
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_CONTACT_ID_UNDEFINED: usize = 0;
|
||||
pub const DC_CONTACT_ID_SELF: usize = 1;
|
||||
const DC_CONTACT_ID_DEVICE: usize = 2;
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
|
||||
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_CREATE_MVBOX: usize = 1;
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
pub enum Delay {
|
||||
Do_not_try_again = 0,
|
||||
At_once = -1,
|
||||
Standard = 3,
|
||||
Increation_poll = 2,
|
||||
}
|
||||
|
||||
// Flags for configuring IMAP and SMTP servers.
|
||||
// These flags are optional
|
||||
// and may be set together with the username, password etc.
|
||||
// via dc_set_config() using the key "server_flags".
|
||||
|
||||
/// Force OAuth2 authorization. This flag does not skip automatic configuration.
|
||||
/// Before calling dc_configure() with DC_LP_AUTH_OAUTH2 set,
|
||||
/// Before calling 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;
|
||||
|
||||
@@ -291,7 +282,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. dc_configure())
|
||||
/// However, for ongoing processes (eg. 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
|
||||
@@ -409,7 +400,7 @@ pub enum Event {
|
||||
/// @return 0
|
||||
LOCATION_CHANGED = 2035,
|
||||
|
||||
/// Inform about the configuration progress started by dc_configure().
|
||||
/// Inform about the configuration progress started by configure().
|
||||
///
|
||||
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
|
||||
@@ -9,10 +9,10 @@ use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_msg::MessageState;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Result;
|
||||
use crate::key::*;
|
||||
use crate::message::MessageState;
|
||||
use crate::peerstate::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -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 as u32 {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
let contact = Contact {
|
||||
context,
|
||||
id: contact_id,
|
||||
@@ -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 as u32);
|
||||
ret.push(DC_CONTACT_ID_SELF);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
@@ -655,7 +655,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 as u32,
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL,
|
||||
"Can not delete special contact"
|
||||
);
|
||||
|
||||
@@ -780,7 +780,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 as u32 {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
return self.context.get_config(Config::Selfavatar);
|
||||
}
|
||||
// TODO: else get image_abs from contact param
|
||||
@@ -810,7 +810,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 as u32 {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
return VerifiedStatus::BidirectVerified;
|
||||
}
|
||||
|
||||
@@ -911,7 +911,6 @@ fn get_first_name<'a>(full_name: &'a str) -> &'a str {
|
||||
/// Returns false if addr is an invalid address, otherwise true.
|
||||
pub fn may_be_valid_addr(addr: &str) -> bool {
|
||||
let res = addr.parse::<EmailAddress>();
|
||||
println!("{:?}", res);
|
||||
res.is_ok()
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_move::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_receive_imf::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::*;
|
||||
@@ -13,6 +12,7 @@ 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;
|
||||
@@ -300,9 +300,11 @@ 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));
|
||||
if blobdir.is_some() && !blobdir.unwrap().is_empty() {
|
||||
let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup();
|
||||
let blobdir = blobdir.unwrap_or_default();
|
||||
if !blobdir.is_empty() {
|
||||
let dir = dc_ensure_no_slash_safe(blobdir).strdup();
|
||||
*context.blobdir.write().unwrap() = dir;
|
||||
} else {
|
||||
let dir = dbfile.to_string() + "-blobs";
|
||||
|
||||
218
src/dc_array.rs
218
src/dc_array.rs
@@ -1,12 +1,11 @@
|
||||
use crate::dc_location::dc_location;
|
||||
use crate::dc_tools::*;
|
||||
use crate::location::Location;
|
||||
use crate::types::*;
|
||||
|
||||
/* * the structure behind dc_array_t */
|
||||
#[derive(Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum dc_array_t {
|
||||
Locations(Vec<dc_location>),
|
||||
Locations(Vec<Location>),
|
||||
Uint(Vec<u32>),
|
||||
}
|
||||
|
||||
@@ -20,10 +19,6 @@ impl dc_array_t {
|
||||
dc_array_t::Locations(Vec::with_capacity(capacity))
|
||||
}
|
||||
|
||||
pub fn into_raw(self) -> *mut Self {
|
||||
Box::into_raw(Box::new(self))
|
||||
}
|
||||
|
||||
pub fn add_id(&mut self, item: uint32_t) {
|
||||
if let Self::Uint(array) = self {
|
||||
array.push(item);
|
||||
@@ -32,7 +27,7 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_location(&mut self, location: dc_location) {
|
||||
pub fn add_location(&mut self, location: Location) {
|
||||
if let Self::Locations(array) = self {
|
||||
array.push(location)
|
||||
} else {
|
||||
@@ -47,7 +42,7 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_location(&self, index: usize) -> &dc_location {
|
||||
pub fn get_location(&self, index: usize) -> &Location {
|
||||
if let Self::Locations(array) = self {
|
||||
&array[index]
|
||||
} else {
|
||||
@@ -55,34 +50,6 @@ 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(),
|
||||
@@ -125,6 +92,14 @@ 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 {
|
||||
@@ -133,161 +108,56 @@ impl From<Vec<u32>> for dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<dc_location>> for dc_array_t {
|
||||
fn from(array: Vec<dc_location>) -> Self {
|
||||
impl From<Vec<Location>> for dc_array_t {
|
||||
fn from(array: Vec<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_id(array: *mut dc_array_t, item: uint32_t) {
|
||||
assert!(!array.is_null());
|
||||
(*array).add_id(item);
|
||||
}
|
||||
|
||||
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_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_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) {
|
||||
if !ret_index.is_null() {
|
||||
*ret_index = i
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const u32 {
|
||||
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()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
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()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_dc_array() {
|
||||
unsafe {
|
||||
let arr = dc_array_new(7 as size_t);
|
||||
assert_eq!(dc_array_get_cnt(arr), 0);
|
||||
let mut arr = dc_array_t::new(7);
|
||||
assert!(arr.is_empty());
|
||||
|
||||
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);
|
||||
|
||||
dc_array_unref(arr);
|
||||
for i in 0..1000 {
|
||||
arr.add_id(i + 2);
|
||||
}
|
||||
|
||||
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 arr = dc_array_new(7);
|
||||
let mut arr = dc_array_t::new(7);
|
||||
for i in 0..1000 {
|
||||
unsafe { dc_array_add_id(arr, (i + 2) as uint32_t) };
|
||||
arr.add_id(i + 2);
|
||||
}
|
||||
unsafe { dc_array_get_id(arr, 1000) };
|
||||
arr.get_id(1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,6 @@ use lazy_static::lazy_static;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
lazy_static! {
|
||||
static ref LINE_RE: regex::Regex = regex::Regex::new(r"(\r?\n)+").unwrap();
|
||||
}
|
||||
@@ -24,19 +21,20 @@ 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 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);
|
||||
pub fn dc_dehtml(buf_terminated: &str) -> String {
|
||||
let buf_terminated = buf_terminated.trim();
|
||||
|
||||
if buf_terminated.is_empty() {
|
||||
return "".into();
|
||||
}
|
||||
|
||||
let mut dehtml = Dehtml {
|
||||
strbuilder: String::with_capacity(strlen(buf_terminated)),
|
||||
strbuilder: String::with_capacity(buf_terminated.len()),
|
||||
add_text: AddText::YesRemoveLineEnds,
|
||||
last_href: None,
|
||||
};
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(as_str(buf_terminated));
|
||||
let mut reader = quick_xml::Reader::from_str(buf_terminated);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
@@ -61,7 +59,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
dehtml.strbuilder.strdup()
|
||||
dehtml.strbuilder
|
||||
}
|
||||
|
||||
fn dehtml_text_cb(event: &BytesText, dehtml: &mut Dehtml) {
|
||||
|
||||
@@ -255,7 +255,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -738,7 +738,7 @@ unsafe fn decrypt_recursive(
|
||||
(if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailmime,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
@@ -766,7 +766,7 @@ unsafe fn decrypt_recursive(
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
*ret_has_unencrypted_parts = 1i32
|
||||
@@ -1019,7 +1019,7 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
} else if (*mime).mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||
@@ -1111,9 +1111,9 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
};
|
||||
unsafe {
|
||||
let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime;
|
||||
let mut decoded_data = 0 as *const libc::c_char;
|
||||
let mut decoded_data = ptr::null();
|
||||
let mut decoded_data_bytes = 0;
|
||||
let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
|
||||
|
||||
assert_eq!(
|
||||
mailmime_transfer_decode(
|
||||
|
||||
106
src/dc_imex.rs
106
src/dc_imex.rs
@@ -8,15 +8,15 @@ 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_configure::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::*;
|
||||
use crate::job::*;
|
||||
use crate::key::*;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::pgp::*;
|
||||
use crate::sql::{self, Sql};
|
||||
@@ -102,7 +102,7 @@ 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: *mut dc_msg_t = ptr::null_mut();
|
||||
let mut msg: Message;
|
||||
if dc_alloc_ongoing(context) == 0 {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
@@ -140,14 +140,13 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
{
|
||||
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
|
||||
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
|
||||
@@ -156,9 +155,7 @@ 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, msg) {
|
||||
dc_msg_unref(msg);
|
||||
msg = ptr::null_mut();
|
||||
if let Ok(msg_id) = chat::send_msg(context, chat_id, &mut msg) {
|
||||
info!(context, 0, "Wait for setup message being sent ...",);
|
||||
loop {
|
||||
if context
|
||||
@@ -171,13 +168,12 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
msg = dc_get_msg(context, msg_id);
|
||||
if 0 != dc_msg_is_sent(msg) {
|
||||
info!(context, 0, "... setup message sent.",);
|
||||
break;
|
||||
if let Ok(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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,7 +183,6 @@ 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()
|
||||
@@ -284,18 +279,17 @@ 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 msg: *mut dc_msg_t = ptr::null_mut();
|
||||
let mut filename: *mut libc::c_char = ptr::null_mut();
|
||||
let mut filecontent: *mut libc::c_char = ptr::null_mut();
|
||||
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();
|
||||
if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) {
|
||||
msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_null()
|
||||
|| !dc_msg_is_setupmessage(msg)
|
||||
let msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_err()
|
||||
|| !dc_msg_is_setupmessage(msg.as_ref().unwrap())
|
||||
|| {
|
||||
filename = dc_msg_get_file(msg);
|
||||
filename = dc_msg_get_file(msg.as_ref().unwrap());
|
||||
filename.is_null()
|
||||
}
|
||||
|| *filename.offset(0isize) as libc::c_int == 0i32
|
||||
@@ -331,7 +325,6 @@ 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
|
||||
@@ -787,32 +780,13 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
_ => {
|
||||
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();
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
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
|
||||
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();
|
||||
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
sql.prepare(
|
||||
"INSERT INTO backup_blobs (file_name, file_content) VALUES (?, ?);",
|
||||
move |mut stmt, _| {
|
||||
@@ -888,6 +862,13 @@ 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()),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
info!(context, 0, "Backup: No files to copy.",);
|
||||
@@ -909,7 +890,14 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot get info for blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir())
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -950,16 +938,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
let mut buf2_headerline: *const libc::c_char = ptr::null_mut();
|
||||
if !dir_name.is_null() {
|
||||
let dir = std::path::Path::new(as_str(dir_name));
|
||||
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();
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
for entry in dir_handle {
|
||||
if entry.is_err() {
|
||||
break;
|
||||
@@ -998,9 +977,9 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
if dc_split_armored_data(
|
||||
buf2,
|
||||
&mut buf2_headerline,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
) && strcmp(
|
||||
buf2_headerline,
|
||||
b"-----BEGIN PGP PUBLIC KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -1043,6 +1022,13 @@ 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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,778 +0,0 @@
|
||||
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_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::job::*;
|
||||
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: i64) {
|
||||
let now = time();
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let is_sending_locations_before: bool;
|
||||
if !(seconds < 0 || 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 } 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);
|
||||
chat::send_msg(context, chat_id, 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 || !job_action_exists(context, Action::MaybeSendLocations) {
|
||||
job_add(context, Action::MaybeSendLocations, 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,
|
||||
) -> Vec<dc_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 = 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,
|
||||
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 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();
|
||||
|
||||
if content_bytes > (1 * 1024 * 1024) {
|
||||
warn!(
|
||||
context,
|
||||
0, "A kml-files with {} bytes is larger than reasonably expected.", content_bytes,
|
||||
);
|
||||
return kml;
|
||||
}
|
||||
|
||||
let content_null = dc_null_terminate(content, content_bytes as libc::c_int);
|
||||
if !content_null.is_null() {
|
||||
let mut reader = quick_xml::Reader::from_str(as_str(content_null));
|
||||
reader.trim_text(true);
|
||||
|
||||
kml.locations = Some(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, &mut kml, &reader),
|
||||
Ok(quick_xml::events::Event::End(ref e)) => kml_endtag_cb(e, &mut kml),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => kml_text_cb(e, &mut kml, &reader),
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Location parsing: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
}
|
||||
|
||||
free(content_null.cast());
|
||||
|
||||
kml
|
||||
}
|
||||
|
||||
fn kml_text_cb<B: std::io::BufRead>(
|
||||
event: &BytesText,
|
||||
kml: &mut dc_kml_t,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
if 0 != kml.tag & (0x4 | 0x10) {
|
||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
||||
|
||||
let val = val
|
||||
.replace("\n", "")
|
||||
.replace("\r", "")
|
||||
.replace("\t", "")
|
||||
.replace(" ", "");
|
||||
|
||||
if 0 != kml.tag & 0x4 && 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) => {
|
||||
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 parts = val.splitn(2, ',').collect::<Vec<_>>();
|
||||
if parts.len() == 2 {
|
||||
kml.curr.longitude = parts[0].parse().unwrap_or_default();
|
||||
kml.curr.latitude = parts[1].parse().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn kml_endtag_cb(event: &BytesEnd, kml: &mut dc_kml_t) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "placemark" {
|
||||
if 0 != kml.tag & 0x1
|
||||
&& 0 != kml.curr.timestamp
|
||||
&& 0. != kml.curr.latitude
|
||||
&& 0. != kml.curr.longitude
|
||||
{
|
||||
if let Some(ref mut locations) = kml.locations {
|
||||
locations.push(std::mem::replace(&mut kml.curr, dc_location::new()));
|
||||
}
|
||||
}
|
||||
kml.tag = 0
|
||||
};
|
||||
}
|
||||
|
||||
fn kml_starttag_cb<B: std::io::BufRead>(
|
||||
event: &BytesStart,
|
||||
kml: &mut dc_kml_t,
|
||||
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()
|
||||
}) {
|
||||
kml.addr = unsafe {
|
||||
addr.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default()
|
||||
.strdup()
|
||||
};
|
||||
}
|
||||
} else if tag == "placemark" {
|
||||
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 tag == "timestamp" && 0 != kml.tag & 0x1 {
|
||||
kml.tag = 0x1 | 0x2
|
||||
} else if tag == "when" && 0 != kml.tag & 0x2 {
|
||||
kml.tag = 0x1 | 0x2 | 0x4
|
||||
} else if tag == "point" && 0 != kml.tag & 0x1 {
|
||||
kml.tag = 0x1 | 0x8
|
||||
} else if tag == "coordinates" && 0 != kml.tag & 0x8 {
|
||||
kml.tag = 0x1 | 0x8 | 0x10;
|
||||
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();
|
||||
|
||||
kml.curr.accuracy = v.trim().parse().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: &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 = 1;
|
||||
(*msg).param.set_int(Param::Cmd, 9);
|
||||
// TODO: handle cleanup on error
|
||||
chat::send_msg(context, chat_id as u32, msg).unwrap();
|
||||
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 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_dc_kml_parse() {
|
||||
unsafe {
|
||||
let context = dummy_context();
|
||||
|
||||
let xml =
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::job::*;
|
||||
use crate::message::*;
|
||||
use crate::param::Params;
|
||||
|
||||
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
|
||||
@@ -18,29 +18,27 @@ pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u3
|
||||
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 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);
|
||||
}
|
||||
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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
|
||||
@@ -14,15 +14,15 @@ use crate::chat::{self, Chat};
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_location::*;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_move::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_securejoin::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Result;
|
||||
use crate::job::*;
|
||||
use crate::location;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::sql;
|
||||
@@ -56,8 +56,8 @@ pub unsafe fn dc_receive_imf(
|
||||
// we use mailmime_parse() through dc_mimeparser (both call mailimf_struct_multiple_parse()
|
||||
// somewhen, I did not found out anything that speaks against this approach yet)
|
||||
|
||||
let mut mime_parser = dc_mimeparser_new(context);
|
||||
dc_mimeparser_parse(&mut mime_parser, imf_raw_not_terminated, imf_raw_bytes);
|
||||
let body = std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes);
|
||||
let mut mime_parser = dc_mimeparser_parse(context, body);
|
||||
|
||||
if mime_parser.header.is_empty() {
|
||||
// Error - even adding an empty record won't help as we do not know the message ID
|
||||
@@ -131,7 +131,7 @@ pub unsafe fn dc_receive_imf(
|
||||
if 0 != check_self {
|
||||
incoming = 0;
|
||||
if 0 != dc_mimeparser_sender_equals_recipient(&mime_parser) {
|
||||
from_id = DC_CONTACT_ID_SELF as u32;
|
||||
from_id = DC_CONTACT_ID_SELF;
|
||||
}
|
||||
} else if from_list.len() >= 1 {
|
||||
// if there is no from given, from_id stays 0 which is just fine. These messages
|
||||
@@ -220,7 +220,7 @@ pub unsafe fn dc_receive_imf(
|
||||
);
|
||||
}
|
||||
|
||||
if !mime_parser.message_kml.is_none() && chat_id > DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if !mime_parser.message_kml.is_none() && chat_id > DC_CHAT_ID_LAST_SPECIAL {
|
||||
save_locations(
|
||||
context,
|
||||
&mime_parser,
|
||||
@@ -362,7 +362,7 @@ unsafe fn add_parts(
|
||||
}
|
||||
|
||||
// 1 or 0 for yes/no
|
||||
msgrmsg = mime_parser.is_send_by_messenger;
|
||||
msgrmsg = mime_parser.is_send_by_messenger as _;
|
||||
if msgrmsg == 0 && 0 != dc_is_reply_to_messenger_message(context, mime_parser) {
|
||||
// 2=no, but is reply to messenger message
|
||||
msgrmsg = 2;
|
||||
@@ -445,7 +445,7 @@ unsafe fn add_parts(
|
||||
|
||||
if *chat_id == 0 {
|
||||
// check if the message belongs to a mailing list
|
||||
if 0 != dc_mimeparser_is_mailinglist_message(mime_parser) {
|
||||
if dc_mimeparser_is_mailinglist_message(mime_parser) {
|
||||
*chat_id = 3;
|
||||
info!(
|
||||
context,
|
||||
@@ -492,7 +492,7 @@ unsafe fn add_parts(
|
||||
}
|
||||
if *chat_id == 0 {
|
||||
// maybe from_id is null or sth. else is suspicious, move message to trash
|
||||
*chat_id = DC_CHAT_ID_TRASH as u32;
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
}
|
||||
|
||||
// if the chat_id is blocked,
|
||||
@@ -509,7 +509,7 @@ unsafe fn add_parts(
|
||||
// the mail is on the IMAP server, probably it is also delivered.
|
||||
// We cannot recreate other states (read, error).
|
||||
state = MessageState::OutDelivered;
|
||||
*from_id = DC_CONTACT_ID_SELF as u32;
|
||||
*from_id = DC_CONTACT_ID_SELF;
|
||||
if !to_ids.is_empty() {
|
||||
*to_id = to_ids[0];
|
||||
if *chat_id == 0 {
|
||||
@@ -565,7 +565,7 @@ unsafe fn add_parts(
|
||||
}
|
||||
}
|
||||
if *chat_id == 0 {
|
||||
*chat_id = DC_CHAT_ID_TRASH as u32;
|
||||
*chat_id = DC_CHAT_ID_TRASH;
|
||||
}
|
||||
}
|
||||
// correct message_timestamp, it should not be used before,
|
||||
@@ -630,21 +630,18 @@ unsafe fn add_parts(
|
||||
continue;
|
||||
}
|
||||
|
||||
if !mime_parser.location_kml.is_none()
|
||||
&& icnt == 1
|
||||
&& !part.msg.is_null()
|
||||
&& (strcmp(
|
||||
part.msg,
|
||||
b"-location-\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
|| *part.msg.offset(0) as libc::c_int == 0)
|
||||
{
|
||||
*hidden = 1;
|
||||
if state == MessageState::InFresh {
|
||||
state = MessageState::InNoticed;
|
||||
if let Some(ref msg) = part.msg {
|
||||
if !mime_parser.location_kml.is_none()
|
||||
&& icnt == 1
|
||||
&& (msg == "-location-" || msg.is_empty())
|
||||
{
|
||||
*hidden = 1;
|
||||
if state == MessageState::InFresh {
|
||||
state = MessageState::InNoticed;
|
||||
}
|
||||
}
|
||||
}
|
||||
if part.type_0 == Viewtype::Text as i32 {
|
||||
if part.type_0 == Viewtype::Text {
|
||||
txt_raw = dc_mprintf(
|
||||
b"%s\n\n%s\x00" as *const u8 as *const libc::c_char,
|
||||
if !mime_parser.subject.is_null() {
|
||||
@@ -673,11 +670,7 @@ unsafe fn add_parts(
|
||||
part.type_0,
|
||||
state,
|
||||
msgrmsg,
|
||||
if !part.msg.is_null() {
|
||||
as_str(part.msg)
|
||||
} else {
|
||||
""
|
||||
},
|
||||
part.msg.as_ref().map_or("", String::as_str),
|
||||
// txt_raw might contain invalid utf8
|
||||
if !txt_raw.is_null() {
|
||||
to_string_lossy(txt_raw)
|
||||
@@ -703,7 +696,7 @@ unsafe fn add_parts(
|
||||
])?;
|
||||
|
||||
free(txt_raw as *mut libc::c_void);
|
||||
txt_raw = 0 as *mut libc::c_char;
|
||||
txt_raw = ptr::null_mut();
|
||||
*insert_msg_id = sql::get_rowid_with_conn(
|
||||
context,
|
||||
conn,
|
||||
@@ -730,7 +723,7 @@ unsafe fn add_parts(
|
||||
);
|
||||
|
||||
// check event to send
|
||||
if *chat_id == DC_CHAT_ID_TRASH as u32 {
|
||||
if *chat_id == DC_CHAT_ID_TRASH {
|
||||
*create_event_to_send = None;
|
||||
} else if 0 != incoming && state == MessageState::InFresh {
|
||||
if 0 != from_id_blocked {
|
||||
@@ -804,7 +797,7 @@ unsafe fn handle_reports(
|
||||
{
|
||||
(*(*(*report_root).mm_data.mm_multipart.mm_mp_list).first).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
.is_null()
|
||||
{
|
||||
@@ -814,11 +807,11 @@ unsafe fn handle_reports(
|
||||
{
|
||||
(*(*(*report_root).mm_data.mm_multipart.mm_mp_list).first).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
})
|
||||
.data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailmime;
|
||||
|
||||
if !report_data.is_null()
|
||||
@@ -908,11 +901,11 @@ unsafe fn handle_reports(
|
||||
}
|
||||
}
|
||||
|
||||
if 0 != mime_parser.is_send_by_messenger || 0 != mdn_consumed {
|
||||
if mime_parser.is_send_by_messenger || 0 != mdn_consumed {
|
||||
let mut param = Params::new();
|
||||
param.set(Param::ServerFolder, server_folder.as_ref());
|
||||
param.set_int(Param::ServerUid, server_uid as i32);
|
||||
if 0 != mime_parser.is_send_by_messenger
|
||||
if mime_parser.is_send_by_messenger
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
@@ -938,36 +931,34 @@ unsafe fn save_locations(
|
||||
let mut send_event = false;
|
||||
|
||||
if !mime_parser.message_kml.is_none() && chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint {
|
||||
let newest_location_id: uint32_t = dc_save_locations(
|
||||
context,
|
||||
chat_id,
|
||||
from_id,
|
||||
&mime_parser.message_kml.as_ref().unwrap().locations,
|
||||
1,
|
||||
);
|
||||
let locations = &mime_parser.message_kml.as_ref().unwrap().locations;
|
||||
let newest_location_id =
|
||||
location::save(context, chat_id, from_id, locations, 1).unwrap_or_default();
|
||||
if 0 != newest_location_id && 0 == hidden {
|
||||
dc_set_msg_location_id(context, insert_msg_id, newest_location_id);
|
||||
location_id_written = true;
|
||||
send_event = true;
|
||||
if location::set_msg_location_id(context, insert_msg_id, newest_location_id).is_ok() {
|
||||
location_id_written = true;
|
||||
send_event = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !mime_parser.location_kml.is_none() && chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint {
|
||||
if !mime_parser.location_kml.as_ref().unwrap().addr.is_null() {
|
||||
if let Some(ref addr) = mime_parser.location_kml.as_ref().unwrap().addr {
|
||||
if let Ok(contact) = Contact::get_by_id(context, from_id) {
|
||||
if !contact.get_addr().is_empty()
|
||||
&& contact.get_addr().to_lowercase()
|
||||
== as_str(mime_parser.location_kml.as_ref().unwrap().addr).to_lowercase()
|
||||
&& contact.get_addr().to_lowercase() == addr.to_lowercase()
|
||||
{
|
||||
let newest_location_id = dc_save_locations(
|
||||
context,
|
||||
chat_id,
|
||||
from_id,
|
||||
&mime_parser.location_kml.as_ref().unwrap().locations,
|
||||
0,
|
||||
);
|
||||
let locations = &mime_parser.location_kml.as_ref().unwrap().locations;
|
||||
let newest_location_id =
|
||||
location::save(context, chat_id, from_id, locations, 0).unwrap_or_default();
|
||||
if newest_location_id != 0 && hidden == 0 && !location_id_written {
|
||||
dc_set_msg_location_id(context, insert_msg_id, newest_location_id);
|
||||
if let Err(err) = location::set_msg_location_id(
|
||||
context,
|
||||
insert_msg_id,
|
||||
newest_location_id,
|
||||
) {
|
||||
error!(context, 0, "Failed to set msg_location_id: {:?}", err);
|
||||
}
|
||||
}
|
||||
send_event = true;
|
||||
}
|
||||
@@ -1260,7 +1251,7 @@ unsafe fn create_or_lookup_group(
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
if chat_id == 0
|
||||
&& 0 == dc_mimeparser_is_mailinglist_message(mime_parser)
|
||||
&& !dc_mimeparser_is_mailinglist_message(mime_parser)
|
||||
&& !grpid.is_empty()
|
||||
&& !grpname.is_null()
|
||||
// otherwise, a pending "quit" message may pop up
|
||||
@@ -1299,10 +1290,10 @@ unsafe fn create_or_lookup_group(
|
||||
}
|
||||
|
||||
// again, check chat_id
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
chat_id = 0;
|
||||
if group_explicitly_left {
|
||||
chat_id = DC_CHAT_ID_TRASH as u32;
|
||||
chat_id = DC_CHAT_ID_TRASH;
|
||||
} else {
|
||||
create_or_lookup_adhoc_group(
|
||||
context,
|
||||
@@ -1343,7 +1334,7 @@ unsafe fn create_or_lookup_group(
|
||||
}
|
||||
if !X_MrGrpImageChanged.is_null() {
|
||||
let mut ok = 0;
|
||||
let mut grpimage = 0 as *mut libc::c_char;
|
||||
let mut grpimage = ptr::null_mut();
|
||||
if strcmp(
|
||||
X_MrGrpImageChanged,
|
||||
b"0\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -1352,7 +1343,7 @@ unsafe fn create_or_lookup_group(
|
||||
ok = 1
|
||||
} else {
|
||||
for part in &mut mime_parser.parts {
|
||||
if part.type_0 == 20 {
|
||||
if part.type_0 == Viewtype::Image {
|
||||
grpimage = part
|
||||
.param
|
||||
.get(Param::File)
|
||||
@@ -1397,7 +1388,7 @@ unsafe fn create_or_lookup_group(
|
||||
let skip = if !X_MrRemoveFromGrp.is_null() {
|
||||
X_MrRemoveFromGrp
|
||||
} else {
|
||||
0 as *mut libc::c_char
|
||||
ptr::null_mut()
|
||||
};
|
||||
sql::execute(
|
||||
context,
|
||||
@@ -1407,9 +1398,9 @@ unsafe fn create_or_lookup_group(
|
||||
)
|
||||
.ok();
|
||||
if skip.is_null() || !addr_cmp(&self_addr, as_str(skip)) {
|
||||
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF as u32);
|
||||
chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF);
|
||||
}
|
||||
if from_id > DC_CHAT_ID_LAST_SPECIAL as u32 {
|
||||
if from_id > DC_CHAT_ID_LAST_SPECIAL {
|
||||
if !Contact::addr_equals_contact(context, &self_addr, from_id as u32)
|
||||
&& (skip.is_null()
|
||||
|| !Contact::addr_equals_contact(context, to_string(skip), from_id as u32))
|
||||
@@ -1435,7 +1426,7 @@ unsafe fn create_or_lookup_group(
|
||||
|
||||
// check the number of receivers -
|
||||
// the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */
|
||||
if to_ids_cnt == 1 && mime_parser.is_send_by_messenger == 0 {
|
||||
if to_ids_cnt == 1 && !mime_parser.is_send_by_messenger {
|
||||
let is_contact_cnt = chat::get_chat_contact_cnt(context, chat_id);
|
||||
if is_contact_cnt > 3 {
|
||||
// to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt.
|
||||
@@ -1479,7 +1470,7 @@ unsafe fn create_or_lookup_adhoc_group(
|
||||
// group matching the to-list or if we can create one
|
||||
let mut chat_id = 0;
|
||||
let mut chat_id_blocked = Blocked::Not;
|
||||
let mut grpname = 0 as *mut libc::c_char;
|
||||
let mut grpname = ptr::null_mut();
|
||||
|
||||
let cleanup = |grpname: *mut libc::c_char,
|
||||
ret_chat_id: *mut uint32_t,
|
||||
@@ -1495,7 +1486,7 @@ unsafe fn create_or_lookup_adhoc_group(
|
||||
};
|
||||
|
||||
// build member list from the given ids
|
||||
if to_ids.is_empty() || 0 != dc_mimeparser_is_mailinglist_message(mime_parser) {
|
||||
if to_ids.is_empty() || dc_mimeparser_is_mailinglist_message(mime_parser) {
|
||||
// too few contacts or a mailinglist
|
||||
cleanup(
|
||||
grpname,
|
||||
@@ -1862,9 +1853,8 @@ unsafe fn set_better_msg<T: AsRef<str>>(mime_parser: &mut dc_mimeparser_t, bette
|
||||
let msg = better_msg.as_ref();
|
||||
if msg.len() > 0 && !mime_parser.parts.is_empty() {
|
||||
let part = &mut mime_parser.parts[0];
|
||||
if (*part).type_0 == 10 {
|
||||
free(part.msg as *mut libc::c_void);
|
||||
part.msg = msg.strdup();
|
||||
if (*part).type_0 == Viewtype::Text {
|
||||
part.msg = Some(msg.to_string());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1918,7 +1908,7 @@ unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist)
|
||||
(if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *const libc::c_char,
|
||||
) {
|
||||
return 1;
|
||||
@@ -1995,7 +1985,7 @@ unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clis
|
||||
(if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *const libc::c_char,
|
||||
) {
|
||||
return 1;
|
||||
@@ -2039,7 +2029,7 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list(
|
||||
let adr: *mut mailimf_address = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailimf_address;
|
||||
if !adr.is_null() {
|
||||
if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
||||
@@ -2070,7 +2060,7 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list(
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2090,7 +2080,7 @@ unsafe fn dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
let mb: *mut mailimf_mailbox = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailimf_mailbox;
|
||||
if !mb.is_null() {
|
||||
add_or_lookup_contact_by_addr(
|
||||
@@ -2105,7 +2095,7 @@ unsafe fn dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use mmime::mailimf_types::*;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
use std::ptr;
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::chat::{self, Chat};
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_token::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::key::*;
|
||||
use crate::lot::LotState;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::qr::check_qr;
|
||||
@@ -30,7 +31,7 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
==== Step 1 in "Setup verified contact" protocol ====
|
||||
========================================================= */
|
||||
|
||||
let mut fingerprint = 0 as *mut libc::c_char;
|
||||
let mut fingerprint = ptr::null_mut();
|
||||
let mut invitenumber: *mut libc::c_char;
|
||||
let mut auth: *mut libc::c_char;
|
||||
let mut qr: Option<String> = None;
|
||||
@@ -244,7 +245,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
.invitenumber
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
0 as *const libc::c_char,
|
||||
ptr::null(),
|
||||
"",
|
||||
);
|
||||
}
|
||||
@@ -270,7 +271,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
context,
|
||||
bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(),
|
||||
None,
|
||||
0 as *mut libc::c_int,
|
||||
ptr::null_mut(),
|
||||
) as libc::c_int
|
||||
} else {
|
||||
ret_chat_id = contact_chat_id as libc::c_int
|
||||
@@ -293,38 +294,37 @@ unsafe fn send_handshake_msg(
|
||||
fingerprint: *const libc::c_char,
|
||||
grpid: impl AsRef<str>,
|
||||
) {
|
||||
let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context);
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
(*msg).text = Some(format!("Secure-Join: {}", to_string(step)));
|
||||
(*msg).hidden = 1;
|
||||
(*msg).param.set_int(Param::Cmd, 7);
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(format!("Secure-Join: {}", to_string(step)));
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::Cmd, 7);
|
||||
if step.is_null() {
|
||||
(*msg).param.remove(Param::Arg);
|
||||
msg.param.remove(Param::Arg);
|
||||
} else {
|
||||
(*msg).param.set(Param::Arg, as_str(step));
|
||||
msg.param.set(Param::Arg, as_str(step));
|
||||
}
|
||||
if !param2.as_ref().is_empty() {
|
||||
(*msg).param.set(Param::Arg2, param2);
|
||||
msg.param.set(Param::Arg2, param2);
|
||||
}
|
||||
if !fingerprint.is_null() {
|
||||
(*msg).param.set(Param::Arg3, as_str(fingerprint));
|
||||
msg.param.set(Param::Arg3, as_str(fingerprint));
|
||||
}
|
||||
if !grpid.as_ref().is_empty() {
|
||||
(*msg).param.set(Param::Arg4, grpid.as_ref());
|
||||
msg.param.set(Param::Arg4, grpid.as_ref());
|
||||
}
|
||||
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
(*msg).param.set_int(
|
||||
msg.param.set_int(
|
||||
Param::ForcePlaintext,
|
||||
ForcePlaintext::AddAutocryptHeader as i32,
|
||||
);
|
||||
} else {
|
||||
(*msg).param.set_int(Param::GuranteeE2ee, 1);
|
||||
msg.param.set_int(Param::GuranteeE2ee, 1);
|
||||
}
|
||||
// TODO. handle cleanup on error
|
||||
chat::send_msg(context, contact_chat_id, msg).unwrap();
|
||||
dc_msg_unref(msg);
|
||||
chat::send_msg(context, contact_chat_id, &mut msg).unwrap();
|
||||
}
|
||||
|
||||
unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) -> uint32_t {
|
||||
@@ -369,10 +369,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
mimeparser: &dc_mimeparser_t,
|
||||
contact_id: uint32_t,
|
||||
) -> libc::c_int {
|
||||
let mut current_block: u64;
|
||||
let mut ok_to_continue: bool;
|
||||
let step: *const libc::c_char;
|
||||
let join_vg: libc::c_int;
|
||||
let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut own_fingerprint: *mut libc::c_char = ptr::null_mut();
|
||||
let contact_chat_id: u32;
|
||||
let contact_chat_id_blocked: Blocked;
|
||||
let mut grpid = "".to_string();
|
||||
@@ -413,10 +413,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
invitenumber = lookup_field(mimeparser, "Secure-Join-Invitenumber");
|
||||
if invitenumber.is_null() {
|
||||
warn!(context, 0, "Secure-join denied (invitenumber missing).",);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) {
|
||||
warn!(context, 0, "Secure-join denied (bad invitenumber).",);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
info!(context, 0, "Secure-join requested.",);
|
||||
|
||||
@@ -434,10 +434,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
b"vc-auth-required\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
"",
|
||||
0 as *const libc::c_char,
|
||||
ptr::null(),
|
||||
"",
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else if strcmp(
|
||||
step,
|
||||
@@ -459,7 +459,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
if cond {
|
||||
warn!(context, 0, "auth-required message out of sync.",);
|
||||
// no error, just aborted somehow or a mail from another handshake
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
let scanned_fingerprint_of_alice = context
|
||||
.bob
|
||||
@@ -509,7 +509,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
},
|
||||
);
|
||||
end_bobs_joining(context, 0i32);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else if 0
|
||||
== fingerprint_equals_sender(
|
||||
context,
|
||||
@@ -524,7 +524,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
as *const libc::c_char,
|
||||
);
|
||||
end_bobs_joining(context, 0i32);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
info!(context, 0, "Fingerprint verified.",);
|
||||
own_fingerprint = get_self_fingerprint(context);
|
||||
@@ -547,7 +547,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
own_fingerprint,
|
||||
grpid,
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
}
|
||||
} else if strcmp(
|
||||
@@ -572,14 +572,14 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
b"Fingerprint not provided.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else if !encrypted_and_signed(mimeparser, as_str(fingerprint)) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"Auth not encrypted.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else if 0
|
||||
== fingerprint_equals_sender(context, as_str(fingerprint), contact_chat_id)
|
||||
{
|
||||
@@ -589,7 +589,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
b"Fingerprint mismatch on inviter-side.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
info!(context, 0, "Fingerprint verified.",);
|
||||
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
|
||||
@@ -601,14 +601,14 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
b"Auth not provided.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else if !dc_token_exists(context, DC_TOKEN_AUTH, auth_0) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"Auth invalid.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else if 0 == mark_peer_as_verified(context, as_str(fingerprint)) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
@@ -616,7 +616,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
b"Fingerprint mismatch on inviter-side.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
@@ -637,15 +637,11 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
);
|
||||
if 0 != join_vg {
|
||||
grpid = to_string(lookup_field(mimeparser, "Secure-Join-Group"));
|
||||
let group_chat_id: uint32_t = chat::get_chat_id_by_grpid(
|
||||
context,
|
||||
&grpid,
|
||||
None,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
let group_chat_id: uint32_t =
|
||||
chat::get_chat_id_by_grpid(context, &grpid, None, ptr::null_mut());
|
||||
if group_chat_id == 0i32 as libc::c_uint {
|
||||
error!(context, 0, "Chat {} not found.", &grpid);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
chat::add_contact_to_chat_ex(
|
||||
context,
|
||||
@@ -653,7 +649,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_id,
|
||||
0x1i32,
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else {
|
||||
send_handshake_msg(
|
||||
@@ -661,7 +657,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char,
|
||||
"",
|
||||
0 as *const libc::c_char,
|
||||
ptr::null(),
|
||||
"",
|
||||
);
|
||||
context.call_cb(
|
||||
@@ -669,7 +665,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_id as uintptr_t,
|
||||
1000i32 as uintptr_t,
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -687,7 +683,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
}
|
||||
if context.bob.read().unwrap().expects != 6 {
|
||||
info!(context, 0, "Message belongs to a different handshake.",);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
let cond = {
|
||||
let bob = context.bob.read().unwrap();
|
||||
@@ -700,7 +696,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
context,
|
||||
0, "Message out of sync or belongs to a different handshake.",
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
let scanned_fingerprint_of_alice = context
|
||||
.bob
|
||||
@@ -750,78 +746,66 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
as *const libc::c_char,
|
||||
);
|
||||
end_bobs_joining(context, 0i32);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
current_block = 5195798230510548452;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else {
|
||||
current_block = 5195798230510548452;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
match current_block {
|
||||
4378276786830486580 => {}
|
||||
_ => {
|
||||
if 0 == mark_peer_as_verified(
|
||||
if ok_to_continue {
|
||||
if 0 == mark_peer_as_verified(context, &scanned_fingerprint_of_alice) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
&scanned_fingerprint_of_alice,
|
||||
) {
|
||||
could_not_establish_secure_connection(
|
||||
contact_chat_id,
|
||||
b"Fingerprint mismatch on joiner-side.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
contact_id,
|
||||
Origin::SecurejoinJoined,
|
||||
);
|
||||
context.call_cb(
|
||||
Event::CONTACTS_CHANGED,
|
||||
0i32 as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
if 0 != join_vg {
|
||||
if !addr_equals_self(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"Fingerprint mismatch on joiner-side.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
contact_id,
|
||||
Origin::SecurejoinJoined,
|
||||
);
|
||||
context.call_cb(
|
||||
Event::CONTACTS_CHANGED,
|
||||
0i32 as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
if 0 != join_vg {
|
||||
if !addr_equals_self(
|
||||
context,
|
||||
as_str(lookup_field(
|
||||
mimeparser,
|
||||
"Chat-Group-Member-Added",
|
||||
)),
|
||||
) {
|
||||
info!(
|
||||
as_str(lookup_field(mimeparser, "Chat-Group-Member-Added")),
|
||||
) {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
current_block = 9180031981464905198;
|
||||
}
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
current_block = 9180031981464905198;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
match current_block {
|
||||
4378276786830486580 => {}
|
||||
_ => {
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.bob.write().unwrap().expects = 0;
|
||||
if 0 != join_vg {
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"vg-member-added-received\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
"",
|
||||
0 as *const libc::c_char,
|
||||
"",
|
||||
);
|
||||
}
|
||||
end_bobs_joining(context, 1i32);
|
||||
current_block = 10256747982273457880;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.bob.write().unwrap().expects = 0;
|
||||
if 0 != join_vg {
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"vg-member-added-received\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
"",
|
||||
ptr::null(),
|
||||
"",
|
||||
);
|
||||
}
|
||||
end_bobs_joining(context, 1i32);
|
||||
ok_to_continue = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -839,7 +823,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if contact.is_verified() == VerifiedStatus::Unverified {
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
@@ -851,21 +835,18 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_id as uintptr_t,
|
||||
1000i32 as uintptr_t,
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
current_block = 4378276786830486580;
|
||||
ok_to_continue = false;
|
||||
}
|
||||
} else {
|
||||
current_block = 10256747982273457880;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
match current_block {
|
||||
4378276786830486580 => {}
|
||||
_ => {
|
||||
if 0 != ret & 0x2i32 {
|
||||
ret |= 0x4i32
|
||||
}
|
||||
if ok_to_continue {
|
||||
if 0 != ret & 0x2i32 {
|
||||
ret |= 0x4i32
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -899,7 +880,7 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint
|
||||
}
|
||||
|
||||
unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char {
|
||||
let mut value: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut value: *const libc::c_char = ptr::null();
|
||||
let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key);
|
||||
if field.is_null()
|
||||
|| (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
|
||||
@@ -909,7 +890,7 @@ unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::
|
||||
value.is_null()
|
||||
}
|
||||
{
|
||||
return 0 as *const libc::c_char;
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
value
|
||||
|
||||
@@ -1,64 +1,49 @@
|
||||
use crate::dc_dehtml::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Simplify {
|
||||
pub is_forwarded: bool,
|
||||
pub is_cut_at_begin: bool,
|
||||
pub is_cut_at_end: 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);
|
||||
}
|
||||
|
||||
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 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();
|
||||
}
|
||||
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()
|
||||
};
|
||||
|
||||
/* 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.retain(|c| c != '\r');
|
||||
out = self.simplify_plain_text(&out, is_msgrmsg);
|
||||
out.retain(|c| c != '\r');
|
||||
|
||||
out
|
||||
}
|
||||
@@ -67,102 +52,75 @@ impl Simplify {
|
||||
* Simplify Plain Text
|
||||
*/
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn simplify_plain_text(
|
||||
&mut self,
|
||||
buf_terminated: *const libc::c_char,
|
||||
is_msgrmsg: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
fn simplify_plain_text(&mut self, buf_terminated: &str, is_msgrmsg: bool) -> String {
|
||||
/* 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 = dc_split_into_lines(buf_terminated);
|
||||
let lines: Vec<_> = buf_terminated.split('\n').collect();
|
||||
let mut l_first: usize = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
let mut is_cut_at_begin = false;
|
||||
let (mut l_last, mut is_cut_at_end) = find_message_footer(&lines);
|
||||
|
||||
if l_last > l_first + 2 {
|
||||
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
|
||||
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()
|
||||
{
|
||||
self.is_forwarded = true;
|
||||
l_first += 3
|
||||
}
|
||||
}
|
||||
for l in l_first..l_last {
|
||||
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
|
||||
let line = lines[l];
|
||||
if line == "-----"
|
||||
|| line == "_____"
|
||||
|| line == "====="
|
||||
|| line == "*****"
|
||||
|| line == "~~~~~"
|
||||
{
|
||||
l_last = l;
|
||||
self.is_cut_at_end = true;
|
||||
is_cut_at_end = true;
|
||||
/* done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if 0 == is_msgrmsg {
|
||||
if !is_msgrmsg {
|
||||
let mut l_lastQuotedLine = None;
|
||||
for l in (l_first..l_last).rev() {
|
||||
line = lines[l];
|
||||
let line = lines[l];
|
||||
if is_plain_quote(line) {
|
||||
l_lastQuotedLine = Some(l)
|
||||
} else if !is_empty_line(line) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if l_lastQuotedLine.is_some() {
|
||||
l_last = l_lastQuotedLine.unwrap();
|
||||
self.is_cut_at_end = true;
|
||||
if let Some(last_quoted_line) = l_lastQuotedLine {
|
||||
l_last = last_quoted_line;
|
||||
is_cut_at_end = true;
|
||||
if l_last > 1 {
|
||||
if is_empty_line(lines[l_last - 1]) {
|
||||
l_last -= 1
|
||||
}
|
||||
}
|
||||
if l_last > 1 {
|
||||
line = lines[l_last - 1];
|
||||
let line = lines[l_last - 1];
|
||||
if is_quoted_headline(line) {
|
||||
l_last -= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if 0 == is_msgrmsg {
|
||||
if !is_msgrmsg {
|
||||
let mut l_lastQuotedLine_0 = None;
|
||||
let mut hasQuotedHeadline = 0;
|
||||
for l in l_first..l_last {
|
||||
line = lines[l];
|
||||
let line = lines[l];
|
||||
if is_plain_quote(line) {
|
||||
l_lastQuotedLine_0 = Some(l)
|
||||
} else if !is_empty_line(line) {
|
||||
@@ -177,21 +135,21 @@ impl Simplify {
|
||||
}
|
||||
}
|
||||
}
|
||||
if l_lastQuotedLine_0.is_some() {
|
||||
l_first = l_lastQuotedLine_0.unwrap() + 1;
|
||||
self.is_cut_at_begin = true
|
||||
if let Some(last_quoted_line) = l_lastQuotedLine_0 {
|
||||
l_first = last_quoted_line + 1;
|
||||
is_cut_at_begin = true
|
||||
}
|
||||
}
|
||||
/* re-create buffer from the remaining lines */
|
||||
let mut ret = String::new();
|
||||
if self.is_cut_at_begin {
|
||||
if 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 {
|
||||
line = lines[l];
|
||||
let line = lines[l];
|
||||
if is_empty_line(line) {
|
||||
pending_linebreaks += 1
|
||||
} else {
|
||||
@@ -205,142 +163,105 @@ impl Simplify {
|
||||
}
|
||||
}
|
||||
// the incoming message might contain invalid UTF8
|
||||
ret += &to_string_lossy(line);
|
||||
ret += line;
|
||||
content_lines_added += 1;
|
||||
pending_linebreaks = 1i32
|
||||
}
|
||||
}
|
||||
if self.is_cut_at_end && (!self.is_cut_at_begin || 0 != content_lines_added) {
|
||||
if is_cut_at_end && (!is_cut_at_begin || 0 != content_lines_added) {
|
||||
ret += " [...]";
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
|
||||
ret.strdup()
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools
|
||||
*/
|
||||
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 {
|
||||
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 > ' ' {
|
||||
return false;
|
||||
}
|
||||
p1 = p1.offset(1isize)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
unsafe fn is_quoted_headline(buf: *const libc::c_char) -> bool {
|
||||
fn is_quoted_headline(buf: &str) -> 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;
|
||||
}
|
||||
|
||||
false
|
||||
buf.len() <= 80 && buf.ends_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
|
||||
fn is_plain_quote(buf: &str) -> bool {
|
||||
buf.starts_with(">")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn test_simplify_trim() {
|
||||
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);
|
||||
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);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"line1\nline2",
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
assert_eq!(plain, "line1\nline2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_parse_href() {
|
||||
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);
|
||||
let mut simplify = Simplify::new();
|
||||
let html = "<a href=url>text</a";
|
||||
let plain = simplify.simplify(html, true, false);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"[text](url)",
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
assert_eq!(plain, "[text](url)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_bold_text() {
|
||||
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 = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
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);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"text *bold*<>",
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
assert_eq!(plain, "text *bold*<>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_html_encoded() {
|
||||
unsafe {
|
||||
let mut simplify = Simplify::new();
|
||||
let html =
|
||||
b"<>"'& äÄöÖüÜß fooÆçÇ ♦‎‏‌&noent;‍\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
let plain = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
let mut simplify = Simplify::new();
|
||||
let html =
|
||||
"<>"'& äÄöÖüÜß fooÆçÇ ♦‎‏‌&noent;‍";
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"<>\"\'& äÄöÖüÜß fooÆçÇ \u{2666}\u{200e}\u{200f}\u{200c}&noent;\u{200d}"
|
||||
);
|
||||
let plain = simplify.simplify(html, true, false);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
}
|
||||
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(""));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr;
|
||||
|
||||
use charset::Charset;
|
||||
use mmime::mailmime_decode::*;
|
||||
@@ -79,7 +80,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 = 0 as *mut libc::c_char;
|
||||
let mut ret_str: *mut libc::c_char = ptr::null_mut();
|
||||
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() {
|
||||
@@ -270,9 +271,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 0 as *mut libc::c_char;
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let mut out: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut out: *mut libc::c_char = ptr::null_mut();
|
||||
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,
|
||||
@@ -616,8 +617,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 = 0 as *mut libc::c_char;
|
||||
let mut charset: *mut libc::c_char = 0 as *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 p2: *const libc::c_char;
|
||||
if !to_decode.is_null() {
|
||||
// get char set
|
||||
|
||||
@@ -355,33 +355,6 @@ unsafe fn dc_utf8_strnlen(s: *const libc::c_char, n: size_t) -> size_t {
|
||||
j
|
||||
}
|
||||
|
||||
/* split string into lines*/
|
||||
pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> Vec<*mut libc::c_char> {
|
||||
let mut lines = Vec::new();
|
||||
let mut line_chars = 0;
|
||||
let mut p1: *const libc::c_char = buf_terminated;
|
||||
let mut line_start: *const libc::c_char = p1;
|
||||
while 0 != *p1 {
|
||||
if *p1 as libc::c_int == '\n' as i32 {
|
||||
lines.push(strndup(line_start, line_chars));
|
||||
p1 = p1.offset(1isize);
|
||||
line_start = p1;
|
||||
line_chars = 0;
|
||||
} else {
|
||||
p1 = p1.offset(1isize);
|
||||
line_chars += 1;
|
||||
}
|
||||
}
|
||||
lines.push(strndup(line_start, line_chars));
|
||||
lines
|
||||
}
|
||||
|
||||
pub unsafe fn dc_free_splitted_lines(lines: Vec<*mut libc::c_char>) {
|
||||
for s in lines {
|
||||
free(s as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_str_from_clist(
|
||||
list: *const clist,
|
||||
delimiter: *const libc::c_char,
|
||||
@@ -1451,7 +1424,7 @@ mod tests {
|
||||
);
|
||||
assert_ne!(str_a, str_a_copy);
|
||||
|
||||
let str_a = 0 as *const u8 as *const libc::c_char;
|
||||
let str_a = ptr::null();
|
||||
let str_a_copy = dc_strdup_keep_null(str_a);
|
||||
assert_eq!(str_a.is_null(), true);
|
||||
assert_eq!(str_a_copy.is_null(), true);
|
||||
|
||||
20
src/imap.rs
20
src/imap.rs
@@ -479,25 +479,19 @@ impl Imap {
|
||||
0, "IMAP unsetup_handle step 1 (closing down stream)."
|
||||
);
|
||||
let stream = self.stream.write().unwrap().take();
|
||||
if stream.is_some() {
|
||||
match stream.unwrap().shutdown(net::Shutdown::Both) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
eprintln!("failed to shutdown connection: {:?}", err);
|
||||
}
|
||||
if let Some(stream) = stream {
|
||||
if let Err(err) = stream.shutdown(net::Shutdown::Both) {
|
||||
eprintln!("failed to shutdown connection: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
);
|
||||
let session = self.session.lock().unwrap().take();
|
||||
if session.is_some() {
|
||||
match session.unwrap().close() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
eprintln!("failed to close connection: {:?}", err);
|
||||
}
|
||||
if let Some(mut session) = self.session.lock().unwrap().take() {
|
||||
if let Err(err) = session.close() {
|
||||
eprintln!("failed to close connection: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
490
src/job.rs
490
src/job.rs
@@ -6,16 +6,16 @@ use deltachat_derive::{FromSql, ToSql};
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::chat;
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_imex::*;
|
||||
use crate::dc_location::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_mimefactory::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::*;
|
||||
use crate::location;
|
||||
use crate::message::*;
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::types::*;
|
||||
@@ -82,7 +82,7 @@ pub struct Job {
|
||||
pub added_timestamp: i64,
|
||||
pub tries: i32,
|
||||
pub param: Params,
|
||||
pub try_again: Delay,
|
||||
pub try_again: i32,
|
||||
pub pending_error: Option<String>,
|
||||
}
|
||||
|
||||
@@ -111,108 +111,18 @@ impl Job {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_SEND(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let mut filename = ptr::null_mut();
|
||||
let mut buf = ptr::null_mut();
|
||||
let mut buf_bytes = 0;
|
||||
|
||||
/* connect to SMTP server, if not yet done */
|
||||
if !context.smtp.lock().unwrap().is_connected() {
|
||||
let loginparam = dc_loginparam_read(context, &context.sql, "configured_");
|
||||
let connected = context.smtp.lock().unwrap().connect(context, &loginparam);
|
||||
|
||||
if !connected {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let filename = self.param.get(Param::File).unwrap_or_default();
|
||||
let body = match dc_read_file_safe(context, filename) {
|
||||
Some(bytes) => bytes,
|
||||
None => {
|
||||
warn!(context, 0, "job {} error", self.job_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let recipients = self.param.get(Param::Recipients);
|
||||
if recipients.is_none() {
|
||||
error!(context, 0, "Missing recipients for job {}", self.job_id,);
|
||||
return;
|
||||
}
|
||||
let recipients_list = recipients
|
||||
.unwrap()
|
||||
.split("\x1e")
|
||||
.filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) {
|
||||
Ok(addr) => Some(addr),
|
||||
Err(err) => {
|
||||
eprintln!("WARNING: invalid recipient: {} {:?}", addr, err);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
/* if there is a msg-id and it does not exist in the db, cancel sending.
|
||||
this happends if dc_delete_msgs() was called
|
||||
before the generated mime was sent out */
|
||||
if 0 != self.foreign_id {
|
||||
if 0 == unsafe { dc_msg_exists(context, self.foreign_id) } {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Message {} for job {} does not exist",
|
||||
self.foreign_id,
|
||||
self.job_id,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* send message while holding the smtp lock long enough
|
||||
to also mark success in the database, to reduce chances
|
||||
of a message getting sent twice.
|
||||
*/
|
||||
let mut sock = context.smtp.lock().unwrap();
|
||||
if 0 == sock.send(context, recipients_list, body) {
|
||||
sock.disconnect();
|
||||
self.try_again_later(Delay::At_once, Some(as_str(sock.error)));
|
||||
return;
|
||||
}
|
||||
dc_delete_file(context, filename);
|
||||
if 0 != self.foreign_id {
|
||||
dc_update_msg_state(
|
||||
context,
|
||||
self.foreign_id,
|
||||
MessageState::OutDelivered,
|
||||
);
|
||||
let chat_id: i32 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
context,
|
||||
"SELECT chat_id FROM msgs WHERE id=?",
|
||||
params![self.foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSG_DELIVERED,
|
||||
chat_id as uintptr_t,
|
||||
self.foreign_id as uintptr_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// this value does not increase the number of tries
|
||||
fn try_again_later(&mut self, try_again: Delay, pending_error: Option<&str>) {
|
||||
self.try_again = try_again;
|
||||
self.pending_error = pending_error.map(|s| s.to_string());
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let msg = unsafe { dc_msg_new_untyped(context) };
|
||||
let mut dest_uid = 0;
|
||||
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3i32, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
@@ -221,7 +131,116 @@ impl Job {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if dc_msg_load_from_db(msg, context, self.foreign_id) {
|
||||
let filename_s = self.param.get(Param::File).unwrap_or_default();
|
||||
filename = unsafe { filename_s.strdup() };
|
||||
if unsafe { strlen(filename) } == 0 {
|
||||
warn!(context, 0, "Missing file name for job {}", self.job_id,);
|
||||
} else if 0 != unsafe { dc_read_file(context, filename, &mut buf, &mut buf_bytes) } {
|
||||
if let Some(recipients) = self.param.get(Param::Recipients) {
|
||||
let recipients_list = recipients
|
||||
.split("\x1e")
|
||||
.filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) {
|
||||
Ok(addr) => Some(addr),
|
||||
Err(err) => {
|
||||
eprintln!("WARNING: invalid recipient: {} {:?}", addr, err);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
/* if there is a msg-id and it does not exist in the db, cancel sending.
|
||||
this happends if dc_delete_msgs() was called
|
||||
before the generated mime was sent out */
|
||||
let ok_to_continue1;
|
||||
if 0 != self.foreign_id {
|
||||
if 0 == unsafe { dc_msg_exists(context, self.foreign_id) } {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Message {} for job {} does not exist",
|
||||
self.foreign_id,
|
||||
self.job_id,
|
||||
);
|
||||
ok_to_continue1 = false;
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
if ok_to_continue1 {
|
||||
/* send message */
|
||||
let body = unsafe {
|
||||
std::slice::from_raw_parts(buf as *const u8, buf_bytes).to_vec()
|
||||
};
|
||||
|
||||
// hold the smtp lock during sending of a job and
|
||||
// its ok/error response processing. Note that if a message
|
||||
// was sent we need to mark it in the database as we
|
||||
// otherwise might send it twice.
|
||||
let mut sock = context.smtp.lock().unwrap();
|
||||
if 0 == sock.send(context, recipients_list, body) {
|
||||
sock.disconnect();
|
||||
self.try_again_later(-1i32, Some(as_str(sock.error)));
|
||||
} else {
|
||||
dc_delete_file(context, filename_s);
|
||||
if 0 != self.foreign_id {
|
||||
dc_update_msg_state(
|
||||
context,
|
||||
self.foreign_id,
|
||||
MessageState::OutDelivered,
|
||||
);
|
||||
let chat_id: i32 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
context,
|
||||
"SELECT chat_id FROM msgs WHERE id=?",
|
||||
params![self.foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSG_DELIVERED,
|
||||
chat_id as uintptr_t,
|
||||
self.foreign_id as uintptr_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "Missing recipients for job {}", self.job_id,);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { free(buf) };
|
||||
unsafe { free(filename.cast()) };
|
||||
}
|
||||
|
||||
// this value does not increase the number of tries
|
||||
fn try_again_later(&mut self, try_again: libc::c_int, pending_error: Option<&str>) {
|
||||
self.try_again = try_again;
|
||||
self.pending_error = pending_error.map(|s| s.to_string());
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let mut dest_uid = 0;
|
||||
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(3, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if let Ok(msg) = dc_msg_load_from_db(context, self.foreign_id) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
@@ -232,8 +251,6 @@ impl Job {
|
||||
}
|
||||
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
|
||||
|
||||
let msg = unsafe { &mut *msg };
|
||||
|
||||
if let Some(dest_folder) = dest_folder {
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
|
||||
@@ -245,88 +262,82 @@ impl Job {
|
||||
&mut dest_uid,
|
||||
) as libc::c_uint
|
||||
{
|
||||
DC_RETRY_LATER => {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
1 => {
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
DC_SUCCESS => {
|
||||
3 => {
|
||||
dc_update_server_uid(context, msg.rfc724_mid, &dest_folder, dest_uid);
|
||||
}
|
||||
_ => {}
|
||||
0 | 2 | _ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { dc_msg_unref(msg) };
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_DELETE_MSG_ON_IMAP(&mut self, context: &Context) {
|
||||
let mut delete_from_server = 1;
|
||||
let msg = unsafe { dc_msg_new_untyped(context) };
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !(!dc_msg_load_from_db(msg, context, self.foreign_id)
|
||||
|| unsafe { (*msg).rfc724_mid.is_null() }
|
||||
|| unsafe { *(*msg).rfc724_mid.offset(0isize) as libc::c_int == 0 })
|
||||
{
|
||||
let ok_to_continue1;
|
||||
/* eg. device messages have no Message-ID */
|
||||
if dc_rfc724_mid_cnt(context, unsafe { (*msg).rfc724_mid }) != 1 {
|
||||
info!(
|
||||
context,
|
||||
0, "The message is deleted from the server when all parts are deleted.",
|
||||
);
|
||||
delete_from_server = 0i32
|
||||
}
|
||||
/* if this is the last existing part of the message, we delete the message from the server */
|
||||
if 0 != delete_from_server {
|
||||
let ok_to_continue;
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if let Ok(mut msg) = dc_msg_load_from_db(context, self.foreign_id) {
|
||||
if !(msg.rfc724_mid.is_null()
|
||||
|| unsafe { *msg.rfc724_mid.offset(0isize) as libc::c_int == 0 })
|
||||
{
|
||||
let ok_to_continue1;
|
||||
/* eg. device messages have no Message-ID */
|
||||
if dc_rfc724_mid_cnt(context, msg.rfc724_mid) != 1 {
|
||||
info!(
|
||||
context,
|
||||
0, "The message is deleted from the server when all parts are deleted.",
|
||||
);
|
||||
delete_from_server = 0i32
|
||||
}
|
||||
/* if this is the last existing part of the message, we delete the message from the server */
|
||||
if 0 != delete_from_server {
|
||||
let ok_to_continue;
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
ok_to_continue = false;
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(3i32, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
let mid = unsafe { CStr::from_ptr((*msg).rfc724_mid).to_str().unwrap() };
|
||||
let server_folder = unsafe { (*msg).server_folder.as_ref().unwrap() };
|
||||
if 0 == inbox.delete_msg(context, mid, server_folder, unsafe {
|
||||
&mut (*msg).server_uid
|
||||
}) {
|
||||
self.try_again_later(Delay::At_once, None);
|
||||
ok_to_continue1 = false;
|
||||
if ok_to_continue {
|
||||
let mid = unsafe { CStr::from_ptr(msg.rfc724_mid).to_str().unwrap() };
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
if 0 == inbox.delete_msg(context, mid, server_folder, &mut msg.server_uid) {
|
||||
self.try_again_later(-1i32, None);
|
||||
ok_to_continue1 = false;
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
ok_to_continue1 = false;
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = false;
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
if ok_to_continue1 {
|
||||
dc_delete_msg_from_db(context, msg.id);
|
||||
}
|
||||
} else {
|
||||
ok_to_continue1 = true;
|
||||
}
|
||||
if ok_to_continue1 {
|
||||
unsafe { dc_delete_msg_from_db(context, (*msg).id) };
|
||||
}
|
||||
}
|
||||
unsafe { dc_msg_unref(msg) }
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) {
|
||||
let ok_to_continue;
|
||||
let msg = unsafe { dc_msg_new_untyped(context) };
|
||||
let inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3i32, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
@@ -335,32 +346,29 @@ impl Job {
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
if dc_msg_load_from_db(msg, context, self.foreign_id) {
|
||||
let server_folder = unsafe { (*msg).server_folder.as_ref().unwrap() };
|
||||
match inbox.set_seen(context, server_folder, unsafe { (*msg).server_uid })
|
||||
as libc::c_uint
|
||||
{
|
||||
if let Ok(msg) = dc_msg_load_from_db(context, self.foreign_id) {
|
||||
let server_folder = msg.server_folder.as_ref().unwrap();
|
||||
match inbox.set_seen(context, server_folder, msg.server_uid) as libc::c_uint {
|
||||
0 => {}
|
||||
1 => {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
_ => {
|
||||
if 0 != unsafe { (*msg).param.get_int(Param::WantsMdn).unwrap_or_default() }
|
||||
if 0 != msg.param.get_int(Param::WantsMdn).unwrap_or_default()
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
let folder = unsafe { (*msg).server_folder.as_ref().unwrap() };
|
||||
let folder = msg.server_folder.as_ref().unwrap();
|
||||
|
||||
match inbox.set_mdnsent(context, folder, unsafe { (*msg).server_uid })
|
||||
as libc::c_uint
|
||||
match inbox.set_mdnsent(context, folder, msg.server_uid) as libc::c_uint
|
||||
{
|
||||
1 => {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
3 => {
|
||||
send_mdn(context, unsafe { (*msg).id });
|
||||
send_mdn(context, msg.id);
|
||||
}
|
||||
0 | 2 | _ => {}
|
||||
}
|
||||
@@ -369,7 +377,6 @@ impl Job {
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { dc_msg_unref(msg) };
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
@@ -387,7 +394,7 @@ impl Job {
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3, None);
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
@@ -397,7 +404,7 @@ impl Job {
|
||||
}
|
||||
if ok_to_continue {
|
||||
if inbox.set_seen(context, &folder, uid) == 0 {
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3i32, None);
|
||||
}
|
||||
if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() {
|
||||
if context
|
||||
@@ -413,7 +420,7 @@ impl Job {
|
||||
if 1 == inbox.mv(context, folder, uid, dest_folder, &mut dest_uid)
|
||||
as libc::c_uint
|
||||
{
|
||||
self.try_again_later(Delay::Standard, None);
|
||||
self.try_again_later(3, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -458,7 +465,7 @@ pub fn perform_imap_fetch(context: &Context) {
|
||||
context,
|
||||
0,
|
||||
"INBOX-fetch done in {:.4} ms.",
|
||||
start.elapsed().as_millis(),
|
||||
start.elapsed().as_nanos() as f64 / 1000.0,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -652,55 +659,38 @@ pub fn job_action_exists(context: &Context, action: Action) -> bool {
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
let mut mimefactory = dc_mimefactory_t {
|
||||
from_addr: ptr::null_mut(),
|
||||
from_displayname: ptr::null_mut(),
|
||||
selfstatus: ptr::null_mut(),
|
||||
recipients_names: ptr::null_mut(),
|
||||
recipients_addr: ptr::null_mut(),
|
||||
timestamp: 0,
|
||||
rfc724_mid: ptr::null_mut(),
|
||||
loaded: DC_MF_NOTHING_LOADED,
|
||||
msg: ptr::null_mut(),
|
||||
chat: None,
|
||||
increation: 0,
|
||||
in_reply_to: ptr::null_mut(),
|
||||
references: ptr::null_mut(),
|
||||
req_mdn: 0,
|
||||
out: ptr::null_mut(),
|
||||
out_encrypted: 0,
|
||||
out_gossiped: 0,
|
||||
out_last_added_location_id: 0,
|
||||
error: ptr::null_mut(),
|
||||
context,
|
||||
};
|
||||
|
||||
/* load message data */
|
||||
if 0 == dc_mimefactory_load_msg(&mut mimefactory, msg_id) || mimefactory.from_addr.is_null() {
|
||||
let mimefactory = dc_mimefactory_load_msg(context, msg_id);
|
||||
if mimefactory.is_err() || mimefactory.as_ref().unwrap().from_addr.is_null() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Cannot load data to send, maybe the message is deleted in between.",
|
||||
);
|
||||
} else {
|
||||
let mut mimefactory = mimefactory.unwrap();
|
||||
// no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed()
|
||||
if chat::msgtype_has_file((*mimefactory.msg).type_0) {
|
||||
if let Some(pathNfilename) = (*mimefactory.msg).param.get(Param::File) {
|
||||
if ((*mimefactory.msg).type_0 == Viewtype::Image
|
||||
|| (*mimefactory.msg).type_0 == Viewtype::Gif)
|
||||
&& !(*mimefactory.msg).param.exists(Param::Width)
|
||||
if chat::msgtype_has_file(mimefactory.msg.type_0) {
|
||||
let file_param = mimefactory
|
||||
.msg
|
||||
.param
|
||||
.get(Param::File)
|
||||
.map(|s| s.to_string());
|
||||
if let Some(pathNfilename) = file_param {
|
||||
if (mimefactory.msg.type_0 == Viewtype::Image
|
||||
|| mimefactory.msg.type_0 == Viewtype::Gif)
|
||||
&& !mimefactory.msg.param.exists(Param::Width)
|
||||
{
|
||||
(*mimefactory.msg).param.set_int(Param::Width, 0);
|
||||
(*mimefactory.msg).param.set_int(Param::Height, 0);
|
||||
mimefactory.msg.param.set_int(Param::Width, 0);
|
||||
mimefactory.msg.param.set_int(Param::Height, 0);
|
||||
|
||||
if let Some(buf) = dc_read_file_safe(context, pathNfilename) {
|
||||
if let Ok((width, height)) = dc_get_filemeta(&buf) {
|
||||
(*mimefactory.msg).param.set_int(Param::Width, width as i32);
|
||||
(*mimefactory.msg)
|
||||
.param
|
||||
.set_int(Param::Height, height as i32);
|
||||
mimefactory.msg.param.set_int(Param::Width, width as i32);
|
||||
mimefactory.msg.param.set_int(Param::Height, height as i32);
|
||||
}
|
||||
}
|
||||
dc_msg_save_param_to_disk(mimefactory.msg);
|
||||
dc_msg_save_param_to_disk(&mut mimefactory.msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -708,7 +698,8 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
if 0 == dc_mimefactory_render(&mut mimefactory) {
|
||||
dc_set_msg_failed(context, msg_id, as_opt_str(mimefactory.error));
|
||||
} else if 0
|
||||
!= (*mimefactory.msg)
|
||||
!= mimefactory
|
||||
.msg
|
||||
.param
|
||||
.get_int(Param::GuranteeE2ee)
|
||||
.unwrap_or_default()
|
||||
@@ -719,7 +710,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
0,
|
||||
"e2e encryption unavailable {} - {:?}",
|
||||
msg_id,
|
||||
(*mimefactory.msg).param.get_int(Param::GuranteeE2ee),
|
||||
mimefactory.msg.param.get_int(Param::GuranteeE2ee),
|
||||
);
|
||||
dc_set_msg_failed(
|
||||
context,
|
||||
@@ -734,7 +725,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
clist_insert_after(
|
||||
mimefactory.recipients_names,
|
||||
(*mimefactory.recipients_names).last,
|
||||
0 as *mut libc::c_void,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
clist_insert_after(
|
||||
mimefactory.recipients_addr,
|
||||
@@ -743,32 +734,38 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
);
|
||||
}
|
||||
if 0 != mimefactory.out_gossiped {
|
||||
chat::set_gossiped_timestamp(context, (*mimefactory.msg).chat_id, time());
|
||||
chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time());
|
||||
}
|
||||
if 0 != mimefactory.out_last_added_location_id {
|
||||
dc_set_kml_sent_timestamp(context, (*mimefactory.msg).chat_id, time());
|
||||
if 0 == (*mimefactory.msg).hidden {
|
||||
dc_set_msg_location_id(
|
||||
if let Err(err) =
|
||||
location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time())
|
||||
{
|
||||
error!(context, 0, "Failed to set kml sent_timestamp: {:?}", err);
|
||||
}
|
||||
if !mimefactory.msg.hidden {
|
||||
if let Err(err) = location::set_msg_location_id(
|
||||
context,
|
||||
(*mimefactory.msg).id,
|
||||
mimefactory.msg.id,
|
||||
mimefactory.out_last_added_location_id,
|
||||
);
|
||||
) {
|
||||
error!(context, 0, "Failed to set msg_location_id: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if 0 != mimefactory.out_encrypted
|
||||
&& (*mimefactory.msg)
|
||||
&& mimefactory
|
||||
.msg
|
||||
.param
|
||||
.get_int(Param::GuranteeE2ee)
|
||||
.unwrap_or_default()
|
||||
== 0
|
||||
{
|
||||
(*mimefactory.msg).param.set_int(Param::GuranteeE2ee, 1);
|
||||
dc_msg_save_param_to_disk(mimefactory.msg);
|
||||
mimefactory.msg.param.set_int(Param::GuranteeE2ee, 1);
|
||||
dc_msg_save_param_to_disk(&mut mimefactory.msg);
|
||||
}
|
||||
success = add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory);
|
||||
}
|
||||
}
|
||||
dc_mimefactory_empty(&mut mimefactory);
|
||||
|
||||
success
|
||||
}
|
||||
@@ -818,7 +815,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
added_timestamp: row.get(4)?,
|
||||
tries: row.get(6)?,
|
||||
param: row.get::<_, String>(3)?.parse().unwrap_or_default(),
|
||||
try_again: Delay::Do_not_try_again,
|
||||
try_again: 0,
|
||||
pending_error: None,
|
||||
};
|
||||
|
||||
@@ -871,7 +868,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
let mut tries = 0;
|
||||
while tries <= 1 {
|
||||
// this can be modified by a job using dc_job_try_again_later()
|
||||
job.try_again = Delay::Do_not_try_again;
|
||||
job.try_again = 0;
|
||||
|
||||
match job.action {
|
||||
Action::SendMsgToSmtp => job.do_DC_JOB_SEND(context),
|
||||
@@ -882,17 +879,17 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
Action::SendMdn => job.do_DC_JOB_SEND(context),
|
||||
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context, &job) },
|
||||
Action::ImexImap => unsafe { dc_job_do_DC_JOB_IMEX_IMAP(context, &job) },
|
||||
Action::MaybeSendLocations => unsafe {
|
||||
dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job)
|
||||
},
|
||||
Action::MaybeSendLocationsEnded => unsafe {
|
||||
dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context, &mut job)
|
||||
},
|
||||
Action::MaybeSendLocations => {
|
||||
location::job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job)
|
||||
}
|
||||
Action::MaybeSendLocationsEnded => {
|
||||
location::job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context, &mut job)
|
||||
}
|
||||
Action::Housekeeping => sql::housekeeping(context),
|
||||
Action::SendMdnOld => {}
|
||||
Action::SendMsgToSmtpOld => {}
|
||||
}
|
||||
if job.try_again != Delay::At_once {
|
||||
if job.try_again != -1 {
|
||||
break;
|
||||
}
|
||||
tries += 1
|
||||
@@ -912,7 +909,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
.unsuspend(context);
|
||||
suspend_smtp_thread(context, false);
|
||||
break;
|
||||
} else if job.try_again == Delay::Increation_poll {
|
||||
} else if job.try_again == 2 {
|
||||
// just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready
|
||||
info!(
|
||||
context,
|
||||
@@ -925,7 +922,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
},
|
||||
job.job_id
|
||||
);
|
||||
} else if job.try_again == Delay::At_once || job.try_again == Delay::Standard {
|
||||
} else if job.try_again == -1 || job.try_again == 3 {
|
||||
let tries = job.tries + 1;
|
||||
if tries < 17 {
|
||||
job.tries = tries;
|
||||
@@ -957,9 +954,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
}
|
||||
} else {
|
||||
if job.action == Action::SendMsgToSmtp {
|
||||
unsafe {
|
||||
dc_set_msg_failed(context, job.foreign_id, job.pending_error.as_ref())
|
||||
};
|
||||
dc_set_msg_failed(context, job.foreign_id, job.pending_error.as_ref());
|
||||
}
|
||||
job.delete(context);
|
||||
}
|
||||
@@ -1012,33 +1007,10 @@ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
|
||||
}
|
||||
|
||||
fn send_mdn(context: &Context, msg_id: uint32_t) {
|
||||
let mut mimefactory = dc_mimefactory_t {
|
||||
from_addr: ptr::null_mut(),
|
||||
from_displayname: ptr::null_mut(),
|
||||
selfstatus: ptr::null_mut(),
|
||||
recipients_names: ptr::null_mut(),
|
||||
recipients_addr: ptr::null_mut(),
|
||||
timestamp: 0,
|
||||
rfc724_mid: ptr::null_mut(),
|
||||
loaded: DC_MF_NOTHING_LOADED,
|
||||
msg: ptr::null_mut(),
|
||||
chat: None,
|
||||
increation: 0,
|
||||
in_reply_to: ptr::null_mut(),
|
||||
references: ptr::null_mut(),
|
||||
req_mdn: 0,
|
||||
out: ptr::null_mut(),
|
||||
out_encrypted: 0,
|
||||
out_gossiped: 0,
|
||||
out_last_added_location_id: 0,
|
||||
error: ptr::null_mut(),
|
||||
context,
|
||||
};
|
||||
|
||||
if !(0 == unsafe { dc_mimefactory_load_mdn(&mut mimefactory, msg_id) }
|
||||
|| 0 == unsafe { dc_mimefactory_render(&mut mimefactory) })
|
||||
{
|
||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory);
|
||||
if let Ok(mut mimefactory) = unsafe { dc_mimefactory_load_mdn(context, msg_id) } {
|
||||
if 0 != unsafe { dc_mimefactory_render(&mut mimefactory) } {
|
||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1046,7 +1018,7 @@ fn send_mdn(context: &Context, msg_id: uint32_t) {
|
||||
fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_t) -> libc::c_int {
|
||||
let pathNfilename: *mut libc::c_char;
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut recipients: *mut libc::c_char = ptr::null_mut();
|
||||
let mut param = Params::new();
|
||||
pathNfilename = unsafe {
|
||||
dc_get_fine_pathNfilename(
|
||||
@@ -1094,7 +1066,7 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_
|
||||
(if mimefactory.loaded as libc::c_uint
|
||||
== DC_MF_MSG_LOADED as libc::c_int as libc::c_uint
|
||||
{
|
||||
unsafe { (*mimefactory.msg).id }
|
||||
mimefactory.msg.id
|
||||
} else {
|
||||
0
|
||||
}) as libc::c_int,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use crate::configure::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::imap::Imap;
|
||||
|
||||
pub struct JobThread {
|
||||
|
||||
@@ -20,12 +20,13 @@ extern crate strum_macros;
|
||||
#[macro_use]
|
||||
mod log;
|
||||
#[macro_use]
|
||||
mod error;
|
||||
pub mod error;
|
||||
|
||||
mod aheader;
|
||||
pub mod chat;
|
||||
pub mod chatlist;
|
||||
pub mod config;
|
||||
pub mod configure;
|
||||
pub mod constants;
|
||||
pub mod contact;
|
||||
pub mod context;
|
||||
@@ -34,7 +35,9 @@ pub mod job;
|
||||
mod job_thread;
|
||||
pub mod key;
|
||||
pub mod keyring;
|
||||
pub mod location;
|
||||
pub mod lot;
|
||||
pub mod message;
|
||||
pub mod oauth2;
|
||||
mod param;
|
||||
pub mod peerstate;
|
||||
@@ -47,16 +50,13 @@ pub mod types;
|
||||
pub mod x;
|
||||
|
||||
pub mod dc_array;
|
||||
pub mod dc_configure;
|
||||
mod dc_dehtml;
|
||||
mod dc_e2ee;
|
||||
pub mod dc_imex;
|
||||
pub mod dc_location;
|
||||
mod dc_loginparam;
|
||||
mod dc_mimefactory;
|
||||
pub mod dc_mimeparser;
|
||||
mod dc_move;
|
||||
pub mod dc_msg;
|
||||
pub mod dc_receive_imf;
|
||||
pub mod dc_securejoin;
|
||||
mod dc_simplify;
|
||||
|
||||
696
src/location.rs
Normal file
696
src/location.rs
Normal file
@@ -0,0 +1,696 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,7 @@ pub unsafe fn dc_split_armored_data(
|
||||
let mut headerline: *mut libc::c_char = ptr::null_mut();
|
||||
let mut base64: *mut libc::c_char = ptr::null_mut();
|
||||
if !ret_headerline.is_null() {
|
||||
*ret_headerline = 0 as *const libc::c_char
|
||||
*ret_headerline = ptr::null()
|
||||
}
|
||||
if !ret_setupcodebegin.is_null() {
|
||||
*ret_setupcodebegin = ptr::null_mut();
|
||||
|
||||
@@ -323,7 +323,7 @@ mod tests {
|
||||
StockMessage::MsgAddMember,
|
||||
"alice@example.com",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32
|
||||
DC_CONTACT_ID_SELF
|
||||
),
|
||||
"Member alice@example.com added by me."
|
||||
)
|
||||
@@ -338,7 +338,7 @@ mod tests {
|
||||
StockMessage::MsgAddMember,
|
||||
"alice@example.com",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF as u32
|
||||
DC_CONTACT_ID_SELF
|
||||
),
|
||||
"Member Alice (alice@example.com) added by me."
|
||||
);
|
||||
@@ -373,7 +373,7 @@ mod tests {
|
||||
StockMessage::MsgGrpName,
|
||||
"Some chat",
|
||||
"Other chat",
|
||||
DC_CONTACT_ID_SELF as u32
|
||||
DC_CONTACT_ID_SELF
|
||||
),
|
||||
"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 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))
|
||||
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))
|
||||
|
||||
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(fn, unsafe, free, gotoblocks))
|
||||
print("{0: <30} unsafe: {1: >3} free: {2: >3} goto-blocks: {3: >3}".format(str(fn), unsafe, free, gotoblocks))
|
||||
sum_unsafe += unsafe
|
||||
sum_free += free
|
||||
sum_gotoblocks += gotoblocks
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use mmime::mailimf_types::*;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
@@ -12,7 +12,6 @@ use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_mimeparser::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::key::*;
|
||||
use deltachat::keyring::*;
|
||||
@@ -70,7 +69,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
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 = 0 as *mut libc::c_void;
|
||||
let mut buf: *mut libc::c_void = ptr::null_mut();
|
||||
let mut buf_bytes: size_t = 0;
|
||||
|
||||
assert_eq!(
|
||||
@@ -163,10 +162,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 = 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;
|
||||
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();
|
||||
buf_0 = strdup(
|
||||
b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\ndata\n-----END PGP MESSAGE-----\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
@@ -175,7 +174,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
&mut base64,
|
||||
);
|
||||
assert!(ok);
|
||||
@@ -200,7 +199,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
@@ -227,7 +226,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
@@ -252,7 +251,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
@@ -265,7 +264,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
&mut base64,
|
||||
);
|
||||
assert!(ok);
|
||||
@@ -298,7 +297,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
&mut preferencrypt,
|
||||
&mut base64,
|
||||
);
|
||||
@@ -354,16 +353,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 = 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;
|
||||
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();
|
||||
buf_1 = strdup(S_EM_SETUPFILE);
|
||||
assert!(dc_split_armored_data(
|
||||
buf_1,
|
||||
&mut headerline_0,
|
||||
&mut setupcodebegin_0,
|
||||
&mut preferencrypt_0,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
));
|
||||
assert!(!headerline_0.is_null());
|
||||
assert_eq!(
|
||||
@@ -389,7 +388,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
&mut headerline_0,
|
||||
&mut setupcodebegin_0,
|
||||
&mut preferencrypt_0,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
));
|
||||
assert!(!headerline_0.is_null());
|
||||
assert_eq!(
|
||||
@@ -417,16 +416,16 @@ unsafe fn stress_functions(context: &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 = 0 as *const libc::c_char;
|
||||
// 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,
|
||||
// 0 as *mut *const libc::c_char,
|
||||
// 0 as *mut *const libc::c_char,
|
||||
// 0 as *mut *const libc::c_char,
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ptr::null_mut(),
|
||||
// ));
|
||||
// assert!(!headerline_2.is_null());
|
||||
// assert_eq!(
|
||||
@@ -653,37 +652,6 @@ unsafe fn create_test_context() -> TestContext {
|
||||
TestContext { ctx: ctx, dir: dir }
|
||||
}
|
||||
|
||||
#[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() };
|
||||
|
||||
Reference in New Issue
Block a user