Compare commits

..

1 Commits

Author SHA1 Message Date
holger krekel
d13427cc84 fix bug that lead to all liveconfig tests failing 2019-08-13 22:03:24 +02:00
69 changed files with 11433 additions and 11340 deletions

View File

@@ -13,7 +13,7 @@ restore-workspace: &restore-workspace
restore-cache: &restore-cache
restore_cache:
keys:
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- repo-source-{{ .Branch }}-{{ .Revision }}
commands:
@@ -53,11 +53,10 @@ jobs:
command: cargo generate-lockfile
- restore_cache:
keys:
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
- run: rustup install $(cat rust-toolchain)
- run: rustup default $(cat rust-toolchain)
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
- run: cargo update
- run: cargo fetch
- run: rustc +stable --version
@@ -68,7 +67,7 @@ jobs:
paths:
- crate
- save_cache:
key: cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
key: cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
paths:
- "~/.cargo"
- "~/.rustup"
@@ -161,15 +160,6 @@ jobs:
- run: ls -laR workspace
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
clippy:
executor: default
steps:
- *restore-workspace
- *restore-cache
- run:
name: Run cargo clippy
command: cargo clippy --all
workflows:
version: 2.1
@@ -185,10 +175,6 @@ workflows:
requires:
- cargo_fetch
- clippy:
requires:
- cargo_fetch
# Linux Desktop 64bit
- test_x86_64-unknown-linux-gnu:
requires:

678
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "1.0.0-alpha.4"
version = "1.0.0-alpha.3"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
license = "MPL"
@@ -16,7 +16,6 @@ pgp = { version = "0.2", default-features = false }
hex = "0.3.2"
sha2 = "0.8.0"
rand = "0.6.5"
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
smallvec = "0.6.9"
reqwest = "0.9.15"
num-derive = "0.2.5"
@@ -38,6 +37,7 @@ rustyline = "4.1.0"
lazy_static = "1.3.0"
regex = "1.1.6"
rusqlite = { version = "0.20", features = ["bundled"] }
addr = "0.2.0"
r2d2_sqlite = "0.12.0"
r2d2 = "0.8.5"
strum = "0.15.0"
@@ -46,9 +46,6 @@ thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
itertools = "0.8.0"
image-meta = "0.1.0"
quick-xml = "0.15.0"
escaper = "0.1.0"
[dev-dependencies]
tempfile = "3.0"

View File

@@ -1,6 +0,0 @@
[dependencies.std]
features = ["panic-unwind"]
# if using `cargo test`
[dependencies.test]
stage = 1

View File

@@ -39,7 +39,7 @@ if [ -n "$TESTS" ]; then
# run tox
# XXX we don't run liveconfig tests because they hang sometimes
# see https://github.com/deltachat/deltachat-core-rust/issues/331
# unset DCC_PY_LIVECONFIG
unset DCC_PY_LIVECONFIG
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
popd

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "1.0.0-alpha.4"
version = "1.0.0-alpha.3"
description = "Deltachat FFI"
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
edition = "2018"
@@ -19,10 +19,10 @@ deltachat = { path = "../", default-features = false }
libc = "0.2"
human-panic = "1.0.1"
num-traits = "0.2.6"
rental = "0.5.4"
[features]
default = ["vendored", "nightly", "ringbuf"]
vendored = ["deltachat/vendored"]
nightly = ["deltachat/nightly"]
ringbuf = ["deltachat/ringbuf"]

View File

@@ -372,10 +372,14 @@ void dc_delete_all_locations (dc_context_t*);
*/
void dc_array_unref (dc_array_t*);
void dc_array_add_uint (dc_array_t*, uintptr_t);
void dc_array_add_id (dc_array_t*, uint32_t);
void dc_array_add_ptr (dc_array_t*, void*);
size_t dc_array_get_cnt (const dc_array_t*);
uintptr_t dc_array_get_uint (const dc_array_t*, size_t index);
uint32_t dc_array_get_id (const dc_array_t*, size_t index);
void* dc_array_get_ptr (const dc_array_t*, size_t index);
double dc_array_get_latitude (const dc_array_t*, size_t index);
double dc_array_get_longitude (const dc_array_t*, size_t index);
double dc_array_get_accuracy (const dc_array_t*, size_t index);
@@ -387,7 +391,7 @@ char* dc_array_get_marker (const dc_array_t*, size_t index);
int dc_array_is_independent (const dc_array_t*, size_t index);
int dc_array_search_id (const dc_array_t*, uint32_t needle, size_t* indx);
const uint32_t* dc_array_get_raw (const dc_array_t*);
const uintptr_t* dc_array_get_raw (const dc_array_t*);
/**
@@ -505,6 +509,7 @@ int dc_chat_is_sending_locations (const dc_chat_t*);
dc_msg_t* dc_msg_new (dc_context_t*, int viewtype);
void dc_msg_unref (dc_msg_t*);
void dc_msg_empty (dc_msg_t*);
uint32_t dc_msg_get_id (const dc_msg_t*);
uint32_t dc_msg_get_from_id (const dc_msg_t*);
uint32_t dc_msg_get_chat_id (const dc_msg_t*);
@@ -593,6 +598,8 @@ int dc_contact_is_verified (dc_contact_t*);
#define DC_TEXT1_SELF 3
dc_lot_t* dc_lot_new ();
void dc_lot_empty (dc_lot_t*);
void dc_lot_unref (dc_lot_t*);
char* dc_lot_get_text1 (const dc_lot_t*);
char* dc_lot_get_text2 (const dc_lot_t*);

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@ pub fn to_sql_derive(input: TokenStream) -> TokenStream {
let num = *self as i64;
let value = rusqlite::types::Value::Integer(num);
let output = rusqlite::types::ToSqlOutput::Owned(value);
std::result::Result::Ok(output)
}
}

View File

@@ -1,23 +1,23 @@
use std::ffi::CString;
use std::str::FromStr;
use deltachat::chat::{self, Chat};
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::configure::*;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_array::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_imex::*;
use deltachat::dc_job::*;
use deltachat::dc_location::*;
use deltachat::dc_lot::*;
use deltachat::dc_msg::*;
use deltachat::dc_qr::*;
use deltachat::dc_receive_imf::*;
use deltachat::dc_tools::*;
use deltachat::error::Error;
use deltachat::job::*;
use deltachat::lot::LotState;
use deltachat::message::*;
use deltachat::peerstate::*;
use deltachat::qr::*;
use deltachat::sql;
use deltachat::types::*;
use deltachat::x::*;
@@ -151,7 +151,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
}
if ok_to_continue {
let ok_to_continue2;
suffix = dc_get_filesuffix_lc(as_str(real_spec));
suffix = dc_get_filesuffix_lc(real_spec);
if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
{
if 0 != dc_poke_eml_file(context, real_spec) {
@@ -211,16 +211,16 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
success
}
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t) {
let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
let contact_name = contact.get_name();
let contact_id = contact.get_id();
let statestr = match dc_msg_get_state(msg) {
MessageState::OutPending => " o",
MessageState::OutDelivered => "",
MessageState::OutMdnRcvd => " √√",
MessageState::OutFailed => " !!",
DC_STATE_OUT_PENDING => " o",
DC_STATE_OUT_DELIVERED => "",
DC_STATE_OUT_MDN_RCVD => " √√",
DC_STATE_OUT_FAILED => " !!",
_ => "",
};
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
@@ -243,9 +243,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
if dc_msg_is_starred(msg) { "" } else { "" },
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
""
} else if dc_msg_get_state(msg) == MessageState::InSeen {
} else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN {
"[SEEN]"
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
"[NOTICED]"
} else {
"[FRESH]"
@@ -261,9 +261,11 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
free(msgtext as *mut libc::c_void);
}
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error> {
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
let cnt = dc_array_get_cnt(msglist) as usize;
let mut lines_out = 0;
for &msg_id in msglist {
for i in 0..cnt {
let msg_id = dc_array_get_id(msglist, i as size_t);
if msg_id == 9 as libc::c_uint {
info!(
context,
@@ -272,7 +274,7 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
);
lines_out += 1
} else if msg_id > 0 {
} else if msg_id > 0 as libc::c_uint {
if lines_out == 0 {
info!(
context, 0,
@@ -280,8 +282,9 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
);
lines_out += 1
}
let msg = dc_get_msg(context, msg_id)?;
log_msg(context, "Msg", &msg);
let msg = dc_get_msg(context, msg_id);
log_msg(context, "Msg", msg);
dc_msg_unref(msg);
}
}
if lines_out > 0 {
@@ -290,7 +293,6 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
0, "--------------------------------------------------------------------------------"
);
}
Ok(())
}
unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
@@ -347,16 +349,22 @@ pub unsafe fn dc_cmdline_skip_auth() {
S_IS_AUTH = 1;
}
fn chat_prefix(chat: &Chat) -> &'static str {
chat.typ.into()
unsafe fn chat_prefix(chat: *const Chat) -> &'static str {
if (*chat).type_0 == 120 {
"Group"
} else if (*chat).type_0 == 130 {
"VerifiedGroup"
} else {
"Single"
}
}
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
let mut sel_chat = if chat_id > 0 {
Chat::load_from_db(context, chat_id).ok()
dc_get_chat(context, chat_id)
} else {
None
std::ptr::null_mut()
};
let mut args = line.splitn(3, ' ');
@@ -396,6 +404,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
_ => println!(
"==========================Database commands==\n\
info\n\
open <file to open or create>\n\
close\n\
set <configuration-key> [<value>]\n\
get <configuration-key>\n\
oauth2\n\
@@ -472,6 +482,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("Already authorized.");
}
}
"open" => {
ensure!(!arg1.is_empty(), "Argument <file> missing");
dc_close(context);
ensure!(dc_open(context, arg1, None), "Open failed");
}
"close" => {
dc_close(context);
}
"initiate-key-transfer" => {
let setup_code = dc_initiate_key_transfer(context);
if !setup_code.is_null() {
@@ -487,9 +505,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"get-setupcodebegin" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let msg_id: u32 = arg1.parse()?;
let msg = dc_get_msg(context, msg_id)?;
if dc_msg_is_setupmessage(&msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(&msg);
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
if dc_msg_is_setupmessage(msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
println!(
"The setup code for setup message Msg#{} starts with: {}",
msg_id,
@@ -499,6 +517,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
} else {
bail!("Msg#{} is no setup message.", msg_id,);
}
dc_msg_unref(msg);
}
"continue-key-transfer" => {
ensure!(
@@ -571,7 +590,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("{}", to_string(dc_get_info(context)));
}
"maybenetwork" => {
maybe_network(context);
dc_maybe_network(context);
}
"housekeeping" => {
sql::housekeeping(context);
@@ -593,49 +612,55 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
for i in (0..cnt).rev() {
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
let temp_subtitle = chat.get_subtitle();
let temp_name = chat.get_name();
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
let temp_subtitle = dc_chat_get_subtitle(chat);
let temp_name = dc_chat_get_name(chat);
info!(
context,
0,
"{}#{}: {} [{}] [{} fresh]",
chat_prefix(&chat),
chat.get_id(),
temp_name,
temp_subtitle,
chat::get_fresh_msg_cnt(context, chat.get_id()),
chat_prefix(chat),
dc_chat_get_id(chat) as libc::c_int,
as_str(temp_name),
as_str(temp_subtitle),
dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int,
);
let lot = chatlist.get_summary(i, Some(&chat));
let statestr = if chat.is_archived() {
free(temp_subtitle as *mut libc::c_void);
free(temp_name as *mut libc::c_void);
let lot = chatlist.get_summary(i, chat);
let statestr = if 0 != dc_chat_get_archived(chat) {
" [Archived]"
} else {
match lot.get_state() {
LotState::MsgOutPending => " o",
LotState::MsgOutDelivered => "",
LotState::MsgOutMdnRcvd => " √√",
LotState::MsgOutFailed => " !!",
match dc_lot_get_state(lot) {
20 => " o",
26 => "",
28 => " √√",
24 => " !!",
_ => "",
}
};
let timestr = dc_timestamp_to_str(lot.get_timestamp());
let text1 = lot.get_text1();
let text2 = lot.get_text2();
let timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot));
let text1 = dc_lot_get_text1(lot);
let text2 = dc_lot_get_text2(lot);
info!(
context,
0,
"{}{}{}{} [{}]{}",
text1.unwrap_or(""),
if text1.is_some() { ": " } else { "" },
text2.unwrap_or(""),
to_string(text1),
if !text1.is_null() { ": " } else { "" },
to_string(text2),
statestr,
&timestr,
if chat.is_sending_locations() {
if 0 != dc_chat_is_sending_locations(chat) {
"📍"
} else {
""
},
);
free(text1 as *mut libc::c_void);
free(text2 as *mut libc::c_void);
dc_lot_unref(lot);
dc_chat_unref(chat);
info!(
context, 0,
"================================================================================"
@@ -648,82 +673,109 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
println!("{} chats", cnt);
}
"chat" => {
if sel_chat.is_none() && arg1.is_empty() {
if sel_chat.is_null() && arg1.is_empty() {
bail!("Argument [chat-id] is missing.");
}
if !sel_chat.is_null() && !arg1.is_empty() {
dc_chat_unref(sel_chat);
}
if !arg1.is_empty() {
let chat_id = arg1.parse()?;
println!("Selecting chat #{}", chat_id);
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
sel_chat = dc_get_chat(context, chat_id);
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
}
ensure!(sel_chat.is_some(), "Failed to select chat");
let sel_chat = sel_chat.as_ref().unwrap();
ensure!(!sel_chat.is_null(), "Failed to select chat");
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
let temp2 = sel_chat.get_subtitle();
let temp_name = sel_chat.get_name();
let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0);
let temp2 = dc_chat_get_subtitle(sel_chat);
let temp_name = dc_chat_get_name(sel_chat);
info!(
context,
0,
"{}#{}: {} [{}]{}",
chat_prefix(sel_chat),
sel_chat.get_id(),
temp_name,
temp2,
if sel_chat.is_sending_locations() {
dc_chat_get_id(sel_chat),
as_str(temp_name),
as_str(temp2),
if 0 != dc_chat_is_sending_locations(sel_chat) {
"📍"
} else {
""
},
);
log_msglist(context, &msglist)?;
if let Ok(draft) = chat::get_draft(context, sel_chat.get_id()) {
log_msg(context, "Draft", &draft);
free(temp_name as *mut libc::c_void);
free(temp2 as *mut libc::c_void);
if !msglist.is_null() {
log_msglist(context, msglist);
dc_array_unref(msglist);
}
let draft = dc_get_draft(context, dc_chat_get_id(sel_chat));
if !draft.is_null() {
log_msg(context, "Draft", draft);
dc_msg_unref(draft);
}
println!(
"{} messages.",
chat::get_msg_cnt(context, sel_chat.get_id())
dc_get_msg_cnt(context, dc_chat_get_id(sel_chat))
);
chat::marknoticed_chat(context, sel_chat.get_id())?;
dc_marknoticed_chat(context, dc_chat_get_id(sel_chat));
}
"createchat" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id: libc::c_int = arg1.parse()?;
let chat_id = chat::create_by_contact_id(context, contact_id as uint32_t)?;
println!("Single#{} created successfully.", chat_id,);
let chat_id: libc::c_int =
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
if chat_id != 0 {
println!("Single#{} created successfully.", chat_id,);
} else {
bail!("Failed to create chat");
}
}
"createchatbymsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
let msg_id: u32 = arg1.parse()?;
let chat_id = chat::create_by_msg_id(context, msg_id)?;
let chat = Chat::load_from_db(context, chat_id)?;
println!("{}#{} created successfully.", chat_prefix(&chat), chat_id,);
let msg_id_0: libc::c_int = arg1.parse()?;
let chat_id_0: libc::c_int =
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
if chat_id_0 != 0 {
let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t);
println!(
"{}#{} created successfully.",
chat_prefix(chat_0),
chat_id_0,
);
dc_chat_unref(chat_0);
} else {
bail!("");
}
}
"creategroup" => {
ensure!(!arg1.is_empty(), "Argument <name> missing.");
let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1)?;
println!("Group#{} created successfully.", chat_id);
let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int;
if chat_id_1 != 0 {
println!("Group#{} created successfully.", chat_id_1,);
} else {
bail!("Failed to create group");
}
}
"createverified" => {
ensure!(!arg1.is_empty(), "Argument <name> missing.");
let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1)?;
println!("VerifiedGroup#{} created successfully.", chat_id);
let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int;
if chat_id_2 != 0 {
println!("VerifiedGroup#{} created successfully.", chat_id_2,);
} else {
bail!("Failed to create verified group");
}
}
"addmember" => {
ensure!(sel_chat.is_some(), "No chat selected");
ensure!(!sel_chat.is_null(), "No chat selected");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_0: libc::c_int = arg1.parse()?;
if 0 != chat::add_contact_to_chat(
if 0 != dc_add_contact_to_chat(
context,
sel_chat.as_ref().unwrap().get_id(),
dc_chat_get_id(sel_chat),
contact_id_0 as uint32_t,
) {
println!("Contact added to chat.");
@@ -732,89 +784,99 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
}
"removemember" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_1: libc::c_int = arg1.parse()?;
chat::remove_contact_from_chat(
if 0 != dc_remove_contact_from_chat(
context,
sel_chat.as_ref().unwrap().get_id(),
dc_chat_get_id(sel_chat),
contact_id_1 as uint32_t,
)?;
println!("Contact added to chat.");
) {
println!("Contact added to chat.");
} else {
bail!("Cannot remove member from chat.");
}
}
"groupname" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <name> missing.");
chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
println!("Chat name set");
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) {
println!("Chat name set");
} else {
bail!("Failed to set chat name");
}
}
"groupimage" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <image> missing.");
chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
println!("Chat image set");
if 0 != dc_set_chat_profile_image(
context,
dc_chat_get_id(sel_chat),
if !arg1.is_empty() {
arg1_c
} else {
std::ptr::null_mut()
},
) {
println!("Chat image set");
} else {
bail!("Failed to set chat image");
}
}
"chatinfo" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat));
info!(context, 0, "Memberlist:");
log_contactlist(context, &contacts);
println!(
"{} contacts\nLocation streaming: {}",
contacts.len(),
dc_is_sending_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id()),
dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)),
);
}
"getlocations" => {
ensure!(sel_chat.is_some(), "No chat selected.");
let contact_id = arg1.parse().unwrap_or_default();
let locations = dc_get_locations(
context,
sel_chat.as_ref().unwrap().get_id(),
contact_id,
0,
0,
);
let default_marker = "-".to_string();
for location in &locations {
let marker = location.marker.as_ref().unwrap_or(&default_marker);
let loc = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0);
let mut j = 0;
while j < dc_array_get_cnt(loc) {
let timestr_0 = dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t));
let marker = dc_array_get_marker(loc, j as size_t);
info!(
context,
0,
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
location.location_id,
dc_timestamp_to_str(location.timestamp),
location.latitude,
location.longitude,
location.accuracy,
location.chat_id,
location.contact_id,
location.msg_id,
marker
dc_array_get_id(loc, j as size_t),
&timestr_0,
dc_array_get_latitude(loc, j as size_t),
dc_array_get_longitude(loc, j as size_t),
dc_array_get_accuracy(loc, j as size_t),
dc_array_get_chat_id(loc, j as size_t),
dc_array_get_contact_id(loc, j as size_t),
dc_array_get_msg_id(loc, j as size_t),
if !marker.is_null() {
as_str(marker)
} else {
"-"
},
);
free(marker as *mut libc::c_void);
j += 1
}
if locations.is_empty() {
if dc_array_get_cnt(loc) == 0 {
info!(context, 0, "No locations.");
}
dc_array_unref(loc);
}
"sendlocations" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "No timeout given.");
let seconds = arg1.parse()?;
dc_send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds);
println!(
"Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.",
sel_chat.as_ref().unwrap().get_id(),
seconds
);
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
}
"setlocation" => {
ensure!(
@@ -835,22 +897,30 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_delete_all_locations(context);
}
"send" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "No message text given.");
let msg = format!("{} {}", arg1, arg2);
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), msg)?;
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
println!("Message sent.");
} else {
bail!("Sending failed.");
}
}
"sendempty" => {
ensure!(sel_chat.is_some(), "No chat selected.");
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), "".into())?;
ensure!(!sel_chat.is_null(), "No chat selected.");
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) {
println!("Message sent.");
} else {
bail!("Sending failed.");
}
}
"sendimage" | "sendfile" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
let mut msg = dc_msg_new(
let msg_0 = dc_msg_new(
context,
if arg0 == "sendimage" {
Viewtype::Image
@@ -858,53 +928,56 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
Viewtype::File
},
);
dc_msg_set_file(&mut msg, arg1_c, 0 as *const libc::c_char);
dc_msg_set_text(&mut msg, arg2_c);
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), &mut msg)?;
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
dc_msg_set_text(msg_0, arg2_c);
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
dc_msg_unref(msg_0);
}
"listmsgs" => {
ensure!(!arg1.is_empty(), "Argument <query> missing.");
let chat = if let Some(ref sel_chat) = sel_chat {
sel_chat.get_id()
let chat = if !sel_chat.is_null() {
dc_chat_get_id(sel_chat)
} else {
0 as libc::c_uint
};
let msglist = dc_search_msgs(context, chat, arg1_c);
let msglist_0 = dc_search_msgs(context, chat, arg1_c);
log_msglist(context, &msglist)?;
println!("{} messages.", msglist.len());
if !msglist_0.is_null() {
log_msglist(context, msglist_0);
println!("{} messages.", dc_array_get_cnt(msglist_0));
dc_array_unref(msglist_0);
}
}
"draft" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
if !arg1.is_empty() {
let mut draft = dc_msg_new(context, Viewtype::Text);
dc_msg_set_text(&mut draft, arg1_c);
chat::set_draft(
context,
sel_chat.as_ref().unwrap().get_id(),
Some(&mut draft),
);
let draft_0 = dc_msg_new(context, Viewtype::Text);
dc_msg_set_text(draft_0, arg1_c);
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
dc_msg_unref(draft_0);
println!("Draft saved.");
} else {
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), None);
dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t);
println!("Draft deleted.");
}
}
"listmedia" => {
ensure!(sel_chat.is_some(), "No chat selected.");
ensure!(!sel_chat.is_null(), "No chat selected.");
let images = chat::get_chat_media(
let images = dc_get_chat_media(
context,
sel_chat.as_ref().unwrap().get_id(),
dc_chat_get_id(sel_chat),
Viewtype::Image,
Viewtype::Gif,
Viewtype::Video,
);
println!("{} images or videos: ", images.len());
for (i, data) in images.iter().enumerate() {
let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int;
println!("{} images or videos: ", icnt);
for i in 0..icnt {
let data = dc_array_get_id(images, i as size_t);
if 0 == i {
print!("Msg#{}", data);
} else {
@@ -912,20 +985,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
}
print!("\n");
dc_array_unref(images);
}
"archive" | "unarchive" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
chat::archive(
context,
chat_id,
if arg0 == "archive" { true } else { false },
)?;
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
}
"delchat" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
chat::delete(context, chat_id)?;
dc_delete_chat(context, chat_id);
}
"msginfo" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
@@ -935,9 +1005,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"listfresh" => {
let msglist = dc_get_fresh_msgs(context);
ensure!(!msglist.is_null(), "Failed to retrieve messages");
log_msglist(context, &msglist)?;
print!("{} fresh messages.", msglist.len());
log_msglist(context, msglist);
print!("{} fresh messages.", dc_array_get_cnt(msglist));
dc_array_unref(msglist);
}
"forward" => {
ensure!(
@@ -948,7 +1020,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let mut msg_ids = [0; 1];
let chat_id = arg2.parse()?;
msg_ids[0] = arg1.parse()?;
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
@@ -1018,8 +1090,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if 0 != i {
res += ", ";
}
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
res += &format!("{}#{}", chat_prefix(&chat), chat.get_id());
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
dc_chat_unref(chat);
}
}
@@ -1031,14 +1104,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"checkqr" => {
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
let res = check_qr(context, arg1);
let res = dc_check_qr(context, arg1_c);
println!(
"state={}, id={}, text1={:?}, text2={:?}",
res.get_state(),
res.get_id(),
res.get_text1(),
res.get_text2()
"state={}, id={}, text1={}, text2={}",
(*res).state as libc::c_int,
(*res).id,
to_string((*res).text1),
to_string((*res).text2)
);
dc_lot_unref(res);
}
"event" => {
ensure!(!arg1.is_empty(), "Argument <id> missing.");
@@ -1052,10 +1126,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"fileinfo" => {
ensure!(!arg1.is_empty(), "Argument <file> missing.");
let mut buf = 0 as *mut libc::c_uchar;
let mut buf_bytes = 0;
let mut w = 0;
let mut h = 0;
if let Some(buf) = dc_read_file_safe(context, &arg1) {
let (width, height) = dc_get_filemeta(&buf)?;
println!("width={}, height={}", width, height);
if 0 != dc_read_file(
context,
arg1_c,
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
&mut buf_bytes,
) {
dc_get_filemeta(buf as *const libc::c_void, buf_bytes, &mut w, &mut h);
println!("width={}, height={}", w, h,);
free(buf as *mut libc::c_void);
} else {
bail!("Command failed.");
}
@@ -1064,6 +1148,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
}
if !sel_chat.is_null() {
dc_chat_unref(sel_chat);
}
free(arg1_c as *mut _);
free(arg2_c as *mut _);

View File

@@ -3,6 +3,7 @@
//!
//! Usage: cargo run --example repl --release -- <databasefile>
//! All further options can be set using the set-command (type ? for help).
#![feature(ptr_cast)]
#[macro_use]
extern crate deltachat;
@@ -14,17 +15,16 @@ extern crate lazy_static;
extern crate rusqlite;
use std::borrow::Cow::{self, Borrowed, Owned};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use deltachat::config;
use deltachat::configure::*;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_configure::*;
use deltachat::dc_job::*;
use deltachat::dc_securejoin::*;
use deltachat::dc_tools::*;
use deltachat::job::*;
use deltachat::oauth2::*;
use deltachat::types::*;
use deltachat::x::*;
@@ -42,7 +42,7 @@ use self::cmdline::*;
// Event Handler
fn receive_event(
unsafe extern "C" fn receive_event(
_context: &Context,
event: Event,
data1: uintptr_t,
@@ -173,11 +173,13 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_imap = std::thread::spawn(move || loop {
while_running!({
perform_imap_jobs(&ctx.read().unwrap());
perform_imap_fetch(&ctx.read().unwrap());
unsafe {
dc_perform_imap_jobs(&ctx.read().unwrap());
dc_perform_imap_fetch(&ctx.read().unwrap());
}
while_running!({
let context = ctx.read().unwrap();
perform_imap_idle(&context);
dc_perform_imap_idle(&context);
});
});
});
@@ -185,9 +187,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_mvbox = std::thread::spawn(move || loop {
while_running!({
perform_mvbox_fetch(&ctx.read().unwrap());
unsafe { dc_perform_mvbox_fetch(&ctx.read().unwrap()) };
while_running!({
perform_mvbox_idle(&ctx.read().unwrap());
unsafe { dc_perform_mvbox_idle(&ctx.read().unwrap()) };
});
});
});
@@ -195,9 +197,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c.clone();
let handle_sentbox = std::thread::spawn(move || loop {
while_running!({
perform_sentbox_fetch(&ctx.read().unwrap());
unsafe { dc_perform_sentbox_fetch(&ctx.read().unwrap()) };
while_running!({
perform_sentbox_idle(&ctx.read().unwrap());
unsafe { dc_perform_sentbox_idle(&ctx.read().unwrap()) };
});
});
});
@@ -205,9 +207,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
let ctx = c;
let handle_smtp = std::thread::spawn(move || loop {
while_running!({
perform_smtp_jobs(&ctx.read().unwrap());
unsafe { dc_perform_smtp_jobs(&ctx.read().unwrap()) };
while_running!({
perform_smtp_idle(&ctx.read().unwrap());
unsafe { dc_perform_smtp_idle(&ctx.read().unwrap()) };
});
});
});
@@ -225,10 +227,12 @@ fn stop_threads(context: &Context) {
println!("Stopping threads");
IS_RUNNING.store(false, Ordering::Relaxed);
interrupt_imap_idle(context);
interrupt_mvbox_idle(context);
interrupt_sentbox_idle(context);
interrupt_smtp_idle(context);
unsafe {
dc_interrupt_imap_idle(context);
dc_interrupt_mvbox_idle(context);
dc_interrupt_sentbox_idle(context);
dc_interrupt_smtp_idle(context);
}
handle.handle_imap.take().unwrap().join().unwrap();
handle.handle_mvbox.take().unwrap().join().unwrap();
@@ -385,16 +389,21 @@ 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()),
);
unsafe { dc_cmdline_skip_auth() };
if args.len() != 2 {
if args.len() == 2 {
if unsafe { !dc_open(&mut context, &args[1], None) } {
println!("Error: Cannot open {}.", args[0],);
}
} else if args.len() != 1 {
println!("Error: Bad arguments, expected [db-name].");
return Err(format_err!("No db-name specified"));
}
let context = Context::new(
Box::new(receive_event),
"CLI".into(),
Path::new(&args[1]).to_path_buf(),
)?;
println!("Delta Chat Core is awaiting your commands.");
@@ -479,19 +488,19 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
if HANDLE.clone().lock().unwrap().is_some() {
println!("smtp-jobs are already running in a thread.",);
} else {
perform_smtp_jobs(&ctx.read().unwrap());
dc_perform_smtp_jobs(&ctx.read().unwrap());
}
}
"imap-jobs" => {
if HANDLE.clone().lock().unwrap().is_some() {
println!("imap-jobs are already running in a thread.");
} else {
perform_imap_jobs(&ctx.read().unwrap());
dc_perform_imap_jobs(&ctx.read().unwrap());
}
}
"configure" => {
start_threads(ctx.clone());
configure(&ctx.read().unwrap());
dc_configure(&ctx.read().unwrap());
}
"oauth2" => {
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {

View File

@@ -5,19 +5,22 @@ use std::sync::{Arc, RwLock};
use std::{thread, time};
use tempfile::tempdir;
use deltachat::chat;
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::configure::*;
use deltachat::constants::Event;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::job::{
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_job::{
dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle,
dc_perform_smtp_jobs,
};
use deltachat::dc_lot::*;
fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
println!("[{:?}]", event);
match event {
Event::CONFIGURE_PROGRESS => {
println!(" progress: {}", data1);
@@ -38,11 +41,7 @@ fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
fn main() {
unsafe {
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("creating database {:?}", dbfile);
let ctx =
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let running = Arc::new(RwLock::new(true));
let info = dc_get_info(&ctx);
let info_s = CStr::from_ptr(info);
@@ -54,12 +53,12 @@ fn main() {
let r1 = running.clone();
let t1 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_imap_jobs(&ctx1);
dc_perform_imap_jobs(&ctx1);
if *r1.read().unwrap() {
perform_imap_fetch(&ctx1);
dc_perform_imap_fetch(&ctx1);
if *r1.read().unwrap() {
perform_imap_idle(&ctx1);
dc_perform_imap_idle(&ctx1);
}
}
}
@@ -69,13 +68,20 @@ fn main() {
let r1 = running.clone();
let t2 = thread::spawn(move || {
while *r1.read().unwrap() {
perform_smtp_jobs(&ctx1);
dc_perform_smtp_jobs(&ctx1);
if *r1.read().unwrap() {
perform_smtp_idle(&ctx1);
dc_perform_smtp_idle(&ctx1);
}
}
});
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
println!("opening database {:?}", dbfile);
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
println!("configuring");
let args = std::env::args().collect::<Vec<String>>();
assert_eq!(args.len(), 2, "missing password");
@@ -83,24 +89,36 @@ fn main() {
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
.unwrap();
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
configure(&ctx);
dc_configure(&ctx);
thread::sleep(duration);
println!("sending a message");
let contact_id =
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id);
dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into());
println!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
for i in 0..chats.len() {
let summary = chats.get_summary(0, None);
let text1 = summary.get_text1();
let text2 = summary.get_text2();
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
let summary = chats.get_summary(0, std::ptr::null_mut());
let text1 = dc_lot_get_text1(summary);
let text2 = dc_lot_get_text2(summary);
let text1_s = if !text1.is_null() {
Some(CStr::from_ptr(text1))
} else {
None
};
let text2_s = if !text2.is_null() {
Some(CStr::from_ptr(text2))
} else {
None
};
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
dc_lot_unref(summary);
}
thread::sleep(duration);
@@ -118,8 +136,8 @@ fn main() {
println!("stopping threads");
*running.clone().write().unwrap() = false;
deltachat::job::interrupt_imap_idle(&ctx);
deltachat::job::interrupt_smtp_idle(&ctx);
deltachat::dc_job::dc_interrupt_imap_idle(&ctx);
deltachat::dc_job::dc_interrupt_smtp_idle(&ctx);
println!("joining");
t1.join().unwrap();

View File

@@ -37,3 +37,16 @@ Message
.. autoclass:: deltachat.message.Message
:members:
MessageType
------------
.. autoclass:: deltachat.message.MessageType
:members:
MessageState
------------
.. autoclass:: deltachat.message.MessageState
:members:

View File

@@ -8,7 +8,8 @@ Playing around on the commandline
----------------------------------
Once you have :doc:`installed deltachat bindings <install>`
you can start playing from the python interpreter commandline.
you can start playing from the python interpreter commandline::
For example you can type ``python`` and then::
# instantiate and configure deltachat account
@@ -22,7 +23,7 @@ For example you can type ``python`` and then::
# create a contact and send a message
contact = ac.create_contact("someother@email.address")
chat = ac.create_chat_by_contact(contact)
chat.send_text("hi from the python interpreter command line")
chat.send_text_message("hi from the python interpreter command line")
Checkout our :doc:`api` for the various high-level things you can do
to send/receive messages, create contacts and chats.

View File

@@ -34,14 +34,13 @@ def py_dc_callback(ctx, evt, data1, data2):
if data1 and event_sig_types & 1:
data1 = ffi.string(ffi.cast('char*', data1)).decode("utf8")
if data2 and event_sig_types & 2:
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
try:
if isinstance(data2, bytes):
data2 = data2.decode("utf8")
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
except UnicodeDecodeError:
# XXX ignoring the decode error is not quite correct but for now
# XXX ignoring this error is not quite correct but for now
# i don't want to hunt down encoding problems in the c lib
pass
data2 = ffi.string(ffi.cast('char*', data2))
try:
ret = callback(ctx, evt_name, data1, data2)
if ret is None:

View File

@@ -176,7 +176,7 @@ class Account(object):
whose name or e-mail matches query.
:param only_verified: if true only return verified contacts.
:param with_self: if true the self-contact is also returned.
:returns: list of :class:`deltachat.chatting.Contact` objects.
:returns: list of :class:`deltachat.message.Message` objects.
"""
flags = 0
query = as_dc_charpointer(query)

View File

@@ -16,4 +16,4 @@ def iter_array(dc_array_t, constructor):
def from_dc_charpointer(obj):
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
return ffi.string(obj).decode("utf8")

View File

@@ -142,7 +142,7 @@ class Message(object):
import email.parser
mime_headers = lib.dc_get_mime_headers(self._dc_context, self.id)
if mime_headers:
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
s = ffi.string(mime_headers)
if isinstance(s, bytes):
s = s.decode("ascii")
return email.message_from_string(s)

View File

@@ -17,19 +17,11 @@ def test_callback_None2int():
clear_context_callback(ctx)
def test_dc_close_events(tmpdir):
ctx = ffi.gc(
capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL),
lib.dc_context_unref,
)
def test_dc_close_events():
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
evlog = EventLogger(ctx)
evlog.set_timeout(5)
set_context_callback(
ctx,
lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2),
)
p = tmpdir.join("hello.db")
lib.dc_open(ctx, p.strpath.encode("ascii"), ffi.NULL)
set_context_callback(ctx, lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2))
capi.lib.dc_close(ctx)
def find(info_string):

View File

@@ -1 +1 @@
nightly-2019-08-13
nightly-2019-07-10

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,11 @@
use crate::chat::*;
use crate::constants::*;
use crate::contact::*;
use crate::context::*;
use crate::dc_chat::*;
use crate::dc_lot::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::error::Result;
use crate::lot::Lot;
use crate::message::*;
use crate::stock::StockMessage;
/// An object representing a single chatlist in memory.
@@ -247,64 +248,57 @@ impl<'a> Chatlist<'a> {
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
// 0 if not applicable.
pub unsafe fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
pub unsafe fn get_summary(&self, index: usize, mut chat: *mut Chat<'a>) -> *mut dc_lot_t {
// The summary is created by the chat, not by the last message.
// This is because we may want to display drafts here or stuff as
// "is typing".
// Also, sth. as "No messages" would not work if the summary comes from a message.
let mut ret = Lot::new();
let mut ret = dc_lot_new();
if index >= self.ids.len() {
ret.text2 = Some("ErrBadChatlistIndex".to_string());
(*ret).text2 = "ErrBadChatlistIndex".strdup();
return ret;
}
let chat_loaded: Chat;
let chat = if let Some(chat) = chat {
chat
} else {
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
}
};
let lastmsg_id = self.ids[index].1;
let mut lastcontact = None;
let lastmsg = if 0 != lastmsg_id {
if let Ok(lastmsg) = dc_msg_load_from_db(self.context, lastmsg_id) {
if lastmsg.from_id != 1 as libc::c_uint
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
{
lastcontact = Contact::load_from_db(self.context, lastmsg.from_id).ok();
}
if chat.is_null() {
chat = dc_chat_new(self.context);
let chat_to_delete = chat;
if !dc_chat_load_from_db(chat, self.ids[index].0) {
(*ret).text2 = "ErrCannotReadChat".strdup();
dc_chat_unref(chat_to_delete);
Some(lastmsg)
} else {
None
return ret;
}
}
let lastmsg = if 0 != lastmsg_id {
let lastmsg = dc_msg_new_untyped(self.context);
dc_msg_load_from_db(lastmsg, self.context, lastmsg_id);
if (*lastmsg).from_id != 1 as libc::c_uint
&& ((*chat).type_0 == DC_CHAT_TYPE_GROUP
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP)
{
lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok();
}
lastmsg
} else {
None
std::ptr::null_mut()
};
if chat.id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
ret.text2 = None;
} else if lastmsg.is_none()
|| lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED as u32
{
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 {
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
} else {
ret.fill(
&mut lastmsg.unwrap(),
chat,
lastcontact.as_ref(),
self.context,
);
dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context);
}
dc_msg_unref(lastmsg);
ret
}
}

View File

@@ -3,9 +3,9 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
use crate::constants::DC_VERSION_STR;
use crate::context::Context;
use crate::dc_job::*;
use crate::dc_tools::*;
use crate::error::Error;
use crate::job::*;
use crate::stock::StockMessage;
/// The available configuration keys.
@@ -48,14 +48,11 @@ pub enum Config {
ConfiguredMailUser,
ConfiguredMailPw,
ConfiguredMailPort,
ConfiguredMailSecurity,
ConfiguredSendServer,
ConfiguredSendUser,
ConfiguredSendPw,
ConfiguredSendPort,
ConfiguredServerFlags,
ConfiguredSendSecurity,
ConfiguredE2EEEnabled,
Configured,
// Deprecated
#[strum(serialize = "sys.version")]
@@ -74,7 +71,7 @@ impl Context {
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string())
}
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
Config::SysConfigKeys => Some(get_config_keys_string()),
_ => self.sql.get_config(self, key),
@@ -102,17 +99,17 @@ impl Context {
}
Config::InboxWatch => {
let ret = self.sql.set_config(self, key, value);
interrupt_imap_idle(self);
unsafe { dc_interrupt_imap_idle(self) };
ret
}
Config::SentboxWatch => {
let ret = self.sql.set_config(self, key, value);
interrupt_sentbox_idle(self);
unsafe { dc_interrupt_sentbox_idle(self) };
ret
}
Config::MvboxWatch => {
let ret = self.sql.set_config(self, key, value);
interrupt_mvbox_idle(self);
unsafe { dc_interrupt_mvbox_idle(self) };
ret
}
Config::Selfstatus => {
@@ -123,7 +120,8 @@ impl Context {
value
};
self.sql.set_config(self, key, val)
let ret = self.sql.set_config(self, key, val);
ret
}
_ => self.sql.set_config(self, key, value),
}

View File

@@ -1,224 +0,0 @@
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_tools::*;
use crate::x::*;
use super::read_autoconf_file;
/* ******************************************************************************
* Thunderbird's Autoconfigure
******************************************************************************/
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
#[repr(C)]
struct moz_autoconfigure_t<'a> {
pub in_0: &'a dc_loginparam_t,
pub in_emaildomain: *mut libc::c_char,
pub in_emaillocalpart: *mut libc::c_char,
pub out: dc_loginparam_t,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_server: libc::c_int,
pub tag_config: libc::c_int,
}
pub unsafe fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &dc_loginparam_t,
) -> Option<dc_loginparam_t> {
let mut moz_ac = moz_autoconfigure_t {
in_0: param_in,
in_emaildomain: std::ptr::null_mut(),
in_emaillocalpart: std::ptr::null_mut(),
out: dc_loginparam_new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_server: 0,
tag_config: 0,
};
let url_c = url.strdup();
let xml_raw = read_autoconf_file(context, url_c);
free(url_c as *mut libc::c_void);
if xml_raw.is_null() {
return None;
}
moz_ac.in_emaillocalpart = param_in.addr.strdup();
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
if p.is_null() {
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
return None;
}
*p = 0 as libc::c_char;
moz_ac.in_emaildomain = dc_strdup(p.offset(1isize));
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
moz_autoconfigure_starttag_cb(e, &mut moz_ac, &reader)
}
Ok(quick_xml::events::Event::End(ref e)) => moz_autoconfigure_endtag_cb(e, &mut moz_ac),
Ok(quick_xml::events::Event::Text(ref e)) => {
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
}
Err(e) => {
error!(
context,
0,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
if moz_ac.out.mail_server.is_empty()
|| moz_ac.out.mail_port == 0
|| moz_ac.out.send_server.is_empty()
|| moz_ac.out.send_port == 0
{
let r = dc_loginparam_get_readable(&moz_ac.out);
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
return None;
}
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
Some(moz_ac.out)
}
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
event: &BytesText,
moz_ac: &mut moz_autoconfigure_t,
reader: &quick_xml::Reader<B>,
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
let addr = &moz_ac.in_0.addr;
let email_local = as_str(moz_ac.in_emaillocalpart);
let email_domain = as_str(moz_ac.in_emaildomain);
let val = val
.trim()
.replace("%EMAILADDRESS%", addr)
.replace("%EMAILLOCALPART%", email_local)
.replace("%EMAILDOMAIN%", email_domain);
if moz_ac.tag_server == 1 {
match moz_ac.tag_config {
10 => moz_ac.out.mail_server = val,
11 => moz_ac.out.mail_port = val.parse().unwrap_or_default(),
12 => moz_ac.out.mail_user = val,
13 => {
let val_lower = val.to_lowercase();
if val_lower == "ssl" {
moz_ac.out.server_flags |= 0x200
}
if val_lower == "starttls" {
moz_ac.out.server_flags |= 0x100
}
if val_lower == "plain" {
moz_ac.out.server_flags |= 0x400
}
}
_ => {}
}
} else if moz_ac.tag_server == 2 {
match moz_ac.tag_config {
10 => moz_ac.out.send_server = val,
11 => moz_ac.out.send_port = val.parse().unwrap_or_default(),
12 => moz_ac.out.send_user = val,
13 => {
let val_lower = val.to_lowercase();
if val_lower == "ssl" {
moz_ac.out.server_flags |= 0x20000
}
if val_lower == "starttls" {
moz_ac.out.server_flags |= 0x10000
}
if val_lower == "plain" {
moz_ac.out.server_flags |= 0x40000
}
}
_ => {}
}
}
}
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut moz_autoconfigure_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "incomingserver" {
moz_ac.tag_server = 0;
moz_ac.tag_config = 0;
moz_ac.out_imap_set = 1;
} else if tag == "outgoingserver" {
moz_ac.tag_server = 0;
moz_ac.tag_config = 0;
moz_ac.out_smtp_set = 1;
} else {
moz_ac.tag_config = 0;
}
}
fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
event: &BytesStart,
moz_ac: &mut moz_autoconfigure_t,
reader: &quick_xml::Reader<B>,
) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "incomingserver" {
moz_ac.tag_server = if let Some(typ) = event.attributes().find(|attr| {
attr.as_ref()
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "type")
.unwrap_or_default()
}) {
let typ = typ
.unwrap()
.unescape_and_decode_value(reader)
.unwrap_or_default()
.to_lowercase();
if typ == "imap" && moz_ac.out_imap_set == 0 {
1
} else {
0
}
} else {
0
};
moz_ac.tag_config = 0;
} else if tag == "outgoingserver" {
moz_ac.tag_server = if moz_ac.out_smtp_set == 0 { 2 } else { 0 };
moz_ac.tag_config = 0;
} else if tag == "hostname" {
moz_ac.tag_config = 10;
} else if tag == "port" {
moz_ac.tag_config = 11;
} else if tag == "sockettype" {
moz_ac.tag_config = 13;
} else if tag == "username" {
moz_ac.tag_config = 12;
}
}

View File

@@ -1,213 +0,0 @@
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_tools::*;
use crate::x::*;
use std::ptr;
use super::read_autoconf_file;
/* ******************************************************************************
* Outlook's Autodiscover
******************************************************************************/
#[repr(C)]
struct outlk_autodiscover_t<'a> {
pub in_0: &'a dc_loginparam_t,
pub out: dc_loginparam_t,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_config: libc::c_int,
pub config: [*mut libc::c_char; 6],
pub redirect: *mut libc::c_char,
}
pub unsafe fn outlk_autodiscover(
context: &Context,
url__: &str,
param_in: &dc_loginparam_t,
) -> Option<dc_loginparam_t> {
let mut xml_raw: *mut libc::c_char = ptr::null_mut();
let mut url = url__.strdup();
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: dc_loginparam_new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_config: 0,
config: [ptr::null_mut(); 6],
redirect: ptr::null_mut(),
};
let ok_to_continue;
let mut i = 0;
loop {
if !(i < 10) {
ok_to_continue = true;
break;
}
memset(
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
0,
::std::mem::size_of::<outlk_autodiscover_t>(),
);
xml_raw = read_autoconf_file(context, url);
if xml_raw.is_null() {
ok_to_continue = false;
break;
}
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
outlk_autodiscover_starttag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::End(ref e)) => {
outlk_autodiscover_endtag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::Text(ref e)) => {
outlk_autodiscover_text_cb(e, &mut outlk_ad, &reader)
}
Err(e) => {
error!(
context,
0,
"Configure xml: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
if !(!outlk_ad.config[5].is_null()
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
{
ok_to_continue = true;
break;
}
free(url as *mut libc::c_void);
url = dc_strdup(outlk_ad.config[5usize]);
outlk_clean_config(&mut outlk_ad);
free(xml_raw as *mut libc::c_void);
xml_raw = ptr::null_mut();
i += 1;
}
if ok_to_continue {
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = dc_loginparam_get_readable(&outlk_ad.out);
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
return None;
}
}
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
Some(outlk_ad.out)
}
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
for i in 0..6 {
free((*outlk_ad).config[i] as *mut libc::c_void);
(*outlk_ad).config[i] = 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
};
}

View File

@@ -1,13 +1,8 @@
//! Constants
#![allow(non_camel_case_types, dead_code)]
use lazy_static::lazy_static;
#![allow(non_camel_case_types)]
use deltachat_derive::*;
lazy_static! {
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
}
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
#[repr(u8)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
@@ -19,30 +14,20 @@ pub enum MoveState {
}
// some defaults
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
pub const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
pub const DC_INBOX_WATCH_DEFAULT: i32 = 1;
pub const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
pub const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
pub const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(u8)]
pub enum Blocked {
Not = 0,
Manually = 1,
Deaddrop = 2,
}
impl Default for Blocked {
fn default() -> Self {
Blocked::Not
}
}
pub const DC_CHAT_NOT_BLOCKED: i32 = 0;
pub const DC_CHAT_MANUALLY_BLOCKED: i32 = 1;
pub const DC_CHAT_DEADDROP_BLOCKED: i32 = 2;
pub const DC_IMAP_SEEN: u32 = 0x1;
const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
@@ -50,28 +35,47 @@ pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
const DC_GCM_ADDDAYMARKER: usize = 0x01;
pub const DC_GCM_ADDDAYMARKER: usize = 0x01;
const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_ADD_SELF: usize = 0x02;
/// param1 is a directory where the keys are written to
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
pub const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
/// param1 is a directory where the keys are searched in and read from
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
pub const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
/// param1 is a directory where the backup is written to
const DC_IMEX_EXPORT_BACKUP: usize = 11;
pub const DC_IMEX_EXPORT_BACKUP: usize = 11;
/// param1 is the file with the backup to import
const DC_IMEX_IMPORT_BACKUP: usize = 12;
pub const DC_IMEX_IMPORT_BACKUP: usize = 12;
/// id=contact
pub const DC_QR_ASK_VERIFYCONTACT: usize = 200;
/// text1=groupname
pub const DC_QR_ASK_VERIFYGROUP: usize = 202;
/// id=contact
pub const DC_QR_FPR_OK: usize = 210;
/// id=contact
pub const DC_QR_FPR_MISMATCH: usize = 220;
/// test1=formatted fingerprint
pub const DC_QR_FPR_WITHOUT_ADDR: usize = 230;
/// id=contact
pub const DC_QR_ADDR: usize = 320;
/// text1=text
pub const DC_QR_TEXT: usize = 330;
/// text1=URL
pub const DC_QR_URL: usize = 332;
/// text1=error string
pub const DC_QR_ERROR: usize = 400;
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
pub(crate) const DC_CHAT_ID_DEADDROP: usize = 1;
pub const DC_CHAT_ID_DEADDROP: usize = 1;
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
pub const DC_CHAT_ID_TRASH: usize = 3;
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
pub const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
/// virtual chat showing all messages flagged with msgs.starred=2
const DC_CHAT_ID_STARRED: usize = 5;
pub const DC_CHAT_ID_STARRED: usize = 5;
/// only an indicator in a chatlist
pub const DC_CHAT_ID_ARCHIVED_LINK: usize = 6;
/// only an indicator in a chatlist
@@ -79,47 +83,41 @@ pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7;
/// larger chat IDs are "real" chats, their messages are "real" messages.
pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9;
#[derive(
Debug,
Display,
Clone,
Copy,
PartialEq,
Eq,
FromPrimitive,
ToPrimitive,
FromSql,
ToSql,
IntoStaticStr,
)]
#[repr(u32)]
pub enum Chattype {
Undefined = 0,
Single = 100,
Group = 120,
VerifiedGroup = 130,
}
impl Default for Chattype {
fn default() -> Self {
Chattype::Undefined
}
}
pub const DC_CHAT_TYPE_UNDEFINED: i32 = 0;
pub const DC_CHAT_TYPE_SINGLE: i32 = 100;
pub const DC_CHAT_TYPE_GROUP: i32 = 120;
pub const DC_CHAT_TYPE_VERIFIED_GROUP: i32 = 130;
pub const DC_MSG_ID_MARKER1: usize = 1;
const DC_MSG_ID_DAYMARKER: usize = 9;
pub const DC_MSG_ID_DAYMARKER: usize = 9;
pub const DC_MSG_ID_LAST_SPECIAL: usize = 9;
pub const DC_STATE_UNDEFINED: i32 = 0;
pub const DC_STATE_IN_FRESH: i32 = 10;
pub const DC_STATE_IN_NOTICED: i32 = 13;
pub const DC_STATE_IN_SEEN: i32 = 16;
pub const DC_STATE_OUT_PREPARING: i32 = 18;
pub const DC_STATE_OUT_DRAFT: i32 = 19;
pub const DC_STATE_OUT_PENDING: i32 = 20;
pub const DC_STATE_OUT_FAILED: i32 = 24;
/// to check if a mail was sent, use dc_msg_is_sent()
pub const DC_STATE_OUT_DELIVERED: i32 = 26;
pub const DC_STATE_OUT_MDN_RCVD: i32 = 28;
/// approx. max. length returned by dc_msg_get_text()
const DC_MAX_GET_TEXT_LEN: usize = 30000;
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
/// approx. max. length returned by dc_get_msg_info()
const DC_MAX_GET_INFO_LEN: usize = 100000;
pub const DC_MAX_GET_INFO_LEN: usize = 100000;
pub const DC_CONTACT_ID_UNDEFINED: usize = 0;
pub const DC_CONTACT_ID_SELF: usize = 1;
const DC_CONTACT_ID_DEVICE: usize = 2;
pub const DC_CONTACT_ID_DEVICE: usize = 2;
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
pub const DC_TEXT1_DRAFT: usize = 1;
pub const DC_TEXT1_USERNAME: usize = 2;
pub const DC_TEXT1_SELF: usize = 3;
pub const DC_CREATE_MVBOX: usize = 1;
// Flags for configuring IMAP and SMTP servers.
@@ -128,13 +126,13 @@ pub const DC_CREATE_MVBOX: usize = 1;
// via dc_set_config() using the key "server_flags".
/// Force OAuth2 authorization. This flag does not skip automatic configuration.
/// Before calling configure() with DC_LP_AUTH_OAUTH2 set,
/// Before calling dc_configure() with DC_LP_AUTH_OAUTH2 set,
/// the user has to confirm access at the URL returned by dc_get_oauth2_url().
pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
/// Force NORMAL authorization, this is the default.
/// If this flag is set, automatic configuration is skipped.
const DC_LP_AUTH_NORMAL: usize = 0x4;
pub const DC_LP_AUTH_NORMAL: usize = 0x4;
/// Connect to IMAP via STARTTLS.
/// If this flag is set, automatic configuration is skipped.
@@ -142,7 +140,7 @@ pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
/// Connect to IMAP via SSL.
/// If this flag is set, automatic configuration is skipped.
const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
pub const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
/// Connect to IMAP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
@@ -154,19 +152,19 @@ pub const DC_LP_SMTP_SOCKET_STARTTLS: usize = 0x10000;
/// Connect to SMTP via SSL.
/// If this flag is set, automatic configuration is skipped.
const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
/// Connect to SMTP unencrypted, this should not be used.
/// If this flag is set, automatic configuration is skipped.
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
/// if none of these flags are set, the default is chosen
const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
pub const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
/// if none of these flags are set, the default is chosen
const DC_LP_IMAP_SOCKET_FLAGS: usize =
pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
/// if none of these flags are set, the default is chosen
const DC_LP_SMTP_SOCKET_FLAGS: usize =
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
@@ -282,7 +280,7 @@ pub enum Event {
/// As most things are asynchronous, things may go wrong at any time and the user
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
///
/// However, for ongoing processes (eg. configure())
/// However, for ongoing processes (eg. dc_configure())
/// or for functions that are expected to fail (eg. dc_continue_key_transfer())
/// it might be better to delay showing these events until the function has really
/// failed (returned false). It should be sufficient to report only the _last_ error
@@ -400,7 +398,7 @@ pub enum Event {
/// @return 0
LOCATION_CHANGED = 2035,
/// Inform about the configuration progress started by configure().
/// Inform about the configuration progress started by dc_configure().
///
/// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done
/// @param data2 0
@@ -465,65 +463,65 @@ pub enum Event {
GET_STRING = 2091,
}
const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
pub const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
pub const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
pub const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
pub const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
pub const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
/// Values for dc_get|set_config("show_emails")
const DC_SHOW_EMAILS_OFF: usize = 0;
const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
const DC_SHOW_EMAILS_ALL: usize = 2;
pub const DC_SHOW_EMAILS_OFF: usize = 0;
pub const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
pub const DC_SHOW_EMAILS_ALL: usize = 2;
// TODO: Strings need some doumentation about used placeholders.
// These constants are used to request strings using #DC_EVENT_GET_STRING.
const DC_STR_NOMESSAGES: usize = 1;
const DC_STR_SELF: usize = 2;
const DC_STR_DRAFT: usize = 3;
const DC_STR_MEMBER: usize = 4;
const DC_STR_CONTACT: usize = 6;
const DC_STR_VOICEMESSAGE: usize = 7;
const DC_STR_DEADDROP: usize = 8;
const DC_STR_IMAGE: usize = 9;
const DC_STR_VIDEO: usize = 10;
const DC_STR_AUDIO: usize = 11;
const DC_STR_FILE: usize = 12;
const DC_STR_STATUSLINE: usize = 13;
const DC_STR_NEWGROUPDRAFT: usize = 14;
const DC_STR_MSGGRPNAME: usize = 15;
const DC_STR_MSGGRPIMGCHANGED: usize = 16;
const DC_STR_MSGADDMEMBER: usize = 17;
const DC_STR_MSGDELMEMBER: usize = 18;
const DC_STR_MSGGROUPLEFT: usize = 19;
const DC_STR_GIF: usize = 23;
const DC_STR_ENCRYPTEDMSG: usize = 24;
const DC_STR_E2E_AVAILABLE: usize = 25;
const DC_STR_ENCR_TRANSP: usize = 27;
const DC_STR_ENCR_NONE: usize = 28;
const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
const DC_STR_FINGERPRINTS: usize = 30;
const DC_STR_READRCPT: usize = 31;
const DC_STR_READRCPT_MAILBODY: usize = 32;
const DC_STR_MSGGRPIMGDELETED: usize = 33;
const DC_STR_E2E_PREFERRED: usize = 34;
const DC_STR_CONTACT_VERIFIED: usize = 35;
const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
const DC_STR_ARCHIVEDCHATS: usize = 40;
const DC_STR_STARREDMSGS: usize = 41;
const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
const DC_STR_SELFTALK_SUBTITLE: usize = 50;
const DC_STR_CANNOT_LOGIN: usize = 60;
const DC_STR_SERVER_RESPONSE: usize = 61;
const DC_STR_MSGACTIONBYUSER: usize = 62;
const DC_STR_MSGACTIONBYME: usize = 63;
const DC_STR_MSGLOCATIONENABLED: usize = 64;
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
const DC_STR_LOCATION: usize = 66;
const DC_STR_COUNT: usize = 66;
pub const DC_STR_NOMESSAGES: usize = 1;
pub const DC_STR_SELF: usize = 2;
pub const DC_STR_DRAFT: usize = 3;
pub const DC_STR_MEMBER: usize = 4;
pub const DC_STR_CONTACT: usize = 6;
pub const DC_STR_VOICEMESSAGE: usize = 7;
pub const DC_STR_DEADDROP: usize = 8;
pub const DC_STR_IMAGE: usize = 9;
pub const DC_STR_VIDEO: usize = 10;
pub const DC_STR_AUDIO: usize = 11;
pub const DC_STR_FILE: usize = 12;
pub const DC_STR_STATUSLINE: usize = 13;
pub const DC_STR_NEWGROUPDRAFT: usize = 14;
pub const DC_STR_MSGGRPNAME: usize = 15;
pub const DC_STR_MSGGRPIMGCHANGED: usize = 16;
pub const DC_STR_MSGADDMEMBER: usize = 17;
pub const DC_STR_MSGDELMEMBER: usize = 18;
pub const DC_STR_MSGGROUPLEFT: usize = 19;
pub const DC_STR_GIF: usize = 23;
pub const DC_STR_ENCRYPTEDMSG: usize = 24;
pub const DC_STR_E2E_AVAILABLE: usize = 25;
pub const DC_STR_ENCR_TRANSP: usize = 27;
pub const DC_STR_ENCR_NONE: usize = 28;
pub const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
pub const DC_STR_FINGERPRINTS: usize = 30;
pub const DC_STR_READRCPT: usize = 31;
pub const DC_STR_READRCPT_MAILBODY: usize = 32;
pub const DC_STR_MSGGRPIMGDELETED: usize = 33;
pub const DC_STR_E2E_PREFERRED: usize = 34;
pub const DC_STR_CONTACT_VERIFIED: usize = 35;
pub const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
pub const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
pub const DC_STR_ARCHIVEDCHATS: usize = 40;
pub const DC_STR_STARREDMSGS: usize = 41;
pub const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
pub const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
pub const DC_STR_SELFTALK_SUBTITLE: usize = 50;
pub const DC_STR_CANNOT_LOGIN: usize = 60;
pub const DC_STR_SERVER_RESPONSE: usize = 61;
pub const DC_STR_MSGACTIONBYUSER: usize = 62;
pub const DC_STR_MSGACTIONBYME: usize = 63;
pub const DC_STR_MSGLOCATIONENABLED: usize = 64;
pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
pub const DC_STR_LOCATION: usize = 66;
pub const DC_STR_COUNT: usize = 66;
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
@@ -539,6 +537,6 @@ pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;

View File

@@ -7,12 +7,12 @@ use crate::aheader::EncryptPreference;
use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::dc_array::*;
use crate::dc_e2ee::*;
use crate::dc_loginparam::*;
use crate::dc_tools::*;
use crate::error::Result;
use crate::key::*;
use crate::message::MessageState;
use crate::peerstate::*;
use crate::sql;
use crate::stock::StockMessage;
@@ -139,7 +139,7 @@ pub enum Modifier {
Created,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
pub enum VerifiedStatus {
/// Contact is not verified.
@@ -251,7 +251,7 @@ impl<'a> Contact<'a> {
context,
&context.sql,
"UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
params![MessageState::InNoticed, id as i32, MessageState::InFresh],
params![DC_STATE_IN_NOTICED, id as i32, DC_STATE_IN_FRESH],
)
.is_ok()
{
@@ -328,7 +328,7 @@ impl<'a> Contact<'a> {
"<unset>"
},
);
bail!("Bad address supplied: {:?}", addr);
bail!("Bad address supplied");
}
let mut update_addr = false;
@@ -564,19 +564,24 @@ impl<'a> Contact<'a> {
}
/// Get blocked contacts.
pub fn get_all_blocked(context: &Context) -> Vec<u32> {
pub fn get_all_blocked(context: &Context) -> *mut dc_array_t {
context
.sql
.query_map(
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;",
params![DC_CONTACT_ID_LAST_SPECIAL as i32],
|row| row.get::<_, u32>(0),
|row| row.get::<_, i32>(0),
|ids| {
ids.collect::<std::result::Result<Vec<_>, _>>()
.map_err(Into::into)
let mut ret = Vec::new();
for id in ids {
ret.push(id? as u32);
}
Ok(dc_array_t::from(ret).into_raw())
},
)
.unwrap_or_default()
.unwrap_or_else(|_| std::ptr::null_mut())
}
/// Returns a textual summary of the encryption state for the contact.
@@ -792,7 +797,7 @@ impl<'a> Contact<'a> {
/// and can be used for an fallback avatar with white initials
/// as well as for headlines in bubbles of group chats.
pub fn get_color(&self) -> u32 {
dc_str_to_color(&self.addr)
dc_str_to_color_safe(&self.addr)
}
/// Check if a contact was verified. E.g. by a secure-join QR code scan
@@ -904,14 +909,26 @@ impl<'a> Contact<'a> {
}
}
fn get_first_name<'a>(full_name: &'a str) -> &'a str {
pub fn get_first_name<'a>(full_name: &'a str) -> &'a str {
full_name.splitn(2, ' ').next().unwrap_or_default()
}
/// Returns false if addr is an invalid address, otherwise true.
pub fn may_be_valid_addr(addr: &str) -> bool {
let res = addr.parse::<EmailAddress>();
res.is_ok()
if addr.is_empty() {
return false;
}
let at = addr.find('@').unwrap_or_default();
if at < 1 {
return false;
}
let dot = addr.find('.').unwrap_or_default();
if dot < 1 || dot > addr.len() - 3 || dot < at + 2 {
return false;
}
true
}
pub fn addr_normalize(addr: &str) -> &str {

View File

@@ -1,53 +1,39 @@
use std::ffi::OsString;
use std::path::Path;
use std::sync::{Arc, Condvar, Mutex, RwLock};
use crate::chat::*;
use crate::constants::*;
use crate::contact::*;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_job::*;
use crate::dc_jobthread::*;
use crate::dc_loginparam::*;
use crate::dc_lot::dc_lot_t;
use crate::dc_move::*;
use crate::dc_msg::*;
use crate::dc_receive_imf::*;
use crate::dc_tools::*;
use crate::error::*;
use crate::imap::*;
use crate::job::*;
use crate::job_thread::JobThread;
use crate::key::*;
use crate::lot::Lot;
use crate::message::*;
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
use crate::types::*;
use crate::x::*;
use std::ptr;
use std::path::PathBuf;
/// Callback function type for [Context].
///
/// @param context The context object as returned by dc_context_new().
/// @param event one of the @ref DC_EVENT constants
/// @param data1 depends on the event parameter
/// @param data2 depends on the event parameter
/// @return return 0 unless stated otherwise in the event parameter documentation
pub type ContextCallback = dyn Fn(&Context, Event, uintptr_t, uintptr_t) -> uintptr_t;
pub struct Context {
pub userdata: *mut libc::c_void,
pub dbfile: Arc<RwLock<Option<PathBuf>>>,
pub dbfile: Arc<RwLock<*mut libc::c_char>>,
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
pub probe_imap_network: Arc<RwLock<bool>>,
pub sentbox_thread: Arc<RwLock<JobThread>>,
pub mvbox_thread: Arc<RwLock<JobThread>>,
pub sentbox_thread: Arc<RwLock<dc_jobthread_t>>,
pub mvbox_thread: Arc<RwLock<dc_jobthread_t>>,
pub smtp: Arc<Mutex<Smtp>>,
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
pub oauth2_critical: Arc<Mutex<()>>,
pub cb: Box<ContextCallback>,
pub cb: Option<dc_callback_t>,
pub os_name: Option<String>,
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
pub bob: Arc<RwLock<BobStatus>>,
@@ -67,88 +53,16 @@ pub struct RunningState {
}
impl Context {
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> {
let mut blob_fname = OsString::new();
blob_fname.push(dbfile.file_name().unwrap_or_default());
blob_fname.push("-blobs");
let blobdir = dbfile.with_file_name(blob_fname);
if !blobdir.exists() {
std::fs::create_dir_all(&blobdir)?;
}
Context::with_blobdir(cb, os_name, dbfile, &blobdir)
}
pub fn with_blobdir(
cb: Box<ContextCallback>,
os_name: String,
dbfile: PathBuf,
blobdir: &Path,
) -> Result<Context> {
if !blobdir.is_dir() {
return Err(format_err!("Blobdir does not exist: {}", blobdir.display()));
}
let ctx = Context {
blobdir: Arc::new(RwLock::new(unsafe { blobdir.strdup() })),
dbfile: Arc::new(RwLock::new(Some(dbfile))),
inbox: Arc::new(RwLock::new({
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
)
})),
userdata: std::ptr::null_mut(),
cb,
os_name: Some(os_name),
running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())),
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
oauth2_critical: Arc::new(Mutex::new(())),
bob: Arc::new(RwLock::new(Default::default())),
last_smeared_timestamp: Arc::new(RwLock::new(0)),
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
"SENTBOX",
"configured_sentbox_folder",
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
))),
mvbox_thread: Arc::new(RwLock::new(JobThread::new(
"MVBOX",
"configured_mvbox_folder",
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
))),
probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
generating_key_mutex: Mutex::new(()),
};
ctx.sql.open(&ctx, &ctx.dbfile.read().unwrap().as_ref().unwrap(), 0)?;
Ok(ctx)
}
pub fn has_dbfile(&self) -> bool {
self.get_dbfile().is_some()
!self.get_dbfile().is_null()
}
pub fn has_blobdir(&self) -> bool {
!self.get_blobdir().is_null()
}
pub fn get_dbfile(&self) -> Option<PathBuf> {
(*self.dbfile.clone().read().unwrap())
.as_ref()
.map(|x| x.clone())
pub fn get_dbfile(&self) -> *const libc::c_char {
*self.dbfile.clone().read().unwrap()
}
pub fn get_blobdir(&self) -> *const libc::c_char {
@@ -156,7 +70,11 @@ impl Context {
}
pub fn call_cb(&self, event: Event, data1: uintptr_t, data2: uintptr_t) -> uintptr_t {
(*self.cb)(self, event, data1, data2)
if let Some(cb) = self.cb {
unsafe { cb(self, event, data1, data2) }
} else {
0
}
}
}
@@ -177,11 +95,21 @@ impl Default for RunningState {
}
}
#[derive(Default)]
#[derive(Debug, PartialEq, Eq)]
pub struct BobStatus {
pub expects: i32,
pub status: i32,
pub qr_scan: Option<Lot>,
pub qr_scan: *mut dc_lot_t,
}
impl Default for BobStatus {
fn default() -> Self {
BobStatus {
expects: 0,
status: 0,
qr_scan: std::ptr::null_mut(),
}
}
}
#[derive(Default, Debug)]
@@ -193,6 +121,60 @@ pub struct SmtpState {
pub probe_network: bool,
}
// create/open/config/information
pub fn dc_context_new(
cb: Option<dc_callback_t>,
userdata: *mut libc::c_void,
os_name: Option<String>,
) -> Context {
Context {
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
dbfile: Arc::new(RwLock::new(std::ptr::null_mut())),
inbox: Arc::new(RwLock::new({
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
)
})),
userdata,
cb,
os_name: os_name,
running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())),
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
oauth2_critical: Arc::new(Mutex::new(())),
bob: Arc::new(RwLock::new(Default::default())),
last_smeared_timestamp: Arc::new(RwLock::new(0)),
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
sentbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
"SENTBOX",
"configured_sentbox_folder",
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
))),
mvbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
"MVBOX",
"configured_mvbox_folder",
Imap::new(
cb_get_config,
cb_set_config,
cb_precheck_imf,
cb_receive_imf,
),
))),
probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
generating_key_mutex: Mutex::new(()),
}
}
unsafe fn cb_receive_imf(
context: &Context,
imf_raw_not_terminated: *const libc::c_char,
@@ -219,7 +201,7 @@ unsafe fn cb_precheck_imf(
) -> libc::c_int {
let mut rfc724_mid_exists: libc::c_int = 0i32;
let msg_id: uint32_t;
let mut old_server_folder: *mut libc::c_char = ptr::null_mut();
let mut old_server_folder: *mut libc::c_char = 0 as *mut libc::c_char;
let mut old_server_uid: uint32_t = 0i32 as uint32_t;
let mut mark_seen: libc::c_int = 0i32;
msg_id = dc_rfc724_mid_exists(
@@ -254,17 +236,11 @@ unsafe fn cb_precheck_imf(
}
dc_do_heuristics_moves(context, server_folder, msg_id);
if 0 != mark_seen {
job_add(
context,
Action::MarkseenMsgOnImap,
msg_id as libc::c_int,
Params::new(),
0,
);
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
}
}
free(old_server_folder as *mut libc::c_void);
rfc724_mid_exists
return rfc724_mid_exists;
}
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
@@ -305,16 +281,49 @@ pub unsafe fn dc_close(context: &Context) {
context.sql.close(context);
let mut dbfile = context.dbfile.write().unwrap();
*dbfile = None;
free(*dbfile as *mut libc::c_void);
*dbfile = 0 as *mut libc::c_char;
let mut blobdir = context.blobdir.write().unwrap();
free(*blobdir as *mut libc::c_void);
*blobdir = ptr::null_mut();
*blobdir = 0 as *mut libc::c_char;
}
pub unsafe fn dc_is_open(context: &Context) -> libc::c_int {
match context.sql.is_open() {
true => 1,
false => 0,
}
}
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
context.userdata as *mut _
}
pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool {
let mut success = false;
if 0 != dc_is_open(context) {
return false;
}
*context.dbfile.write().unwrap() = dbfile.strdup();
if blobdir.is_some() && blobdir.unwrap().len() > 0 {
let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup();
*context.blobdir.write().unwrap() = dir;
} else {
let dir = dbfile.to_string() + "-blobs";
dc_create_folder(context, &dir);
*context.blobdir.write().unwrap() = dir.strdup();
}
// Create/open sqlite database, this may already use the blobdir
let dbfile_path = std::path::Path::new(dbfile);
if context.sql.open(context, dbfile_path, 0) {
success = true
}
if !success {
dc_close(context);
}
success
}
pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char {
dc_strdup(*context.blobdir.clone().read().unwrap())
}
@@ -328,7 +337,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
let l = dc_loginparam_read(context, &context.sql, "");
let l2 = dc_loginparam_read(context, &context.sql, "configured_");
let displayname = context.sql.get_config(context, "displayname");
let chats = get_chat_cnt(context) as usize;
let chats = dc_get_chat_cnt(context) as usize;
let real_msgs = dc_get_real_msg_cnt(context) as usize;
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
let contacts = Contact::get_real_cnt(context) as usize;
@@ -430,7 +439,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
public_key_count={}\n\
fingerprint={}\n\
level=awesome\n",
&*DC_VERSION_STR,
as_str(DC_VERSION_STR as *const u8 as *const _),
rusqlite::version(),
sqlite3_threadsafe(),
// arch
@@ -439,10 +448,11 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
real_msgs,
deaddrop_msgs,
contacts,
context
.get_dbfile()
.as_ref()
.map_or(unset, |p| p.to_str().unwrap()),
if context.has_dbfile() {
as_str(context.get_dbfile())
} else {
unset
},
dbversion,
if context.has_blobdir() {
as_str(context.get_blobdir())
@@ -471,10 +481,10 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
}
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
(&*DC_VERSION_STR).strdup()
dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char)
}
pub fn dc_get_fresh_msgs(context: &Context) -> Vec<u32> {
pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
let show_deaddrop = 0;
context
@@ -494,7 +504,7 @@ pub fn dc_get_fresh_msgs(context: &Context) -> Vec<u32> {
let id: u32 = row?;
ret.push(id);
}
Ok(ret)
Ok(dc_array_t::from(ret).into_raw())
},
)
.unwrap()
@@ -505,14 +515,14 @@ pub fn dc_search_msgs(
context: &Context,
chat_id: uint32_t,
query: *const libc::c_char,
) -> Vec<u32> {
) -> *mut dc_array_t {
if query.is_null() {
return Vec::new();
return std::ptr::null_mut();
}
let real_query = to_string(query).trim().to_string();
if real_query.is_empty() {
return Vec::new();
return std::ptr::null_mut();
}
let strLikeInText = format!("%{}%", &real_query);
let strLikeBeg = format!("{}%", &real_query);
@@ -528,21 +538,28 @@ pub fn dc_search_msgs(
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
};
context
let mut ret = Vec::new();
let success = context
.sql
.query_map(
query,
params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg],
|row| row.get::<_, i32>(0),
|rows| {
let mut ret = Vec::new();
for id in rows {
ret.push(id? as u32);
}
Ok(ret)
Ok(())
},
)
.unwrap_or_default()
.is_ok();
if success {
return dc_array_t::from(ret).into_raw();
}
std::ptr::null_mut()
}
pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef<str>) -> bool {
@@ -572,70 +589,19 @@ pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
mod tests {
use super::*;
use crate::test_utils::*;
#[test]
fn test_wrong_db() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOs".into(), dbfile);
assert!(res.is_err());
}
#[test]
fn test_blobdir_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap();
let blobdir = tmp.path().join("db.sqlite-blobs");
assert!(blobdir.is_dir());
}
#[test]
fn test_wrong_blogdir() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("db.sqlite-blobs");
std::fs::write(&blobdir, b"123").unwrap();
let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile);
assert!(res.is_err());
}
#[test]
fn test_sqlite_parent_not_exists() {
let tmp = tempfile::tempdir().unwrap();
let subdir = tmp.path().join("subdir");
let dbfile = subdir.join("db.sqlite");
let dbfile2 = dbfile.clone();
Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap();
assert!(subdir.is_dir());
assert!(dbfile2.is_file());
}
#[test]
fn test_with_blobdir_not_exists() {
let tmp = tempfile::tempdir().unwrap();
let dbfile = tmp.path().join("db.sqlite");
let blobdir = tmp.path().join("blobs");
let res =
Context::with_blobdir(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile, &blobdir);
assert!(res.is_err());
}
#[test]
fn no_crashes_on_context_deref() {
let t = dummy_context();
std::mem::drop(t.ctx);
let ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into()));
std::mem::drop(ctx);
}
#[test]
fn test_context_double_close() {
let t = dummy_context();
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
unsafe {
dc_close(&t.ctx);
dc_close(&t.ctx);
dc_close(&ctx);
dc_close(&ctx);
}
std::mem::drop(t.ctx);
std::mem::drop(ctx);
}
}

View File

@@ -1,4 +1,5 @@
use crate::dc_location::dc_location;
use crate::dc_tools::*;
use crate::types::*;
/* * the structure behind dc_array_t */
@@ -6,7 +7,7 @@ use crate::types::*;
#[allow(non_camel_case_types)]
pub enum dc_array_t {
Locations(Vec<dc_location>),
Uint(Vec<u32>),
Uint(Vec<uintptr_t>),
}
impl dc_array_t {
@@ -19,14 +20,22 @@ impl dc_array_t {
dc_array_t::Locations(Vec::with_capacity(capacity))
}
pub fn add_id(&mut self, item: uint32_t) {
pub fn into_raw(self) -> *mut Self {
Box::into_raw(Box::new(self))
}
pub fn add_uint(&mut self, item: uintptr_t) {
if let Self::Uint(array) = self {
array.push(item);
} else {
panic!("Attempt to add id to array of other type");
panic!("Attempt to add uint to array of other type");
}
}
pub fn add_id(&mut self, item: uint32_t) {
self.add_uint(item as uintptr_t);
}
pub fn add_location(&mut self, location: dc_location) {
if let Self::Locations(array) = self {
array.push(location)
@@ -35,6 +44,14 @@ impl dc_array_t {
}
}
pub fn get_uint(&self, index: usize) -> uintptr_t {
if let Self::Uint(array) = self {
array[index]
} else {
panic!("Attempt to get uint from array of other type");
}
}
pub fn get_id(&self, index: usize) -> uint32_t {
match self {
Self::Locations(array) => array[index].location_id,
@@ -42,6 +59,14 @@ impl dc_array_t {
}
}
pub fn get_ptr(&self, index: size_t) -> *mut libc::c_void {
if let Self::Uint(array) = self {
array[index] as *mut libc::c_void
} else {
panic!("Not an array of pointers");
}
}
pub fn get_location(&self, index: usize) -> &dc_location {
if let Self::Locations(array) = self {
&array[index]
@@ -50,6 +75,34 @@ impl dc_array_t {
}
}
pub fn get_latitude(&self, index: usize) -> libc::c_double {
self.get_location(index).latitude
}
pub fn get_longitude(&self, index: size_t) -> libc::c_double {
self.get_location(index).longitude
}
pub fn get_accuracy(&self, index: size_t) -> libc::c_double {
self.get_location(index).accuracy
}
pub fn get_timestamp(&self, index: size_t) -> i64 {
self.get_location(index).timestamp
}
pub fn get_chat_id(&self, index: size_t) -> uint32_t {
self.get_location(index).chat_id
}
pub fn get_contact_id(&self, index: size_t) -> uint32_t {
self.get_location(index).contact_id
}
pub fn get_msg_id(&self, index: size_t) -> uint32_t {
self.get_location(index).msg_id
}
pub fn is_empty(&self) -> bool {
match self {
Self::Locations(array) => array.is_empty(),
@@ -72,7 +125,7 @@ impl dc_array_t {
}
}
pub fn search_id(&self, needle: u32) -> Option<usize> {
pub fn search_id(&self, needle: uintptr_t) -> Option<usize> {
if let Self::Uint(array) = self {
for (i, &u) in array.iter().enumerate() {
if u == needle {
@@ -92,19 +145,11 @@ impl dc_array_t {
panic!("Attempt to sort array of something other than uints");
}
}
pub fn as_ptr(&self) -> *const u32 {
if let dc_array_t::Uint(v) = self {
v.as_ptr()
} else {
panic!("Attempt to convert array of something other than uints to raw");
}
}
}
impl From<Vec<u32>> for dc_array_t {
fn from(array: Vec<u32>) -> Self {
dc_array_t::Uint(array)
dc_array_t::Uint(array.iter().map(|&x| x as uintptr_t).collect())
}
}
@@ -114,50 +159,249 @@ impl From<Vec<dc_location>> for dc_array_t {
}
}
pub unsafe fn dc_array_unref(array: *mut dc_array_t) {
assert!(!array.is_null());
Box::from_raw(array);
}
pub unsafe fn dc_array_add_uint(array: *mut dc_array_t, item: uintptr_t) {
assert!(!array.is_null());
(*array).add_uint(item);
}
pub unsafe fn dc_array_add_id(array: *mut dc_array_t, item: uint32_t) {
assert!(!array.is_null());
(*array).add_id(item);
}
pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
dc_array_add_uint(array, item as uintptr_t);
}
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
assert!(!array.is_null());
(*array).len()
}
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
assert!(!array.is_null());
(*array).get_uint(index)
}
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
assert!(!array.is_null());
(*array).get_id(index)
}
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
assert!(!array.is_null());
(*array).get_ptr(index)
}
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
assert!(!array.is_null());
(*array).get_latitude(index)
}
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
assert!(!array.is_null());
(*array).get_longitude(index)
}
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
assert!(!array.is_null());
(*array).get_accuracy(index)
}
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
assert!(!array.is_null());
(*array).get_timestamp(index)
}
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
assert!(!array.is_null());
(*array).get_chat_id(index)
}
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
assert!(!array.is_null());
(*array).get_contact_id(index)
}
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
assert!(!array.is_null());
(*array).get_msg_id(index)
}
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
assert!(!array.is_null());
if let dc_array_t::Locations(v) = &*array {
if let Some(s) = &v[index].marker {
s.strdup()
} else {
std::ptr::null_mut()
}
} else {
panic!("Not an array of locations");
}
}
/**
* Return the independent-state of the location at the given index.
* Independent locations do not belong to the track of the user.
*
* @memberof dc_array_t
* @param array The array object.
* @param index Index of the item. Must be between 0 and dc_array_get_cnt()-1.
* @return 0=Location belongs to the track of the user,
* 1=Location was reported independently.
*/
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
assert!(!array.is_null());
if let dc_array_t::Locations(v) = &*array {
v[index].independent as libc::c_int
} else {
panic!("Attempt to get location independent field from array of something other than locations");
}
}
pub unsafe fn dc_array_search_id(
array: *const dc_array_t,
needle: uint32_t,
ret_index: *mut size_t,
) -> bool {
assert!(!array.is_null());
if let Some(i) = (*array).search_id(needle as uintptr_t) {
if !ret_index.is_null() {
*ret_index = i
}
true
} else {
false
}
}
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
assert!(!array.is_null());
if let dc_array_t::Uint(v) = &*array {
v.as_ptr()
} else {
panic!("Attempt to convert array of something other than uints to raw");
}
}
pub fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
dc_array_t::new(initsize).into_raw()
}
pub fn dc_array_new_locations(initsize: size_t) -> *mut dc_array_t {
dc_array_t::new_locations(initsize).into_raw()
}
pub unsafe fn dc_array_empty(array: *mut dc_array_t) {
assert!(!array.is_null());
(*array).clear()
}
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
assert!(!array.is_null());
(*array).clone().into_raw()
}
pub unsafe fn dc_array_get_string(
array: *const dc_array_t,
sep: *const libc::c_char,
) -> *mut libc::c_char {
assert!(!array.is_null());
assert!(!sep.is_null());
if let dc_array_t::Uint(v) = &*array {
let cnt = v.len();
let sep = as_str(sep);
let res = v
.iter()
.enumerate()
.fold(String::with_capacity(2 * cnt), |res, (i, n)| {
if i == 0 {
res + &n.to_string()
} else {
res + sep + &n.to_string()
}
});
res.strdup()
} else {
panic!("Attempt to get string from array of other type");
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::x::*;
use std::ffi::CStr;
#[test]
fn test_dc_array() {
let mut arr = dc_array_t::new(7);
assert!(arr.is_empty());
unsafe {
let arr = dc_array_new(7 as size_t);
assert_eq!(dc_array_get_cnt(arr), 0);
for i in 0..1000 {
arr.add_id(i + 2);
for i in 0..1000 {
dc_array_add_id(arr, (i + 2) as uint32_t);
}
assert_eq!(dc_array_get_cnt(arr), 1000);
for i in 0..1000 {
assert_eq!(
dc_array_get_id(arr, i as size_t),
(i + 1i32 * 2i32) as libc::c_uint
);
}
dc_array_empty(arr);
assert_eq!(dc_array_get_cnt(arr), 0);
dc_array_add_id(arr, 13 as uint32_t);
dc_array_add_id(arr, 7 as uint32_t);
dc_array_add_id(arr, 666 as uint32_t);
dc_array_add_id(arr, 0 as uint32_t);
dc_array_add_id(arr, 5000 as uint32_t);
(*arr).sort_ids();
assert_eq!(dc_array_get_id(arr, 0 as size_t), 0);
assert_eq!(dc_array_get_id(arr, 1 as size_t), 7);
assert_eq!(dc_array_get_id(arr, 2 as size_t), 13);
assert_eq!(dc_array_get_id(arr, 3 as size_t), 666);
let str = dc_array_get_string(arr, b"-\x00" as *const u8 as *const libc::c_char);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"0-7-13-666-5000"
);
free(str as *mut libc::c_void);
dc_array_unref(arr);
}
assert_eq!(arr.len(), 1000);
for i in 0..1000 {
assert_eq!(arr.get_id(i), (i + 2) as u32);
}
arr.clear();
assert!(arr.is_empty());
arr.add_id(13);
arr.add_id(7);
arr.add_id(666);
arr.add_id(0);
arr.add_id(5000);
arr.sort_ids();
assert_eq!(arr.get_id(0), 0);
assert_eq!(arr.get_id(1), 7);
assert_eq!(arr.get_id(2), 13);
assert_eq!(arr.get_id(3), 666);
}
#[test]
#[should_panic]
fn test_dc_array_out_of_bounds() {
let mut arr = dc_array_t::new(7);
let arr = dc_array_new(7);
for i in 0..1000 {
arr.add_id(i + 2);
unsafe { dc_array_add_id(arr, (i + 2) as uint32_t) };
}
arr.get_id(1000);
unsafe { dc_array_get_id(arr, 1000) };
}
}

2259
src/dc_chat.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,35 +3,73 @@ use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use crate::constants::Event;
use crate::context::Context;
use crate::dc_e2ee::*;
use crate::dc_job::*;
use crate::dc_loginparam::*;
use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::job::*;
use crate::oauth2::*;
use crate::param::Params;
use crate::types::*;
mod auto_outlook;
use auto_outlook::outlk_autodiscover;
mod auto_mozilla;
use auto_mozilla::moz_autoconfigure;
use crate::x::*;
macro_rules! progress {
($context:tt, $progress:expr) => {
assert!(
$progress >= 0 && $progress <= 1000,
"value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success"
);
$context.call_cb(
Event::CONFIGURE_PROGRESS,
$progress as uintptr_t,
(if $progress < 1 {
1
} else if $progress > 1000 {
1000
} else {
$progress
}) as uintptr_t,
0 as uintptr_t,
);
};
}
/* ******************************************************************************
* Configure folders
******************************************************************************/
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_imapfolder_t {
pub name_to_select: *mut libc::c_char,
pub name_utf8: *mut libc::c_char,
pub meaning: libc::c_int,
}
/* ******************************************************************************
* Thunderbird's Autoconfigure
******************************************************************************/
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
#[repr(C)]
pub struct moz_autoconfigure_t<'a> {
pub in_0: &'a dc_loginparam_t,
pub in_emaildomain: *mut libc::c_char,
pub in_emaillocalpart: *mut libc::c_char,
pub out: dc_loginparam_t,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_server: libc::c_int,
pub tag_config: libc::c_int,
}
/* ******************************************************************************
* Outlook's Autodiscover
******************************************************************************/
#[repr(C)]
pub struct outlk_autodiscover_t<'a> {
pub in_0: &'a dc_loginparam_t,
pub out: dc_loginparam_t,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_config: libc::c_int,
pub config: [*mut libc::c_char; 6],
pub redirect: *mut libc::c_char,
}
// connect
pub unsafe fn configure(context: &Context) {
pub unsafe fn dc_configure(context: &Context) {
if 0 != dc_has_ongoing(context) {
warn!(
context,
@@ -39,11 +77,11 @@ pub unsafe fn configure(context: &Context) {
);
return;
}
job_kill_action(context, Action::ConfigureImap);
job_add(context, Action::ConfigureImap, 0, Params::new(), 0);
dc_job_kill_action(context, 900);
dc_job_add(context, 900, 0, Params::new(), 0);
}
unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
pub unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
let s_a = context.running_state.clone();
let s = s_a.read().unwrap();
@@ -80,7 +118,7 @@ pub fn dc_stop_ongoing_process(context: &Context) {
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
#[allow(non_snake_case, unused_must_use)]
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_job_t) {
let flags: libc::c_int;
let mut success = false;
let mut imap_connected_here = false;
@@ -113,7 +151,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
let s = s_a.read().unwrap();
if !s.shall_stop_ongoing {
progress!(context, 1);
progress!(context, 0);
let mut param = dc_loginparam_read(context, &context.sql, "");
if param.addr.is_empty() {
@@ -149,13 +187,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
ok_to_continue0 = true;
}
if ok_to_continue0 {
let parsed: Result<EmailAddress, _> = param.addr.parse();
let parsed: addr::Result<addr::Email> = param.addr.parse();
let mut ok_to_continue7 = false;
if parsed.is_err() {
error!(context, 0, "Bad email-address.");
} else {
let parsed = parsed.unwrap();
let param_domain = parsed.domain;
let param_domain = parsed.host();
let param_addr_urlencoded =
utf8_percent_encode(&param.addr, NON_ALPHANUMERIC).to_string();
@@ -654,6 +692,410 @@ pub unsafe fn dc_free_ongoing(context: &Context) {
s.shall_stop_ongoing = true;
}
unsafe fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &dc_loginparam_t,
) -> Option<dc_loginparam_t> {
let mut moz_ac = moz_autoconfigure_t {
in_0: param_in,
in_emaildomain: std::ptr::null_mut(),
in_emaillocalpart: std::ptr::null_mut(),
out: dc_loginparam_new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_server: 0,
tag_config: 0,
};
let url_c = url.strdup();
let xml_raw = read_autoconf_file(context, url_c);
free(url_c as *mut libc::c_void);
if xml_raw.is_null() {
return None;
}
moz_ac.in_emaillocalpart = param_in.addr.strdup();
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
if p.is_null() {
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
return None;
}
*p = 0 as libc::c_char;
moz_ac.in_emaildomain = dc_strdup(p.offset(1isize));
let mut saxparser = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
dc_saxparser_init(
&mut saxparser,
&mut moz_ac as *mut moz_autoconfigure_t as *mut libc::c_void,
);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(moz_autoconfigure_starttag_cb),
Some(moz_autoconfigure_endtag_cb),
);
dc_saxparser_set_text_handler(&mut saxparser, Some(moz_autoconfigure_text_cb));
dc_saxparser_parse(&mut saxparser, xml_raw);
if moz_ac.out.mail_server.is_empty()
|| moz_ac.out.mail_port == 0
|| moz_ac.out.send_server.is_empty()
|| moz_ac.out.send_port == 0
{
let r = dc_loginparam_get_readable(&moz_ac.out);
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
return None;
}
free(xml_raw as *mut libc::c_void);
free(moz_ac.in_emaildomain as *mut libc::c_void);
free(moz_ac.in_emaillocalpart as *mut libc::c_void);
Some(moz_ac.out)
}
unsafe fn moz_autoconfigure_text_cb(
userdata: *mut libc::c_void,
text: *const libc::c_char,
_len: libc::c_int,
) {
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
let mut val: *mut libc::c_char = dc_strdup(text);
dc_trim(val);
let addr = (*moz_ac).in_0.addr.strdup();
dc_str_replace(
&mut val,
b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char,
addr,
);
free(addr as *mut libc::c_void);
dc_str_replace(
&mut val,
b"%EMAILLOCALPART%\x00" as *const u8 as *const libc::c_char,
(*moz_ac).in_emaillocalpart,
);
dc_str_replace(
&mut val,
b"%EMAILDOMAIN%\x00" as *const u8 as *const libc::c_char,
(*moz_ac).in_emaildomain,
);
if (*moz_ac).tag_server == 1 {
match (*moz_ac).tag_config {
10 => {
(*moz_ac).out.mail_server = to_string(val);
val = 0 as *mut libc::c_char
}
11 => (*moz_ac).out.mail_port = dc_atoi_null_is_0(val),
12 => {
(*moz_ac).out.mail_user = to_string(val);
val = 0 as *mut libc::c_char
}
13 => {
if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).out.server_flags |= 0x200
}
if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).out.server_flags |= 0x100
}
if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).out.server_flags |= 0x400
}
}
_ => {}
}
} else if (*moz_ac).tag_server == 2 {
match (*moz_ac).tag_config {
10 => {
(*moz_ac).out.send_server = to_string(val);
val = 0 as *mut libc::c_char
}
11 => (*moz_ac).out.send_port = as_str(val).parse().unwrap_or_default(),
12 => {
(*moz_ac).out.send_user = to_string(val);
val = 0 as *mut libc::c_char
}
13 => {
if strcasecmp(val, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).out.server_flags |= 0x20000
}
if strcasecmp(val, b"starttls\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).out.server_flags |= 0x10000
}
if strcasecmp(val, b"plain\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).out.server_flags |= 0x40000
}
}
_ => {}
}
}
free(val as *mut libc::c_void);
}
unsafe fn moz_autoconfigure_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
if strcmp(
tag,
b"incomingserver\x00" as *const u8 as *const libc::c_char,
) == 0
{
(*moz_ac).tag_server = 0;
(*moz_ac).tag_config = 0;
(*moz_ac).out_imap_set = 1
} else if strcmp(
tag,
b"outgoingserver\x00" as *const u8 as *const libc::c_char,
) == 0
{
(*moz_ac).tag_server = 0;
(*moz_ac).tag_config = 0;
(*moz_ac).out_smtp_set = 1
} else {
(*moz_ac).tag_config = 0
};
}
unsafe fn moz_autoconfigure_starttag_cb(
userdata: *mut libc::c_void,
tag: *const libc::c_char,
attr: *mut *mut libc::c_char,
) {
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
let mut p1: *const libc::c_char = 0 as *const libc::c_char;
if strcmp(
tag,
b"incomingserver\x00" as *const u8 as *const libc::c_char,
) == 0
{
(*moz_ac).tag_server = if (*moz_ac).out_imap_set == 0
&& {
p1 = dc_attr_find(attr, b"type\x00" as *const u8 as *const libc::c_char);
!p1.is_null()
}
&& strcasecmp(p1, b"imap\x00" as *const u8 as *const libc::c_char) == 0
{
1
} else {
0
};
(*moz_ac).tag_config = 0
} else if strcmp(
tag,
b"outgoingserver\x00" as *const u8 as *const libc::c_char,
) == 0
{
(*moz_ac).tag_server = if (*moz_ac).out_smtp_set == 0 { 2 } else { 0 };
(*moz_ac).tag_config = 0
} else if strcmp(tag, b"hostname\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).tag_config = 10
} else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).tag_config = 11
} else if strcmp(tag, b"sockettype\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).tag_config = 13
} else if strcmp(tag, b"username\x00" as *const u8 as *const libc::c_char) == 0 {
(*moz_ac).tag_config = 12
};
}
fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc::c_char {
info!(context, 0, "Testing {} ...", to_string(url));
match reqwest::Client::new()
.get(as_str(url))
.send()
.and_then(|mut res| res.text())
{
Ok(res) => unsafe { res.strdup() },
Err(_err) => {
info!(context, 0, "Can\'t read file.",);
std::ptr::null_mut()
}
}
}
unsafe fn outlk_autodiscover(
context: &Context,
url__: &str,
param_in: &dc_loginparam_t,
) -> Option<dc_loginparam_t> {
let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char;
let mut url = url__.strdup();
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: dc_loginparam_new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_config: 0,
config: [0 as *mut libc::c_char; 6],
redirect: 0 as *mut libc::c_char,
};
let ok_to_continue;
let mut i = 0;
loop {
if !(i < 10) {
ok_to_continue = true;
break;
}
memset(
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
0,
::std::mem::size_of::<outlk_autodiscover_t>(),
);
xml_raw = read_autoconf_file(context, url);
if xml_raw.is_null() {
ok_to_continue = false;
break;
}
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
dc_saxparser_init(
&mut saxparser,
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(outlk_autodiscover_starttag_cb),
Some(outlk_autodiscover_endtag_cb),
);
dc_saxparser_set_text_handler(&mut saxparser, Some(outlk_autodiscover_text_cb));
dc_saxparser_parse(&mut saxparser, xml_raw);
if !(!outlk_ad.config[5usize].is_null()
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
{
ok_to_continue = true;
break;
}
free(url as *mut libc::c_void);
url = dc_strdup(outlk_ad.config[5usize]);
outlk_clean_config(&mut outlk_ad);
free(xml_raw as *mut libc::c_void);
xml_raw = 0 as *mut libc::c_char;
i += 1;
}
if ok_to_continue {
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = dc_loginparam_get_readable(&outlk_ad.out);
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
return None;
}
}
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
Some(outlk_ad.out)
}
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
let mut i: libc::c_int = 0;
while i < 6 {
free((*outlk_ad).config[i as usize] as *mut libc::c_void);
(*outlk_ad).config[i as usize] = 0 as *mut libc::c_char;
i += 1
}
}
unsafe fn outlk_autodiscover_text_cb(
userdata: *mut libc::c_void,
text: *const libc::c_char,
_len: libc::c_int,
) {
let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t;
let val: *mut libc::c_char = dc_strdup(text);
dc_trim(val);
free((*outlk_ad).config[(*outlk_ad).tag_config as usize] as *mut libc::c_void);
(*outlk_ad).config[(*outlk_ad).tag_config as usize] = val;
}
unsafe fn outlk_autodiscover_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t;
if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0 {
if !(*outlk_ad).config[1usize].is_null() {
let port: libc::c_int = dc_atoi_null_is_0((*outlk_ad).config[3usize]);
let ssl_on: libc::c_int = (!(*outlk_ad).config[4usize].is_null()
&& strcasecmp(
(*outlk_ad).config[4usize],
b"on\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
let ssl_off: libc::c_int = (!(*outlk_ad).config[4usize].is_null()
&& strcasecmp(
(*outlk_ad).config[4usize],
b"off\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
if strcasecmp(
(*outlk_ad).config[1usize],
b"imap\x00" as *const u8 as *const libc::c_char,
) == 0
&& (*outlk_ad).out_imap_set == 0
{
(*outlk_ad).out.mail_server = to_string((*outlk_ad).config[2]);
(*outlk_ad).out.mail_port = port;
if 0 != ssl_on {
(*outlk_ad).out.server_flags |= 0x200
} else if 0 != ssl_off {
(*outlk_ad).out.server_flags |= 0x400
}
(*outlk_ad).out_imap_set = 1
} else if strcasecmp(
(*outlk_ad).config[1usize],
b"smtp\x00" as *const u8 as *const libc::c_char,
) == 0
&& (*outlk_ad).out_smtp_set == 0
{
(*outlk_ad).out.send_server = to_string((*outlk_ad).config[2]);
(*outlk_ad).out.send_port = port;
if 0 != ssl_on {
(*outlk_ad).out.server_flags |= 0x20000
} else if 0 != ssl_off {
(*outlk_ad).out.server_flags |= 0x40000
}
(*outlk_ad).out_smtp_set = 1
}
}
outlk_clean_config(outlk_ad);
}
(*outlk_ad).tag_config = 0;
}
unsafe fn outlk_autodiscover_starttag_cb(
userdata: *mut libc::c_void,
tag: *const libc::c_char,
_attr: *mut *mut libc::c_char,
) {
let mut outlk_ad: *mut outlk_autodiscover_t = userdata as *mut outlk_autodiscover_t;
if strcmp(tag, b"protocol\x00" as *const u8 as *const libc::c_char) == 0 {
outlk_clean_config(outlk_ad);
} else if strcmp(tag, b"type\x00" as *const u8 as *const libc::c_char) == 0 {
(*outlk_ad).tag_config = 1
} else if strcmp(tag, b"server\x00" as *const u8 as *const libc::c_char) == 0 {
(*outlk_ad).tag_config = 2
} else if strcmp(tag, b"port\x00" as *const u8 as *const libc::c_char) == 0 {
(*outlk_ad).tag_config = 3
} else if strcmp(tag, b"ssl\x00" as *const u8 as *const libc::c_char) == 0 {
(*outlk_ad).tag_config = 4
} else if strcmp(tag, b"redirecturl\x00" as *const u8 as *const libc::c_char) == 0 {
(*outlk_ad).tag_config = 5
};
}
pub unsafe fn dc_alloc_ongoing(context: &Context) -> libc::c_int {
if 0 != dc_has_ongoing(context) {
warn!(
@@ -694,20 +1136,3 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_
ret_connected
}
pub fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc::c_char {
info!(context, 0, "Testing {} ...", to_string(url));
match reqwest::Client::new()
.get(as_str(url))
.send()
.and_then(|mut res| res.text())
{
Ok(res) => unsafe { res.strdup() },
Err(_err) => {
info!(context, 0, "Can\'t read file.",);
std::ptr::null_mut()
}
}
}

View File

@@ -1,7 +1,6 @@
use lazy_static::lazy_static;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::x::*;
@@ -35,67 +34,56 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
add_text: AddText::YesRemoveLineEnds,
last_href: None,
};
let mut reader = quick_xml::Reader::from_str(as_str(buf_terminated));
let mut buf = Vec::new();
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
dehtml_starttag_cb(e, &mut dehtml, &reader)
}
Ok(quick_xml::events::Event::End(ref e)) => dehtml_endtag_cb(e, &mut dehtml),
Ok(quick_xml::events::Event::Text(ref e)) => dehtml_text_cb(e, &mut dehtml),
Ok(quick_xml::events::Event::CData(ref e)) => dehtml_cdata_cb(e, &mut dehtml),
Err(e) => {
eprintln!(
"Parse html error: Error at position {}: {:?}",
reader.buffer_position(),
e
);
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
let mut saxparser = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
dc_saxparser_init(
&mut saxparser,
&mut dehtml as *mut Dehtml as *mut libc::c_void,
);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(dehtml_starttag_cb),
Some(dehtml_endtag_cb),
);
dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb));
dc_saxparser_parse(&mut saxparser, buf_terminated);
dehtml.strbuilder.strdup()
}
fn dehtml_text_cb(event: &BytesText, dehtml: &mut Dehtml) {
unsafe fn dehtml_text_cb(
userdata: *mut libc::c_void,
text: *const libc::c_char,
_len: libc::c_int,
) {
let dehtml = &mut *(userdata as *mut Dehtml);
if dehtml.add_text == AddText::YesPreserveLineEnds
|| dehtml.add_text == AddText::YesRemoveLineEnds
{
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
let last_added = std::ffi::CStr::from_ptr(text)
.to_str()
.expect("invalid utf8");
// TODO: why does len does not match?
// assert_eq!(last_added.len(), len as usize);
if dehtml.add_text == AddText::YesRemoveLineEnds {
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref();
} else {
dehtml.strbuilder += &last_added;
dehtml.strbuilder += last_added.as_ref();
}
}
}
fn dehtml_cdata_cb(event: &BytesText, dehtml: &mut Dehtml) {
if dehtml.add_text == AddText::YesPreserveLineEnds
|| dehtml.add_text == AddText::YesRemoveLineEnds
{
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
let mut dehtml = &mut *(userdata as *mut Dehtml);
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
if dehtml.add_text == AddText::YesRemoveLineEnds {
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
} else {
dehtml.strbuilder += &last_added;
}
}
}
fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
match tag.as_str() {
match tag.as_ref() {
"p" | "div" | "table" | "td" | "style" | "script" | "title" | "pre" => {
dehtml.strbuilder += "\n\n";
dehtml.add_text = AddText::YesRemoveLineEnds;
@@ -117,14 +105,15 @@ fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) {
}
}
fn dehtml_starttag_cb<B: std::io::BufRead>(
event: &BytesStart,
dehtml: &mut Dehtml,
reader: &quick_xml::Reader<B>,
unsafe fn dehtml_starttag_cb(
userdata: *mut libc::c_void,
tag: *const libc::c_char,
attr: *mut *mut libc::c_char,
) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
let mut dehtml = &mut *(userdata as *mut Dehtml);
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
match tag.as_str() {
match tag.as_ref() {
"p" | "div" | "table" | "td" => {
dehtml.strbuilder += "\n\n";
dehtml.add_text = AddText::YesRemoveLineEnds;
@@ -141,21 +130,14 @@ fn dehtml_starttag_cb<B: std::io::BufRead>(
dehtml.add_text = AddText::YesPreserveLineEnds;
}
"a" => {
if let Some(href) = event.html_attributes().find(|attr| {
attr.as_ref()
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "href")
.unwrap_or_default()
}) {
let href = href
.unwrap()
.unescape_and_decode_value(reader)
.unwrap_or_default()
.to_lowercase();
if !href.is_empty() {
dehtml.last_href = Some(href);
dehtml.strbuilder += "[";
}
let text_c = std::ffi::CStr::from_ptr(dc_attr_find(
attr,
b"href\x00" as *const u8 as *const libc::c_char,
));
let text_r = text_c.to_str().expect("invalid utf8");
if !text_r.is_empty() {
dehtml.last_href = Some(text_r.to_string());
dehtml.strbuilder += "[";
}
}
"b" | "strong" => {

View File

@@ -2,7 +2,6 @@
use std::collections::HashSet;
use std::ffi::CStr;
use std::ptr;
use std::str::FromStr;
use mmime::clist::*;
@@ -146,7 +145,7 @@ pub unsafe fn dc_e2ee_encrypt(
iter1 = if !iter1.is_null() {
(*iter1).next
} else {
ptr::null_mut()
0 as *mut clistcell
}
}
}
@@ -171,19 +170,19 @@ pub unsafe fn dc_e2ee_encrypt(
mailprivacy_prepare_mime(in_out_message);
let mut part_to_encrypt: *mut mailmime =
(*in_out_message).mm_data.mm_message.mm_msg_mime;
(*part_to_encrypt).mm_parent = ptr::null_mut();
(*part_to_encrypt).mm_parent = 0 as *mut mailmime;
let imffields_encrypted: *mut mailimf_fields = mailimf_fields_new_empty();
/* mailmime_new_message_data() calls mailmime_fields_new_with_version() which would add the unwanted MIME-Version:-header */
let message_to_encrypt: *mut mailmime = mailmime_new(
MAILMIME_MESSAGE as libc::c_int,
ptr::null(),
0 as *const libc::c_char,
0i32 as size_t,
mailmime_fields_new_empty(),
mailmime_get_content_message(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0 as *mut mailmime_data,
0 as *mut mailmime_data,
0 as *mut mailmime_data,
0 as *mut clist,
imffields_encrypted,
part_to_encrypt,
);
@@ -215,7 +214,7 @@ pub unsafe fn dc_e2ee_encrypt(
let field: *mut mailimf_field = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
0 as *mut libc::c_void
})
as *mut mailimf_field;
if !field.is_null() {
@@ -266,28 +265,28 @@ pub unsafe fn dc_e2ee_encrypt(
imffields_unprotected,
mailimf_field_new(
MAILIMF_FIELD_SUBJECT as libc::c_int,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0 as *mut mailimf_return,
0 as *mut mailimf_orig_date,
0 as *mut mailimf_from,
0 as *mut mailimf_sender,
0 as *mut mailimf_to,
0 as *mut mailimf_cc,
0 as *mut mailimf_bcc,
0 as *mut mailimf_message_id,
0 as *mut mailimf_orig_date,
0 as *mut mailimf_from,
0 as *mut mailimf_sender,
0 as *mut mailimf_reply_to,
0 as *mut mailimf_to,
0 as *mut mailimf_cc,
0 as *mut mailimf_bcc,
0 as *mut mailimf_message_id,
0 as *mut mailimf_in_reply_to,
0 as *mut mailimf_references,
subject,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0 as *mut mailimf_comments,
0 as *mut mailimf_keywords,
0 as *mut mailimf_optional_field,
),
);
clist_insert_after(
@@ -315,7 +314,7 @@ pub unsafe fn dc_e2ee_encrypt(
/* create MIME-structure that will contain the encrypted text */
let mut encrypted_part: *mut mailmime = new_data_part(
ptr::null_mut(),
0 as *mut libc::c_void,
0i32 as size_t,
b"multipart/encrypted\x00" as *const u8 as *const libc::c_char
as *mut libc::c_char,
@@ -401,7 +400,7 @@ unsafe fn new_data_part(
let encoding_type: libc::c_int;
let content_type_str: *mut libc::c_char;
let mut do_encoding: libc::c_int;
encoding = ptr::null_mut();
encoding = 0 as *mut mailmime_mechanism;
if default_content_type.is_null() {
content_type_str =
b"application/octet-stream\x00" as *const u8 as *const libc::c_char as *mut libc::c_char
@@ -436,7 +435,7 @@ unsafe fn new_data_part(
} else {
encoding_type = default_encoding
}
encoding = mailmime_mechanism_new(encoding_type, ptr::null_mut());
encoding = mailmime_mechanism_new(encoding_type, 0 as *mut libc::c_char);
if encoding.is_null() {
ok_to_continue = false;
}
@@ -444,10 +443,10 @@ unsafe fn new_data_part(
if ok_to_continue {
mime_fields = mailmime_fields_new_with_data(
encoding,
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
0 as *mut libc::c_char,
0 as *mut libc::c_char,
0 as *mut mailmime_disposition,
0 as *mut mailmime_language,
);
if mime_fields.is_null() {
ok_to_continue = false;
@@ -477,7 +476,7 @@ unsafe fn new_data_part(
mailmime_content_free(content);
}
}
ptr::null_mut()
return 0 as *mut mailmime;
}
/// Load public key from database or generate a new one.
@@ -540,10 +539,10 @@ pub unsafe fn dc_e2ee_decrypt(
/*just a pointer into mailmime structure, must not be freed*/
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
let mut message_time = 0;
let mut from: *mut libc::c_char = ptr::null_mut();
let mut from: *mut libc::c_char = 0 as *mut libc::c_char;
let mut private_keyring = Keyring::default();
let mut public_keyring_for_validate = Keyring::default();
let mut gossip_headers: *mut mailimf_fields = ptr::null_mut();
let mut gossip_headers: *mut mailimf_fields = 0 as *mut mailimf_fields;
if !(in_out_message.is_null() || imffields.is_null()) {
let mut field: *mut mailimf_field =
mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
@@ -647,7 +646,7 @@ unsafe fn update_gossip_peerstates(
let field: *mut mailimf_field = (if !cur1.is_null() {
(*cur1).data
} else {
ptr::null_mut()
0 as *mut libc::c_void
}) as *mut mailimf_field;
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
let optional_field: *const mailimf_optional_field =
@@ -699,7 +698,7 @@ unsafe fn update_gossip_peerstates(
cur1 = if !cur1.is_null() {
(*cur1).next
} else {
ptr::null_mut()
0 as *mut clistcell
}
}
@@ -732,7 +731,7 @@ unsafe fn decrypt_recursive(
{
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
while !cur.is_null() {
let mut decrypted_mime: *mut mailmime = ptr::null_mut();
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
if 0 != decrypt_part(
context,
(if !cur.is_null() {
@@ -747,7 +746,7 @@ unsafe fn decrypt_recursive(
) {
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
let mut dummy: size_t = 0i32 as size_t;
let mut test: *mut mailimf_fields = ptr::null_mut();
let mut test: *mut mailimf_fields = 0 as *mut mailimf_fields;
if mailimf_envelope_and_optional_fields_parse(
(*decrypted_mime).mm_mime_start,
(*decrypted_mime).mm_length,
@@ -778,7 +777,7 @@ unsafe fn decrypt_recursive(
(if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
0 as *mut libc::c_void
}) as *mut mailmime,
private_keyring,
public_keyring_for_validate,
@@ -791,7 +790,7 @@ unsafe fn decrypt_recursive(
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
0 as *mut clistcell
}
}
}
@@ -826,12 +825,12 @@ unsafe fn decrypt_part(
let mime_data: *mut mailmime_data;
let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int;
/* mmap_string_unref()'d if set */
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char;
/* must not be free()'d */
let mut decoded_data: *const libc::c_char = ptr::null_mut();
let mut decoded_data: *const libc::c_char = 0 as *const libc::c_char;
let mut decoded_data_bytes: size_t = 0i32 as size_t;
let mut sth_decrypted: libc::c_int = 0i32;
*ret_decrypted_mime = ptr::null_mut();
*ret_decrypted_mime = 0 as *mut mailmime;
mime_data = (*mime).mm_data.mm_single;
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
if !((*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int
@@ -845,7 +844,7 @@ unsafe fn decrypt_part(
let field: *mut mailmime_field = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
0 as *mut libc::c_void
}) as *mut mailmime_field;
if !field.is_null() {
if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
@@ -857,7 +856,7 @@ unsafe fn decrypt_part(
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
0 as *mut clistcell
}
}
}
@@ -913,7 +912,7 @@ unsafe fn decrypt_part(
let plain_buf = plain.as_ptr() as *const libc::c_char;
let mut index: size_t = 0i32 as size_t;
let mut decrypted_mime: *mut mailmime = ptr::null_mut();
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
if mailmime_parse(
plain_buf as *const _,
plain_bytes,
@@ -1011,7 +1010,7 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
(if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
0 as *mut libc::c_void
}) as *mut mailmime,
) {
return 1i32;
@@ -1034,7 +1033,7 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
/* frees data referenced by "mailmime" but not freed by mailmime_free(). After calling this function, in_out_message cannot be used any longer! */
pub unsafe fn dc_e2ee_thanks(helper: &mut dc_e2ee_helper_t) {
free(helper.cdata_to_free);
helper.cdata_to_free = ptr::null_mut();
helper.cdata_to_free = 0 as *mut libc::c_void;
}
/// Ensures a private key exists for the configured user.

View File

@@ -1,22 +1,21 @@
use std::ffi::CString;
use std::ptr;
use mmime::mailmime_content::*;
use mmime::mmapstring::*;
use mmime::other::*;
use rand::{thread_rng, Rng};
use crate::chat;
use crate::config::Config;
use crate::configure::*;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_configure::*;
use crate::dc_e2ee::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::error::*;
use crate::job::*;
use crate::key::*;
use crate::message::*;
use crate::param::*;
use crate::pgp::*;
use crate::sql::{self, Sql};
@@ -44,8 +43,8 @@ pub unsafe fn dc_imex(
param.set(Param::Arg2, as_str(param2));
}
job_kill_action(context, Action::ImexImap);
job_add(context, Action::ImexImap, 0, param, 0);
dc_job_kill_action(context, 910);
dc_job_add(context, 910, 0, param, 0);
}
/// Returns the filename of the backup if found, nullptr otherwise.
@@ -62,7 +61,7 @@ pub unsafe fn dc_imex_has_backup(
"Backup check: Cannot open directory \"{}\".\x00",
dir_name.display(),
);
return ptr::null_mut();
return 0 as *mut libc::c_char;
}
let mut newest_backup_time = 0;
let mut newest_backup_path: Option<std::path::PathBuf> = None;
@@ -74,7 +73,7 @@ pub unsafe fn dc_imex_has_backup(
let name = name.to_string_lossy();
if name.starts_with("delta-chat") && name.ends_with(".bak") {
let sql = Sql::new();
if sql.open(context, &path, 0x1).is_ok() {
if sql.open(context, &path, 0x1) {
let curr_backup_time =
sql.get_config_int(context, "backup_time")
.unwrap_or_default() as u64;
@@ -101,8 +100,8 @@ pub unsafe fn dc_imex_has_backup(
}
pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
let mut setup_file_name: *mut libc::c_char = ptr::null_mut();
let mut msg: Message;
let mut setup_file_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
if dc_alloc_ongoing(context) == 0 {
return std::ptr::null_mut();
}
@@ -138,15 +137,17 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
setup_file_content_c.as_bytes().len(),
))
{
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t);
if !(chat_id == 0i32 as libc::c_uint) {
msg = dc_msg_new_untyped(context);
msg.type_0 = Viewtype::File;
msg.param.set(Param::File, as_str(setup_file_name));
(*msg).type_0 = Viewtype::File;
(*msg).param.set(Param::File, as_str(setup_file_name));
msg.param
(*msg)
.param
.set(Param::MimeType, "application/autocrypt-setup");
msg.param.set_int(Param::Cmd, 6);
msg.param.set_int(Param::ForcePlaintext, 2);
(*msg).param.set_int(Param::Cmd, 6);
(*msg).param.set_int(Param::ForcePlaintext, 2);
if !context
.running_state
@@ -155,7 +156,10 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
.unwrap()
.shall_stop_ongoing
{
if let Ok(msg_id) = chat::send_msg(context, chat_id, &mut msg) {
let msg_id = dc_send_msg(context, chat_id, msg);
if msg_id != 0 {
dc_msg_unref(msg);
msg = 0 as *mut dc_msg_t;
info!(context, 0, "Wait for setup message being sent ...",);
loop {
if context
@@ -168,12 +172,13 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
break;
}
std::thread::sleep(std::time::Duration::from_secs(1));
if let Ok(msg) = dc_get_msg(context, msg_id) {
if 0 != dc_msg_is_sent(&msg) {
info!(context, 0, "... setup message sent.",);
break;
}
msg = dc_get_msg(context, msg_id);
if 0 != dc_msg_is_sent(msg) {
info!(context, 0, "... setup message sent.",);
break;
}
dc_msg_unref(msg);
msg = 0 as *mut dc_msg_t
}
}
}
@@ -183,6 +188,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
}
}
free(setup_file_name as *mut libc::c_void);
dc_msg_unref(msg);
dc_free_ongoing(context);
setup_code.strdup()
@@ -279,17 +285,18 @@ pub unsafe fn dc_continue_key_transfer(
setup_code: *const libc::c_char,
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut filename: *mut libc::c_char = ptr::null_mut();
let mut filecontent: *mut libc::c_char = ptr::null_mut();
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut filecontent: *mut libc::c_char = 0 as *mut libc::c_char;
let mut filebytes: size_t = 0i32 as size_t;
let mut armored_key: *mut libc::c_char = ptr::null_mut();
let mut norm_sc: *mut libc::c_char = ptr::null_mut();
let mut armored_key: *mut libc::c_char = 0 as *mut libc::c_char;
let mut norm_sc: *mut libc::c_char = 0 as *mut libc::c_char;
if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) {
let msg = dc_get_msg(context, msg_id);
if msg.is_err()
|| !dc_msg_is_setupmessage(msg.as_ref().unwrap())
msg = dc_get_msg(context, msg_id);
if msg.is_null()
|| !dc_msg_is_setupmessage(msg)
|| {
filename = dc_msg_get_file(msg.as_ref().unwrap());
filename = dc_msg_get_file(msg);
filename.is_null()
}
|| *filename.offset(0isize) as libc::c_int == 0i32
@@ -325,6 +332,7 @@ pub unsafe fn dc_continue_key_transfer(
free(armored_key as *mut libc::c_void);
free(filecontent as *mut libc::c_void);
free(filename as *mut libc::c_void);
dc_msg_unref(msg);
free(norm_sc as *mut libc::c_void);
success
@@ -415,19 +423,19 @@ pub unsafe fn dc_decrypt_setup_file(
filecontent: *const libc::c_char,
) -> *mut libc::c_char {
let fc_buf: *mut libc::c_char;
let mut fc_headerline: *const libc::c_char = ptr::null();
let mut fc_base64: *const libc::c_char = ptr::null();
let mut binary: *mut libc::c_char = ptr::null_mut();
let mut fc_headerline: *const libc::c_char = 0 as *const libc::c_char;
let mut fc_base64: *const libc::c_char = 0 as *const libc::c_char;
let mut binary: *mut libc::c_char = 0 as *mut libc::c_char;
let mut binary_bytes: size_t = 0i32 as size_t;
let mut indx: size_t = 0i32 as size_t;
let mut payload: *mut libc::c_char = ptr::null_mut();
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
fc_buf = dc_strdup(filecontent);
if dc_split_armored_data(
fc_buf,
&mut fc_headerline,
ptr::null_mut(),
ptr::null_mut(),
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
&mut fc_base64,
) && !fc_headerline.is_null()
&& strcmp(
@@ -471,7 +479,7 @@ pub unsafe fn dc_normalize_setup_code(
in_0: *const libc::c_char,
) -> *mut libc::c_char {
if in_0.is_null() {
return ptr::null_mut();
return 0 as *mut libc::c_char;
}
let mut out = String::new();
let mut outlen;
@@ -499,7 +507,7 @@ pub unsafe fn dc_normalize_setup_code(
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) {
let mut ok_to_continue = true;
let mut success: libc::c_int = 0;
let mut ongoing_allocated_here: libc::c_int = 0;
@@ -507,10 +515,10 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
if !(0 == dc_alloc_ongoing(context)) {
ongoing_allocated_here = 1;
what = job.param.get_int(Param::Cmd).unwrap_or_default();
let param1_s = job.param.get(Param::Arg).unwrap_or_default();
what = (*job).param.get_int(Param::Cmd).unwrap_or_default();
let param1_s = (*job).param.get(Param::Arg).unwrap_or_default();
let param1 = CString::yolo(param1_s);
let _param2 = CString::yolo(job.param.get(Param::Arg2).unwrap_or_default());
let _param2 = CString::yolo((*job).param.get(Param::Arg2).unwrap_or_default());
if strlen(param1.as_ptr()) == 0 {
error!(context, 0, "No Import/export dir/file given.",);
@@ -588,10 +596,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
0,
"Import \"{}\" to \"{}\".",
as_str(backup_to_import),
context
.get_dbfile()
.as_ref()
.map_or("<<None>>", |p| p.to_str().unwrap())
as_str(context.get_dbfile()),
);
if 0 != dc_is_configured(context) {
@@ -599,8 +604,8 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
return 0;
}
&context.sql.close(&context);
dc_delete_file(context, context.get_dbfile().unwrap());
if dc_file_exist(context, context.get_dbfile().unwrap()) {
dc_delete_file(context, as_path(context.get_dbfile()));
if dc_file_exist(context, as_path(context.get_dbfile())) {
error!(
context,
0, "Cannot import backups: Cannot delete the old file.",
@@ -611,17 +616,13 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
if !dc_copy_file(
context,
as_path(backup_to_import),
context.get_dbfile().unwrap(),
as_path(context.get_dbfile()),
) {
return 0;
}
/* error already logged */
/* re-open copied database file */
if context
.sql
.open(&context, &context.get_dbfile().unwrap(), 0)
.is_err()
{
if !context.sql.open(&context, as_path(context.get_dbfile()), 0) {
return 0;
}
@@ -711,7 +712,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
/* the FILE_PROGRESS macro calls the callback with the permille of files processed.
The macro avoids weird values of 0% or 100% while still working. */
// TODO should return bool /rtn
#[allow(non_snake_case, unused_must_use)]
#[allow(non_snake_case)]
unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_int {
let mut current_block: u64;
let mut success: libc::c_int = 0;
@@ -740,25 +741,20 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
context,
0,
"Backup \"{}\" to \"{}\".",
context
.get_dbfile()
.as_ref()
.map_or("<<None>>", |p| p.to_str().unwrap()),
as_str(context.get_dbfile()),
as_str(dest_pathNfilename),
);
if dc_copy_file(
context,
context.get_dbfile().unwrap(),
as_path(context.get_dbfile()),
as_path(dest_pathNfilename),
) {
context
.sql
.open(&context, &context.get_dbfile().unwrap(), 0);
context.sql.open(&context, as_path(context.get_dbfile()), 0);
closed = false;
/* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
/*for logging only*/
let sql = Sql::new();
if sql.open(context, as_path(dest_pathNfilename), 0).is_ok() {
if sql.open(context, as_path(dest_pathNfilename), 0) {
if !sql.table_exists("backup_blobs") {
if sql::execute(
context,
@@ -909,9 +905,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
}
}
if closed {
context
.sql
.open(&context, &context.get_dbfile().unwrap(), 0);
context.sql.open(&context, as_path(context.get_dbfile()), 0);
}
if 0 != delete_dest_file {
dc_delete_file(context, as_path(dest_pathNfilename));
@@ -932,16 +926,16 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
Maybe we should make the "default" key handlong also a little bit smarter
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
let mut imported_cnt: libc::c_int = 0;
let mut suffix: *mut libc::c_char = ptr::null_mut();
let mut path_plus_name: *mut libc::c_char = ptr::null_mut();
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut set_default: libc::c_int;
let mut buf: *mut libc::c_char = ptr::null_mut();
let mut buf: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf_bytes: size_t = 0 as size_t;
// a pointer inside buf, MUST NOT be free()'d
let mut private_key: *const libc::c_char;
let mut buf2: *mut libc::c_char = ptr::null_mut();
let mut buf2: *mut libc::c_char = 0 as *mut libc::c_char;
// a pointer inside buf2, MUST NOT be free()'d
let mut buf2_headerline: *const libc::c_char = ptr::null_mut();
let mut buf2_headerline: *const libc::c_char = 0 as *const libc::c_char;
if !dir_name.is_null() {
let dir = std::path::Path::new(as_str(dir_name));
let dir_handle = std::fs::read_dir(dir);
@@ -962,7 +956,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
free(suffix as *mut libc::c_void);
let name_f = entry.file_name();
let name_c = name_f.to_c_string().unwrap();
suffix = dc_get_filesuffix_lc(name_f.to_string_lossy());
suffix = dc_get_filesuffix_lc(name_c.as_ptr());
if suffix.is_null()
|| strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0
{
@@ -976,7 +970,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
);
info!(context, 0, "Checking: {}", as_str(path_plus_name));
free(buf as *mut libc::c_void);
buf = ptr::null_mut();
buf = 0 as *mut libc::c_char;
if 0 == dc_read_file(
context,
path_plus_name,
@@ -1160,7 +1154,7 @@ mod tests {
#[test]
fn test_render_setup_file() {
let t = test_context(Some(Box::new(logging_cb)));
let t = test_context(Some(logging_cb));
configure_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
@@ -1178,9 +1172,14 @@ mod tests {
assert!(msg.contains("-----END PGP MESSAGE-----\n"));
}
fn ac_setup_msg_cb(ctx: &Context, evt: Event, d1: uintptr_t, d2: uintptr_t) -> uintptr_t {
unsafe extern "C" fn ac_setup_msg_cb(
ctx: &Context,
evt: Event,
d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() {
unsafe { "hello\r\nthere".strdup() as usize }
"hello\r\nthere".strdup() as usize
} else {
logging_cb(ctx, evt, d1, d2)
}
@@ -1188,7 +1187,7 @@ mod tests {
#[test]
fn test_render_setup_file_newline_replace() {
let t = test_context(Some(Box::new(ac_setup_msg_cb)));
let t = test_context(Some(ac_setup_msg_cb));
configure_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
println!("{}", &msg);

1149
src/dc_job.rs Normal file

File diff suppressed because it is too large Load Diff

209
src/dc_jobthread.rs Normal file
View File

@@ -0,0 +1,209 @@
use std::sync::{Arc, Condvar, Mutex};
use crate::context::Context;
use crate::dc_configure::*;
use crate::imap::Imap;
use crate::x::*;
#[repr(C)]
pub struct dc_jobthread_t {
pub name: &'static str,
pub folder_config_name: &'static str,
pub imap: Imap,
pub state: Arc<(Mutex<JobState>, Condvar)>,
}
pub fn dc_jobthread_init(
name: &'static str,
folder_config_name: &'static str,
imap: Imap,
) -> dc_jobthread_t {
dc_jobthread_t {
name,
folder_config_name,
imap,
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
}
}
#[derive(Debug, Default)]
pub struct JobState {
idle: bool,
jobs_needed: i32,
suspended: i32,
using_handle: i32,
}
pub unsafe fn dc_jobthread_suspend(
context: &Context,
jobthread: &dc_jobthread_t,
suspend: libc::c_int,
) {
if 0 != suspend {
info!(context, 0, "Suspending {}-thread.", jobthread.name,);
{
jobthread.state.0.lock().unwrap().suspended = 1;
}
dc_jobthread_interrupt_idle(context, jobthread);
loop {
let using_handle = jobthread.state.0.lock().unwrap().using_handle;
if using_handle == 0 {
return;
}
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
} else {
info!(context, 0, "Unsuspending {}-thread.", jobthread.name);
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
state.suspended = 0;
state.idle = true;
cvar.notify_one();
}
}
pub unsafe fn dc_jobthread_interrupt_idle(context: &Context, jobthread: &dc_jobthread_t) {
{
jobthread.state.0.lock().unwrap().jobs_needed = 1;
}
info!(context, 0, "Interrupting {}-IDLE...", jobthread.name);
jobthread.imap.interrupt_idle();
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
state.idle = true;
cvar.notify_one();
}
pub unsafe fn dc_jobthread_fetch(
context: &Context,
jobthread: &mut dc_jobthread_t,
use_network: libc::c_int,
) {
let start;
{
let &(ref lock, _) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
if 0 != state.suspended {
return;
}
state.using_handle = 1;
}
if 0 != use_network {
start = clock();
if !(0 == connect_to_imap(context, jobthread)) {
info!(context, 0, "{}-fetch started...", jobthread.name);
jobthread.imap.fetch(context);
if jobthread.imap.should_reconnect() {
info!(
context,
0, "{}-fetch aborted, starting over...", jobthread.name,
);
jobthread.imap.fetch(context);
}
info!(
context,
0,
"{}-fetch done in {:.3} ms.",
jobthread.name,
clock().wrapping_sub(start) as f64 / 1000.0,
);
}
}
jobthread.state.0.lock().unwrap().using_handle = 0;
}
/* ******************************************************************************
* the typical fetch, idle, interrupt-idle
******************************************************************************/
unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int {
if jobthread.imap.is_connected() {
return 1;
}
let mut ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
if !(0 == ret_connected) {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
jobthread.imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) = context
.sql
.get_config(context, jobthread.folder_config_name)
{
jobthread.imap.set_watch_folder(mvbox_name);
} else {
jobthread.imap.disconnect(context);
ret_connected = 0;
}
}
ret_connected
}
pub unsafe fn dc_jobthread_idle(
context: &Context,
jobthread: &dc_jobthread_t,
use_network: libc::c_int,
) {
{
let &(ref lock, ref cvar) = &*jobthread.state.clone();
let mut state = lock.lock().unwrap();
if 0 != state.jobs_needed {
info!(
context,
0,
"{}-IDLE will not be started as it was interrupted while not ideling.",
jobthread.name,
);
state.jobs_needed = 0;
return;
}
if 0 != state.suspended {
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
state.using_handle = 1;
if 0 == use_network {
state.using_handle = 0;
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
}
connect_to_imap(context, jobthread);
info!(context, 0, "{}-IDLE started...", jobthread.name,);
jobthread.imap.idle(context);
info!(context, 0, "{}-IDLE ended.", jobthread.name);
jobthread.state.0.lock().unwrap().using_handle = 0;
}

View File

@@ -1,13 +1,14 @@
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use std::ffi::CString;
use crate::chat;
use crate::constants::Event;
use crate::constants::*;
use crate::context::*;
use crate::dc_array::*;
use crate::dc_chat::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::job::*;
use crate::message::*;
use crate::param::*;
use crate::sql;
use crate::stock::StockMessage;
@@ -68,11 +69,15 @@ impl dc_kml_t {
}
// location streaming
pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, seconds: i64) {
pub unsafe fn dc_send_locations_to_chat(
context: &Context,
chat_id: uint32_t,
seconds: libc::c_int,
) {
let now = time();
let mut msg: Message;
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) {
if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) {
is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id);
if sql::execute(
context,
@@ -83,7 +88,11 @@ pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, se
WHERE id=?",
params![
if 0 != seconds { now } else { 0 },
if 0 != seconds { now + seconds } else { 0 },
if 0 != seconds {
now + seconds as i64
} else {
0
},
chat_id as i32,
],
)
@@ -91,14 +100,19 @@ pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, se
{
if 0 != seconds && !is_sending_locations_before {
msg = dc_msg_new(context, Viewtype::Text);
msg.text =
(*msg).text =
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
msg.param.set_int(Param::Cmd, 8);
chat::send_msg(context, chat_id, &mut msg).unwrap();
(*msg).param.set_int(Param::Cmd, 8);
dc_send_msg(context, chat_id, msg);
} else if 0 == seconds && is_sending_locations_before {
let stock_str =
context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
chat::add_device_msg(context, chat_id, stock_str);
let stock_str = CString::new(context.stock_system_msg(
StockMessage::MsgLocationDisabled,
"",
"",
0,
))
.unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
}
context.call_cb(
Event::CHAT_MODIFIED,
@@ -107,16 +121,17 @@ pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, se
);
if 0 != seconds {
schedule_MAYBE_SEND_LOCATIONS(context, 0i32);
job_add(
dc_job_add(
context,
Action::MaybeSendLocationsEnded,
5007i32,
chat_id as libc::c_int,
Params::new(),
seconds + 1,
seconds + 1i32,
);
}
}
}
dc_msg_unref(msg);
}
/*******************************************************************************
@@ -124,8 +139,8 @@ pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, se
******************************************************************************/
#[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);
if 0 != flags & 0x1 || !dc_job_action_exists(context, 5005) {
dc_job_add(context, 5005, 0, Params::new(), 60);
};
}
@@ -186,7 +201,7 @@ pub fn dc_get_locations(
contact_id: uint32_t,
timestamp_from: i64,
mut timestamp_to: i64,
) -> Vec<dc_location> {
) -> *mut dc_array_t {
if timestamp_to == 0 {
timestamp_to = time() + 10;
}
@@ -224,10 +239,10 @@ pub fn dc_get_locations(
accuracy: row.get(3)?,
timestamp: row.get(4)?,
independent: row.get(5)?,
msg_id,
msg_id: msg_id,
contact_id: row.get(7)?,
chat_id: row.get(8)?,
marker,
marker: marker,
};
Ok(loc)
},
@@ -237,10 +252,10 @@ pub fn dc_get_locations(
for location in locations {
ret.push(location?);
}
Ok(ret)
Ok(dc_array_t::from(ret).into_raw())
},
)
.unwrap_or_default()
.unwrap_or_else(|_| std::ptr::null_mut())
}
fn is_marker(txt: &str) -> bool {
@@ -464,159 +479,165 @@ pub unsafe fn dc_kml_parse(
content_bytes: size_t,
) -> dc_kml_t {
let mut kml = dc_kml_t::new();
let mut content_nullterminated: *mut libc::c_char = 0 as *mut libc::c_char;
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
if content_bytes > (1 * 1024 * 1024) {
warn!(
context,
0, "A kml-files with {} bytes is larger than reasonably expected.", content_bytes,
);
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();
} else {
content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int);
if !content_nullterminated.is_null() {
kml.locations = Some(Vec::with_capacity(100));
dc_saxparser_init(
&mut saxparser,
&mut kml as *mut dc_kml_t as *mut libc::c_void,
);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(kml_starttag_cb),
Some(kml_endtag_cb),
);
dc_saxparser_set_text_handler(&mut saxparser, Some(kml_text_cb));
dc_saxparser_parse(&mut saxparser, content_nullterminated);
}
}
free(content_null.cast());
free(content_nullterminated as *mut libc::c_void);
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 {
unsafe fn kml_text_cb(userdata: *mut libc::c_void, text: *const libc::c_char, _len: libc::c_int) {
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
if 0 != (*kml).tag & (0x4 | 0x10) {
let mut val: *mut libc::c_char = dc_strdup(text);
dc_str_replace(
&mut val,
b"\n\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_str_replace(
&mut val,
b"\r\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_str_replace(
&mut val,
b"\t\x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
dc_str_replace(
&mut val,
b" \x00" as *const u8 as *const libc::c_char,
b"\x00" as *const u8 as *const libc::c_char,
);
if 0 != (*kml).tag & 0x4 && strlen(val) >= 19 {
// YYYY-MM-DDTHH:MM:SSZ
// 0 4 7 10 13 16 19
match chrono::NaiveDateTime::parse_from_str(&val, "%Y-%m-%dT%H:%M:%SZ") {
let val_r = as_str(val);
match chrono::NaiveDateTime::parse_from_str(val_r, "%Y-%m-%dT%H:%M:%SZ") {
Ok(res) => {
kml.curr.timestamp = res.timestamp();
if kml.curr.timestamp > time() {
kml.curr.timestamp = time();
(*kml).curr.timestamp = res.timestamp();
if (*kml).curr.timestamp > time() {
(*kml).curr.timestamp = time();
}
}
Err(_err) => {
kml.curr.timestamp = time();
(*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();
} else if 0 != (*kml).tag & 0x10 {
let mut comma: *mut libc::c_char = strchr(val, ',' as i32);
if !comma.is_null() {
let longitude: *mut libc::c_char = val;
let latitude: *mut libc::c_char = comma.offset(1isize);
*comma = 0 as libc::c_char;
comma = strchr(latitude, ',' as i32);
if !comma.is_null() {
*comma = 0 as libc::c_char
}
(*kml).curr.latitude = dc_atof(latitude);
(*kml).curr.longitude = dc_atof(longitude)
}
}
}
}
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
free(val as *mut libc::c_void);
};
}
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()
};
unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
if 0 != (*kml).tag & 0x1
&& 0 != (*kml).curr.timestamp
&& 0. != (*kml).curr.latitude
&& 0. != (*kml).curr.longitude
{
let location = (*kml).curr.clone();
((*kml).locations.as_mut().unwrap()).push(location);
}
} 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).tag = 0
};
}
kml.curr.accuracy = v.trim().parse().unwrap_or_default();
/*******************************************************************************
* parse kml-files
******************************************************************************/
unsafe fn kml_starttag_cb(
userdata: *mut libc::c_void,
tag: *const libc::c_char,
attr: *mut *mut libc::c_char,
) {
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
if strcmp(tag, b"document\x00" as *const u8 as *const libc::c_char) == 0 {
let addr: *const libc::c_char =
dc_attr_find(attr, b"addr\x00" as *const u8 as *const libc::c_char);
if !addr.is_null() {
(*kml).addr = dc_strdup(addr)
}
}
} else if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
(*kml).tag = 0x1;
(*kml).curr.timestamp = 0;
(*kml).curr.latitude = 0 as libc::c_double;
(*kml).curr.longitude = 0.0f64;
(*kml).curr.accuracy = 0.0f64
} else if strcmp(tag, b"timestamp\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x1
{
(*kml).tag = 0x1 | 0x2
} else if strcmp(tag, b"when\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x2
{
(*kml).tag = 0x1 | 0x2 | 0x4
} else if strcmp(tag, b"point\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x1
{
(*kml).tag = 0x1 | 0x8
} else if strcmp(tag, b"coordinates\x00" as *const u8 as *const libc::c_char) == 0
&& 0 != (*kml).tag & 0x8
{
(*kml).tag = 0x1 | 0x8 | 0x10;
let accuracy: *const libc::c_char =
dc_attr_find(attr, b"accuracy\x00" as *const u8 as *const libc::c_char);
if !accuracy.is_null() {
(*kml).curr.accuracy = dc_atof(accuracy)
}
};
}
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
free(kml.addr as *mut libc::c_void);
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) {
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mut dc_job_t) {
let now = time();
let mut continue_streaming: libc::c_int = 1;
info!(
@@ -679,10 +700,10 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Jo
// (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
chat::send_msg(context, chat_id as u32, &mut msg).unwrap();
(*msg).hidden = 1;
(*msg).param.set_int(Param::Cmd, 9);
dc_send_msg(context, chat_id as u32, msg);
dc_msg_unref(msg);
}
Ok(())
},
@@ -697,7 +718,7 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Jo
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) {
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut dc_job_t) {
// this function is called when location-streaming _might_ have ended for a chat.
// the function checks, if location-streaming is really ended;
// if so, a device-message is added if not yet done.
@@ -719,8 +740,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
"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);
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
context.call_cb(
Event::CHAT_MODIFIED,
chat_id as usize,
@@ -731,46 +752,3 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
}
}
}
#[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);
}
}
}

181
src/dc_lot.rs Normal file
View File

@@ -0,0 +1,181 @@
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
/* * Structure behind dc_lot_t */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_lot_t {
pub text1_meaning: libc::c_int,
pub text1: *mut libc::c_char,
pub text2: *mut libc::c_char,
pub timestamp: i64,
pub state: libc::c_int,
pub id: uint32_t,
pub fingerprint: *mut libc::c_char,
pub invitenumber: *mut libc::c_char,
pub auth: *mut libc::c_char,
}
/* *
* @class dc_lot_t
*
* An object containing a set of values.
* The meaning of the values is defined by the function returning the object.
* Lot objects are created
* eg. by chatlist.get_summary() or dc_msg_get_summary().
*
* NB: _Lot_ is used in the meaning _heap_ here.
*/
pub unsafe fn dc_lot_new() -> *mut dc_lot_t {
let mut lot: *mut dc_lot_t;
lot = calloc(1, ::std::mem::size_of::<dc_lot_t>()) as *mut dc_lot_t;
assert!(!lot.is_null());
(*lot).text1_meaning = 0i32;
lot
}
pub unsafe fn dc_lot_empty(mut lot: *mut dc_lot_t) {
if lot.is_null() {
return;
}
free((*lot).text1 as *mut libc::c_void);
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32;
free((*lot).text2 as *mut libc::c_void);
(*lot).text2 = 0 as *mut libc::c_char;
free((*lot).fingerprint as *mut libc::c_void);
(*lot).fingerprint = 0 as *mut libc::c_char;
free((*lot).invitenumber as *mut libc::c_void);
(*lot).invitenumber = 0 as *mut libc::c_char;
free((*lot).auth as *mut libc::c_void);
(*lot).auth = 0 as *mut libc::c_char;
(*lot).timestamp = 0;
(*lot).state = 0i32;
(*lot).id = 0i32 as uint32_t;
}
pub unsafe fn dc_lot_unref(set: *mut dc_lot_t) {
if set.is_null() {
return;
}
dc_lot_empty(set);
free(set as *mut libc::c_void);
}
pub unsafe fn dc_lot_get_text1(lot: *const dc_lot_t) -> *mut libc::c_char {
if lot.is_null() {
return 0 as *mut libc::c_char;
}
dc_strdup_keep_null((*lot).text1)
}
pub unsafe fn dc_lot_get_text2(lot: *const dc_lot_t) -> *mut libc::c_char {
if lot.is_null() {
return 0 as *mut libc::c_char;
}
dc_strdup_keep_null((*lot).text2)
}
pub unsafe fn dc_lot_get_text1_meaning(lot: *const dc_lot_t) -> libc::c_int {
if lot.is_null() {
return 0i32;
}
(*lot).text1_meaning
}
pub unsafe fn dc_lot_get_state(lot: *const dc_lot_t) -> libc::c_int {
if lot.is_null() {
return 0i32;
}
(*lot).state
}
pub unsafe fn dc_lot_get_id(lot: *const dc_lot_t) -> uint32_t {
if lot.is_null() {
return 0i32 as uint32_t;
}
(*lot).id
}
pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
if lot.is_null() {
return 0;
}
(*lot).timestamp
}
/* library-internal */
/* in practice, the user additionally cuts the string himself pixel-accurate */
pub unsafe fn dc_lot_fill(
mut lot: *mut dc_lot_t,
msg: *mut dc_msg_t,
chat: *const Chat,
contact: Option<&Contact>,
context: &Context,
) {
if lot.is_null() || msg.is_null() {
return;
}
if (*msg).state == 19i32 {
(*lot).text1 = context.stock_str(StockMessage::Draft).strdup();
(*lot).text1_meaning = 1i32
} else if (*msg).from_id == 1i32 as libc::c_uint {
if 0 != dc_msg_is_info(msg) || 0 != dc_chat_is_self_talk(chat) {
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32
} else {
(*lot).text1 = context.stock_str(StockMessage::SelfMsg).strdup();
(*lot).text1_meaning = 3i32
}
} else if chat.is_null() {
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32
} else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
if 0 != dc_msg_is_info(msg) || contact.is_none() {
(*lot).text1 = 0 as *mut libc::c_char;
(*lot).text1_meaning = 0i32
} else {
if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint {
if let Some(contact) = contact {
(*lot).text1 = contact.get_display_name().strdup();
} else {
(*lot).text1 = std::ptr::null_mut();
}
} else {
if let Some(contact) = contact {
(*lot).text1 = contact.get_first_name().strdup();
} else {
(*lot).text1 = std::ptr::null_mut();
}
}
(*lot).text1_meaning = 2i32;
}
}
let message_text = (*msg).text.as_ref().unwrap();
(*lot).text2 = dc_msg_get_summarytext_by_raw(
(*msg).type_0,
message_text.strdup(),
&mut (*msg).param,
160,
context,
);
(*lot).timestamp = dc_msg_get_timestamp(msg);
(*lot).state = (*msg).state;
}

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,11 @@ pub struct dc_mimeparser_t<'a> {
pub message_kml: Option<dc_kml_t>,
}
// deprecated
pub unsafe fn dc_no_compound_msgs() {
S_GENERATE_COMPOUND_MSGS = 0i32;
}
// deprecated: flag to switch generation of compound messages on and off.
static mut S_GENERATE_COMPOUND_MSGS: libc::c_int = 1i32;
@@ -88,7 +93,7 @@ pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) {
dc_mimeparser_empty(mimeparser);
}
unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
pub unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
for part in mimeparser.parts.drain(..) {
dc_mimepart_unref(part);
}
@@ -1077,7 +1082,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
mimeparser: &mut dc_mimeparser_t,
mime: *mut mailmime,
) -> libc::c_int {
let mut ok_to_continue = true;
let mut current_block: u64;
let old_part_count = mimeparser.parts.len();
let mime_type: libc::c_int;
let mime_data: *mut mailmime_data;
@@ -1134,12 +1139,14 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
info!(mimeparser.context, 0, "decoded message: '{}'", res);
if res.is_empty() {
/* no error - but nothing to add */
ok_to_continue = false;
current_block = 8795901732489102124;
} else {
let b = res.as_bytes();
decoded_data = b.as_ptr() as *const libc::c_char;
decoded_data_bytes = b.len();
std::mem::forget(res);
current_block = 17788412896529399552;
}
} else {
warn!(
@@ -1149,35 +1156,45 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
decoded_data_bytes as libc::c_int,
as_str(charset),
);
current_block = 17788412896529399552;
}
} else {
current_block = 17788412896529399552;
}
if ok_to_continue {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg =
(!dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version")
.is_null()) as libc::c_int;
match current_block {
8795901732489102124 => {}
_ => {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg = (!dc_mimeparser_lookup_optional_field(
&mimeparser,
"Chat-Version",
)
.is_null())
as libc::c_int;
let simplified_txt = simplifier.unwrap().simplify(
decoded_data,
decoded_data_bytes as libc::c_int,
mime_type == 70i32,
is_msgrmsg,
);
if !simplified_txt.is_null()
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
{
let mut part = dc_mimepart_new();
part.type_0 = 10i32;
part.int_mimetype = mime_type;
part.msg = simplified_txt;
part.msg_raw =
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
do_add_single_part(mimeparser, part);
} else {
free(simplified_txt as *mut libc::c_void);
}
if simplifier.unwrap().is_forwarded {
mimeparser.is_forwarded = 1i32
let simplified_txt = simplifier.unwrap().simplify(
decoded_data,
decoded_data_bytes as libc::c_int,
mime_type == 70i32,
is_msgrmsg,
);
if !simplified_txt.is_null()
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
{
let mut part = dc_mimepart_new();
part.type_0 = 10i32;
part.int_mimetype = mime_type;
part.msg = simplified_txt;
part.msg_raw =
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
do_add_single_part(mimeparser, part);
} else {
free(simplified_txt as *mut libc::c_void);
}
if simplifier.unwrap().is_forwarded {
mimeparser.is_forwarded = 1i32
}
current_block = 10261677128829721533;
}
}
}
@@ -1280,61 +1297,76 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
b"file.%s\x00" as *const u8 as *const libc::c_char,
(*(*mime).mm_content_type).ct_subtype,
);
current_block = 17019156190352891614;
} else {
ok_to_continue = false;
current_block = 8795901732489102124;
}
} else {
current_block = 17019156190352891614;
}
if ok_to_continue {
if strncmp(
desired_filename,
b"location\x00" as *const u8 as *const libc::c_char,
8,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
mimeparser.location_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
} else if strncmp(
desired_filename,
b"message\x00" as *const u8 as *const libc::c_char,
7,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
mimeparser.message_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
} else {
dc_replace_bad_utf8_chars(desired_filename);
do_add_single_file_part(
mimeparser,
msg_type,
mime_type,
raw_mime,
decoded_data,
decoded_data_bytes,
match current_block {
8795901732489102124 => {}
_ => {
if strncmp(
desired_filename,
);
b"location\x00" as *const u8 as *const libc::c_char,
8,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
mimeparser.location_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
current_block = 8795901732489102124;
} else if strncmp(
desired_filename,
b"message\x00" as *const u8 as *const libc::c_char,
7,
) == 0i32
&& strncmp(
desired_filename
.offset(strlen(desired_filename) as isize)
.offset(-4isize),
b".kml\x00" as *const u8 as *const libc::c_char,
4,
) == 0i32
{
mimeparser.message_kml = Some(dc_kml_parse(
mimeparser.context,
decoded_data,
decoded_data_bytes,
));
current_block = 8795901732489102124;
} else {
dc_replace_bad_utf8_chars(desired_filename);
do_add_single_file_part(
mimeparser,
msg_type,
mime_type,
raw_mime,
decoded_data,
decoded_data_bytes,
desired_filename,
);
current_block = 10261677128829721533;
}
}
}
}
_ => {
current_block = 10261677128829721533;
}
}
match current_block {
8795901732489102124 => {}
_ => {}
}
}
@@ -1347,7 +1379,11 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
free(file_suffix as *mut libc::c_void);
free(desired_filename as *mut libc::c_void);
free(raw_mime as *mut libc::c_void);
(mimeparser.parts.len() > old_part_count) as libc::c_int
return if mimeparser.parts.len() > old_part_count {
1
} else {
0
};
}
#[allow(non_snake_case)]
@@ -1383,15 +1419,16 @@ unsafe fn do_add_single_file_part(
part.param.set(Param::File, as_str(pathNfilename));
part.param.set(Param::MimeType, as_str(raw_mime));
if mime_type == 80 {
assert!(!decoded_data.is_null(), "invalid image data");
let data = std::slice::from_raw_parts(
decoded_data as *const u8,
decoded_data_bytes as usize,
);
if let Ok((width, height)) = dc_get_filemeta(data) {
part.param.set_int(Param::Width, width as i32);
part.param.set_int(Param::Height, height as i32);
let mut w = 0;
let mut h = 0;
if 0 != dc_get_filemeta(
decoded_data as *const libc::c_void,
decoded_data_bytes,
&mut w,
&mut h,
) {
part.param.set_int(Param::Width, w as i32);
part.param.set_int(Param::Height, h as i32);
}
}
do_add_single_part(parser, part);

View File

@@ -1,7 +1,7 @@
use crate::constants::*;
use crate::context::*;
use crate::job::*;
use crate::message::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::param::Params;
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
@@ -18,27 +18,23 @@ pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u3
return;
}
if let Ok(msg) = dc_msg_new_load(context, msg_id) {
if dc_msg_is_setupmessage(&msg) {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
return;
}
if dc_is_mvbox(context, folder) {
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Stay);
}
// 1 = dc message, 2 = reply to dc message
if 0 != msg.is_dc_message {
job_add(
context,
Action::MoveMsg,
msg.id as libc::c_int,
Params::new(),
0,
);
dc_update_msg_move_state(context, msg.rfc724_mid, MoveState::Moving);
}
let msg = dc_msg_new_load(context, msg_id);
if dc_msg_is_setupmessage(msg) {
// do not move setup messages;
// there may be a non-delta device that wants to handle it
dc_msg_unref(msg);
return;
}
if dc_is_mvbox(context, folder) {
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Stay);
}
// 1 = dc message, 2 = reply to dc message
if 0 != (*msg).is_dc_message {
dc_job_add(context, 200, (*msg).id as libc::c_int, Params::new(), 0);
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Moving);
}
dc_msg_unref(msg);
}

1591
src/dc_msg.rs Normal file

File diff suppressed because it is too large Load Diff

346
src/dc_qr.rs Normal file
View File

@@ -0,0 +1,346 @@
use percent_encoding::percent_decode_str;
use crate::contact::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_lot::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::param::*;
use crate::peerstate::*;
use crate::types::*;
use crate::x::*;
// out-of-band verification
// id=contact
// text1=groupname
// id=contact
// id=contact
// test1=formatted fingerprint
// id=contact
// text1=text
// text1=URL
// text1=error string
pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc_lot_t {
let mut current_block: u64;
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
// must be normalized, if set
let mut addr: *mut libc::c_char = 0 as *mut libc::c_char;
// must be normalized, if set
let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
let mut name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut invitenumber: *mut libc::c_char = 0 as *mut libc::c_char;
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
let mut qr_parsed: *mut dc_lot_t = dc_lot_new();
let mut chat_id: uint32_t = 0i32 as uint32_t;
let mut device_msg: *mut libc::c_char = 0 as *mut libc::c_char;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char;
(*qr_parsed).state = 0i32;
if !qr.is_null() {
info!(context, 0, "Scanned QR code: {}", as_str(qr),);
/* split parameters from the qr code
------------------------------------ */
if strncasecmp(
qr,
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
strlen(b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
payload =
dc_strdup(&*qr.offset(strlen(
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
) as isize));
let mut fragment: *mut libc::c_char = strchr(payload, '#' as i32);
if !fragment.is_null() {
*fragment = 0i32 as libc::c_char;
fragment = fragment.offset(1isize);
let param: Params = as_str(fragment).parse().expect("invalid params");
addr = param
.get(Param::Forwarded)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
if !addr.is_null() {
if let Some(ref name_enc) = param.get(Param::SetLongitude) {
let name_r = percent_decode_str(name_enc)
.decode_utf8()
.expect("invalid name");
name = normalize_name(name_r).strdup();
}
invitenumber = param
.get(Param::ProfileImage)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
auth = param
.get(Param::Auth)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
grpid = param
.get(Param::GroupId)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
if !grpid.is_null() {
if let Some(grpname_enc) = param.get(Param::GroupName) {
let grpname_r = percent_decode_str(grpname_enc)
.decode_utf8()
.expect("invalid groupname");
grpname = grpname_r.strdup();
}
}
}
}
fingerprint = dc_normalize_fingerprint_c(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"mailto:\x00" as *const u8 as *const libc::c_char,
strlen(b"mailto:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
payload = dc_strdup(
&*qr.offset(strlen(b"mailto:\x00" as *const u8 as *const libc::c_char) as isize),
);
let query: *mut libc::c_char = strchr(payload, '?' as i32);
if !query.is_null() {
*query = 0i32 as libc::c_char
}
addr = dc_strdup(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"SMTP:\x00" as *const u8 as *const libc::c_char,
strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
payload = dc_strdup(
&*qr.offset(strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char) as isize),
);
let colon: *mut libc::c_char = strchr(payload, ':' as i32);
if !colon.is_null() {
*colon = 0i32 as libc::c_char
}
addr = dc_strdup(payload);
current_block = 5023038348526654800;
} else if strncasecmp(
qr,
b"MATMSG:\x00" as *const u8 as *const libc::c_char,
strlen(b"MATMSG:\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
/* scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` - there may or may not be linebreaks after the fields */
/* does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. we ignore this case. */
let to: *mut libc::c_char = strstr(qr, b"TO:\x00" as *const u8 as *const libc::c_char);
if !to.is_null() {
addr = dc_strdup(&mut *to.offset(3isize));
let semicolon: *mut libc::c_char = strchr(addr, ';' as i32);
if !semicolon.is_null() {
*semicolon = 0i32 as libc::c_char
}
current_block = 5023038348526654800;
} else {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 =
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
current_block = 16562876845594826114;
}
} else {
if strncasecmp(
qr,
b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char,
strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
let lines = dc_split_into_lines(qr);
for &key in &lines {
dc_trim(key);
let mut value: *mut libc::c_char = strchr(key, ':' as i32);
if !value.is_null() {
*value = 0i32 as libc::c_char;
value = value.offset(1isize);
let mut semicolon_0: *mut libc::c_char = strchr(key, ';' as i32);
if !semicolon_0.is_null() {
*semicolon_0 = 0i32 as libc::c_char
}
if strcasecmp(key, b"EMAIL\x00" as *const u8 as *const libc::c_char) == 0i32
{
semicolon_0 = strchr(value, ';' as i32);
if !semicolon_0.is_null() {
*semicolon_0 = 0i32 as libc::c_char
}
addr = dc_strdup(value)
} else if strcasecmp(key, b"N\x00" as *const u8 as *const libc::c_char)
== 0i32
{
semicolon_0 = strchr(value, ';' as i32);
if !semicolon_0.is_null() {
semicolon_0 = strchr(semicolon_0.offset(1isize), ';' as i32);
if !semicolon_0.is_null() {
*semicolon_0 = 0i32 as libc::c_char
}
}
name = dc_strdup(value);
dc_str_replace(
&mut name,
b";\x00" as *const u8 as *const libc::c_char,
b",\x00" as *const u8 as *const libc::c_char,
);
name = normalize_name(as_str(name)).strdup();
}
}
}
dc_free_splitted_lines(lines);
}
current_block = 5023038348526654800;
}
match current_block {
16562876845594826114 => {}
_ => {
/* check the parameters
---------------------- */
if !addr.is_null() {
/* urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases */
let mut temp: *mut libc::c_char = dc_urldecode(addr);
free(addr as *mut libc::c_void);
addr = temp;
temp = addr_normalize(as_str(addr)).strdup();
free(addr as *mut libc::c_void);
addr = temp;
if !may_be_valid_addr(as_str(addr)) {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char,
);
current_block = 16562876845594826114;
} else {
current_block = 14116432890150942211;
}
} else {
current_block = 14116432890150942211;
}
match current_block {
16562876845594826114 => {}
_ => {
if !fingerprint.is_null() {
if strlen(fingerprint) != 40 {
(*qr_parsed).state = 400i32;
(*qr_parsed).text1 = dc_strdup(
b"Bad fingerprint length in QR code.\x00" as *const u8
as *const libc::c_char,
);
current_block = 16562876845594826114;
} else {
current_block = 5409161009579131794;
}
} else {
current_block = 5409161009579131794;
}
match current_block {
16562876845594826114 => {}
_ => {
if !fingerprint.is_null() {
let peerstate = Peerstate::from_fingerprint(
context,
&context.sql,
as_str(fingerprint),
);
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
if let Some(peerstate) = peerstate {
(*qr_parsed).state = 210i32;
let addr = peerstate
.addr
.as_ref()
.map(|s| s.as_str())
.unwrap_or_else(|| "");
(*qr_parsed).id = Contact::add_or_lookup(
context,
"",
addr,
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
dc_create_or_lookup_nchat_by_contact_id(
context,
(*qr_parsed).id,
2i32,
&mut chat_id,
0 as *mut libc::c_int,
);
device_msg = dc_mprintf(
b"%s verified.\x00" as *const u8
as *const libc::c_char,
peerstate.addr,
)
} else {
(*qr_parsed).text1 =
dc_format_fingerprint_c(fingerprint);
(*qr_parsed).state = 230i32
}
} else {
if !grpid.is_null() && !grpname.is_null() {
(*qr_parsed).state = 202i32;
(*qr_parsed).text1 = dc_strdup(grpname);
(*qr_parsed).text2 = dc_strdup(grpid)
} else {
(*qr_parsed).state = 200i32
}
(*qr_parsed).id = Contact::add_or_lookup(
context,
as_str(name),
as_str(addr),
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
(*qr_parsed).auth = dc_strdup(auth)
}
} else if !addr.is_null() {
(*qr_parsed).state = 320i32;
(*qr_parsed).id = Contact::add_or_lookup(
context,
as_str(name),
as_str(addr),
Origin::UnhandledQrScan,
)
.map(|(id, _)| id)
.unwrap_or_default();
} else if strstr(
qr,
b"http://\x00" as *const u8 as *const libc::c_char,
) == qr as *mut libc::c_char
|| strstr(
qr,
b"https://\x00" as *const u8 as *const libc::c_char,
) == qr as *mut libc::c_char
{
(*qr_parsed).state = 332i32;
(*qr_parsed).text1 = dc_strdup(qr)
} else {
(*qr_parsed).state = 330i32;
(*qr_parsed).text1 = dc_strdup(qr)
}
if !device_msg.is_null() {
dc_add_device_msg(context, chat_id, device_msg);
}
}
}
}
}
}
}
}
free(addr as *mut libc::c_void);
free(fingerprint as *mut libc::c_void);
free(payload as *mut libc::c_void);
free(name as *mut libc::c_void);
free(invitenumber as *mut libc::c_void);
free(auth as *mut libc::c_void);
free(device_msg as *mut libc::c_void);
free(grpname as *mut libc::c_void);
free(grpid as *mut libc::c_void);
qr_parsed
}

File diff suppressed because it is too large Load Diff

1089
src/dc_saxparser.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,25 @@
use std::ffi::CString;
use mmime::mailimf_types::*;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
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_chat::*;
use crate::dc_configure::*;
use crate::dc_e2ee::*;
use crate::dc_lot::*;
use crate::dc_mimeparser::*;
use crate::dc_msg::*;
use crate::dc_qr::*;
use crate::dc_strencode::*;
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;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
@@ -33,6 +36,9 @@ pub unsafe fn dc_get_securejoin_qr(
let mut fingerprint = 0 as *mut libc::c_char;
let mut invitenumber: *mut libc::c_char;
let mut auth: *mut libc::c_char;
let mut chat = 0 as *mut Chat;
let mut group_name = 0 as *mut libc::c_char;
let mut group_name_urlencoded = 0 as *mut libc::c_char;
let mut qr: Option<String> = None;
dc_ensure_secret_key_exists(context).ok();
@@ -48,10 +54,13 @@ pub unsafe fn dc_get_securejoin_qr(
}
let self_addr = context.sql.get_config(context, "configured_addr");
let cleanup = |fingerprint| {
let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| {
free(fingerprint as *mut libc::c_void);
free(invitenumber as *mut libc::c_void);
free(auth as *mut libc::c_void);
dc_chat_unref(chat);
free(group_name as *mut libc::c_void);
free(group_name_urlencoded as *mut libc::c_void);
if let Some(qr) = qr {
qr.strdup()
@@ -62,7 +71,7 @@ pub unsafe fn dc_get_securejoin_qr(
if self_addr.is_none() {
error!(context, 0, "Not configured, cannot generate QR code.",);
return cleanup(fingerprint);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
let self_addr = self_addr.unwrap();
@@ -74,34 +83,34 @@ pub unsafe fn dc_get_securejoin_qr(
fingerprint = get_self_fingerprint(context);
if fingerprint.is_null() {
return cleanup(fingerprint);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string();
qr = if 0 != group_chat_id {
if let Ok(chat) = Chat::load_from_db(context, group_chat_id) {
let group_name = chat.get_name();
let group_name_urlencoded =
utf8_percent_encode(&group_name, NON_ALPHANUMERIC).to_string();
Some(format!(
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
&group_name_urlencoded,
&chat.grpid,
as_str(invitenumber),
as_str(auth),
))
} else {
chat = dc_get_chat(context, group_chat_id);
if chat.is_null() {
error!(
context,
0, "Cannot get QR-code for chat-id {}", group_chat_id,
);
return cleanup(fingerprint);
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
group_name = dc_chat_get_name(chat);
group_name_urlencoded = dc_urlencode(group_name);
Some(format!(
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
as_str(fingerprint),
self_addr_urlencoded,
as_str(group_name_urlencoded),
as_str((*chat).grpid),
as_str(invitenumber),
as_str(auth),
))
} else {
Some(format!(
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
@@ -115,7 +124,7 @@ pub unsafe fn dc_get_securejoin_qr(
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
cleanup(fingerprint)
cleanup(fingerprint, chat, group_name, group_name_urlencoded)
}
fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
@@ -137,19 +146,16 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
let ongoing_allocated: libc::c_int;
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut join_vg: libc::c_int = 0i32;
let mut qr_scan: *mut dc_lot_t = 0 as *mut dc_lot_t;
info!(context, 0, "Requesting secure-join ...",);
dc_ensure_secret_key_exists(context).ok();
ongoing_allocated = dc_alloc_ongoing(context);
if !(ongoing_allocated == 0i32) {
let qr_scan = check_qr(context, as_str(qr));
if qr_scan.state != LotState::QrAskVerifyContact
&& qr_scan.state != LotState::QrAskVerifyGroup
{
qr_scan = dc_check_qr(context, qr);
if qr_scan.is_null() || (*qr_scan).state != 200i32 && (*qr_scan).state != 202i32 {
error!(context, 0, "Unknown QR code.",);
} else {
contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default();
contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id);
if contact_chat_id == 0i32 as libc::c_uint {
error!(context, 0, "Unknown contact.",);
} else if !(context
@@ -159,28 +165,17 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
.unwrap()
.shall_stop_ongoing)
{
join_vg = (qr_scan.get_state() == LotState::QrAskVerifyGroup) as libc::c_int;
join_vg = ((*qr_scan).state == 202i32) as libc::c_int;
{
let mut bob = context.bob.write().unwrap();
let bob_a = context.bob.clone();
let mut bob = bob_a.write().unwrap();
bob.status = 0;
bob.qr_scan = Some(qr_scan);
bob.qr_scan = qr_scan;
}
if 0 != fingerprint_equals_sender(
context,
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap(),
contact_chat_id,
) {
if 0 != fingerprint_equals_sender(context, (*qr_scan).fingerprint, contact_chat_id)
{
info!(context, 0, "Taking protocol shortcut.");
context.bob.write().unwrap().expects = 6;
context.bob.clone().write().unwrap().expects = 6;
context.call_cb(
Event::SECUREJOIN_JOINER_PROGRESS,
chat_id_2_contact_id(context, contact_chat_id) as uintptr_t,
@@ -195,37 +190,17 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
} else {
b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char
},
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.auth
.as_ref()
.unwrap()
.to_string(),
(*qr_scan).auth,
own_fingerprint,
if 0 != join_vg {
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string()
(*qr_scan).text2
} else {
"".to_string()
0 as *mut libc::c_char
},
);
free(own_fingerprint as *mut libc::c_void);
} else {
context.bob.write().unwrap().expects = 2;
context.bob.clone().write().unwrap().expects = 2;
send_handshake_msg(
context,
contact_chat_id,
@@ -234,18 +209,9 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
} else {
b"vc-request\x00" as *const u8 as *const libc::c_char
},
context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.invitenumber
.as_ref()
.unwrap(),
(*qr_scan).invitenumber,
0 as *const libc::c_char,
0 as *const libc::c_char,
"",
);
}
@@ -262,23 +228,25 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
}
}
}
let mut bob = context.bob.write().unwrap();
let bob_a = context.bob.clone();
let mut bob = bob_a.write().unwrap();
bob.expects = 0;
if bob.status == 1 {
if 0 != join_vg {
ret_chat_id = chat::get_chat_id_by_grpid(
ret_chat_id = dc_get_chat_id_by_grpid(
context,
bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(),
None,
(*qr_scan).text2,
0 as *mut libc::c_int,
0 as *mut libc::c_int,
) as libc::c_int
} else {
ret_chat_id = contact_chat_id as libc::c_int
}
}
bob.qr_scan = std::ptr::null_mut();
bob.qr_scan = None;
dc_lot_unref(qr_scan);
if 0 != ongoing_allocated {
dc_free_ongoing(context);
}
@@ -289,45 +257,45 @@ unsafe fn send_handshake_msg(
context: &Context,
contact_chat_id: uint32_t,
step: *const libc::c_char,
param2: impl AsRef<str>,
param2: *const libc::c_char,
fingerprint: *const libc::c_char,
grpid: impl AsRef<str>,
grpid: *const libc::c_char,
) {
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);
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);
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);
if !param2.is_null() {
(*msg).param.set(Param::Arg2, as_str(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());
if !grpid.is_null() {
(*msg).param.set(Param::Arg4, as_str(grpid));
}
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, &mut msg).unwrap();
dc_send_msg(context, contact_chat_id, msg);
dc_msg_unref(msg);
}
unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) -> uint32_t {
let contacts = chat::get_chat_contacts(context, contact_chat_id);
let contacts = dc_get_chat_contacts(context, contact_chat_id);
if contacts.len() == 1 {
contacts[0]
} else {
@@ -337,17 +305,20 @@ unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) ->
unsafe fn fingerprint_equals_sender(
context: &Context,
fingerprint: impl AsRef<str>,
contact_chat_id: u32,
fingerprint: *const libc::c_char,
contact_chat_id: uint32_t,
) -> libc::c_int {
let mut fingerprint_equal = 0;
let contacts = chat::get_chat_contacts(context, contact_chat_id);
if fingerprint.is_null() {
return 0;
}
let mut fingerprint_equal: libc::c_int = 0i32;
let contacts = dc_get_chat_contacts(context, contact_chat_id);
if contacts.len() == 1 {
if let Ok(contact) = Contact::load_from_db(context, contacts[0]) {
if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr())
{
let fingerprint_normalized = dc_normalize_fingerprint(fingerprint.as_ref());
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
if peerstate.public_key_fingerprint.is_some()
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
{
@@ -371,10 +342,12 @@ pub unsafe fn dc_handle_securejoin_handshake(
let mut current_block: u64;
let step: *const libc::c_char;
let join_vg: libc::c_int;
let mut scanned_fingerprint_of_alice: *mut libc::c_char = 0 as *mut libc::c_char;
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
let contact_chat_id: u32;
let contact_chat_id_blocked: Blocked;
let mut grpid = "".to_string();
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
let mut contact_chat_id_blocked: libc::c_int = 0i32;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let mut ret: libc::c_int = 0i32;
if !(contact_id <= 9i32 as libc::c_uint) {
@@ -388,13 +361,15 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0)
as libc::c_int;
let (id, bl) = chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not)
.unwrap_or_default();
contact_chat_id = id;
contact_chat_id_blocked = bl;
if Blocked::Not != contact_chat_id_blocked {
chat::unblock(context, contact_chat_id);
dc_create_or_lookup_nchat_by_contact_id(
context,
contact_id,
0i32,
&mut contact_chat_id,
&mut contact_chat_id_blocked,
);
if 0 != contact_chat_id_blocked {
dc_unblock_chat(context, contact_chat_id);
}
ret = 0x2i32;
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
@@ -432,9 +407,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
} else {
b"vc-auth-required\x00" as *const u8 as *const libc::c_char
},
"",
0 as *const libc::c_char,
"",
0 as *const libc::c_char,
0 as *const libc::c_char,
);
current_block = 10256747982273457880;
}
@@ -448,11 +423,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
) == 0i32
{
let cond = {
let bob = context.bob.read().unwrap();
let scan = bob.qr_scan.as_ref();
scan.is_none()
|| bob.expects != 2
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
let bob_a = context.bob.clone();
let bob = bob_a.read().unwrap();
let scan = bob.qr_scan;
scan.is_null() || bob.expects != 2 || 0 != join_vg && (*scan).state != 202
};
if cond {
@@ -460,44 +434,15 @@ pub unsafe fn dc_handle_securejoin_handshake(
// no error, just aborted somehow or a mail from another handshake
current_block = 4378276786830486580;
} else {
let scanned_fingerprint_of_alice = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap()
.to_string();
let auth = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.auth
.as_ref()
.unwrap()
.to_string();
if 0 != join_vg {
grpid = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string();
{
let scan = context.bob.clone().read().unwrap().qr_scan;
scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint);
auth = dc_strdup((*scan).auth);
if 0 != join_vg {
grpid = dc_strdup((*scan).text2)
}
}
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
if 0 == encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -512,7 +457,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
} else if 0
== fingerprint_equals_sender(
context,
&scanned_fingerprint_of_alice,
scanned_fingerprint_of_alice,
contact_chat_id,
)
{
@@ -532,7 +477,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
contact_id as uintptr_t,
400i32 as uintptr_t,
);
context.bob.write().unwrap().expects = 6;
context.bob.clone().write().unwrap().expects = 6;
send_handshake_msg(
context,
@@ -564,7 +509,8 @@ pub unsafe fn dc_handle_securejoin_handshake(
==== Step 6 in "Out-of-band verified groups" protocol ====
============================================================ */
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
let fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
let fingerprint: *const libc::c_char;
fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
if fingerprint.is_null() {
could_not_establish_secure_connection(
context,
@@ -572,16 +518,14 @@ pub unsafe fn dc_handle_securejoin_handshake(
b"Fingerprint not provided.\x00" as *const u8 as *const libc::c_char,
);
current_block = 4378276786830486580;
} else if !encrypted_and_signed(mimeparser, as_str(fingerprint)) {
} else if 0 == encrypted_and_signed(mimeparser, 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;
} else if 0
== fingerprint_equals_sender(context, as_str(fingerprint), contact_chat_id)
{
} else if 0 == fingerprint_equals_sender(context, fingerprint, contact_chat_id) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -608,7 +552,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
b"Auth invalid.\x00" as *const u8 as *const libc::c_char,
);
current_block = 4378276786830486580;
} else if 0 == mark_peer_as_verified(context, as_str(fingerprint)) {
} else if 0 == mark_peer_as_verified(context, fingerprint) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -635,18 +579,18 @@ pub unsafe fn dc_handle_securejoin_handshake(
600i32 as uintptr_t,
);
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(
grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group"));
let group_chat_id: uint32_t = dc_get_chat_id_by_grpid(
context,
&grpid,
None,
grpid,
0 as *mut libc::c_int,
0 as *mut libc::c_int,
);
if group_chat_id == 0i32 as libc::c_uint {
error!(context, 0, "Chat {} not found.", &grpid);
error!(context, 0, "Chat {} not found.", as_str(grpid),);
current_block = 4378276786830486580;
} else {
chat::add_contact_to_chat_ex(
dc_add_contact_to_chat_ex(
context,
group_chat_id,
contact_id,
@@ -659,9 +603,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
context,
contact_chat_id,
b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char,
"",
0 as *const libc::c_char,
"",
0 as *const libc::c_char,
0 as *const libc::c_char,
);
context.call_cb(
Event::SECUREJOIN_INVITER_PROGRESS,
@@ -684,15 +628,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
if 0 != join_vg {
ret = 0x1i32
}
if context.bob.read().unwrap().expects != 6 {
if context.bob.clone().read().unwrap().expects != 6 {
info!(context, 0, "Message belongs to a different handshake.",);
current_block = 4378276786830486580;
} else {
let cond = {
let bob = context.bob.read().unwrap();
let scan = bob.qr_scan.as_ref();
scan.is_none()
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
let scan = context.bob.clone().read().unwrap().qr_scan;
scan.is_null() || 0 != join_vg && (*scan).state != 202
};
if cond {
warn!(
@@ -701,39 +643,20 @@ pub unsafe fn dc_handle_securejoin_handshake(
);
current_block = 4378276786830486580;
} else {
let scanned_fingerprint_of_alice = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.fingerprint
.as_ref()
.unwrap()
.to_string();
if 0 != join_vg {
grpid = context
.bob
.read()
.unwrap()
.qr_scan
.as_ref()
.unwrap()
.text2
.as_ref()
.unwrap()
.to_string();
{
let scan = context.bob.clone().read().unwrap().qr_scan;
scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint);
if 0 != join_vg {
grpid = dc_strdup((*scan).text2)
}
}
let mut vg_expect_encrypted: libc::c_int = 1i32;
if 0 != join_vg {
let mut is_verified_group: libc::c_int = 0i32;
chat::get_chat_id_by_grpid(
dc_get_chat_id_by_grpid(
context,
grpid,
None,
0 as *mut libc::c_int,
&mut is_verified_group,
);
if 0 == is_verified_group {
@@ -741,7 +664,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
}
}
if 0 != vg_expect_encrypted {
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
if 0 == encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) {
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -759,10 +682,8 @@ pub unsafe fn dc_handle_securejoin_handshake(
match current_block {
4378276786830486580 => {}
_ => {
if 0 == mark_peer_as_verified(
context,
&scanned_fingerprint_of_alice,
) {
if 0 == mark_peer_as_verified(context, scanned_fingerprint_of_alice)
{
could_not_establish_secure_connection(
context,
contact_chat_id,
@@ -805,16 +726,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
4378276786830486580 => {}
_ => {
secure_connection_established(context, contact_chat_id);
context.bob.write().unwrap().expects = 0;
context.bob.clone().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,
"",
0 as *const libc::c_char,
0 as *const libc::c_char,
);
}
end_bobs_joining(context, 1i32);
@@ -870,13 +791,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
}
}
free(scanned_fingerprint_of_alice as *mut libc::c_void);
free(auth as *mut libc::c_void);
free(own_fingerprint as *mut libc::c_void);
free(grpid as *mut libc::c_void);
ret
}
unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) {
context.bob.write().unwrap().status = status;
context.bob.clone().write().unwrap().status = status;
dc_stop_ongoing_process(context);
}
@@ -888,8 +812,9 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint
} else {
"?"
};
let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr);
chat::add_device_msg(context, contact_chat_id, msg);
let msg =
CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap();
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,
@@ -929,18 +854,21 @@ unsafe fn could_not_establish_secure_connection(
"?"
},
);
chat::add_device_msg(context, contact_chat_id, &msg);
error!(context, 0, "{} ({})", &msg, as_str(details));
let msg_c = CString::new(msg.as_str()).unwrap();
dc_add_device_msg(context, contact_chat_id, msg_c.as_ptr());
error!(context, 0, "{} ({})", msg, as_str(details));
}
unsafe fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> libc::c_int {
unsafe fn mark_peer_as_verified(
context: &Context,
fingerprint: *const libc::c_char,
) -> libc::c_int {
let mut success = 0;
if let Some(ref mut peerstate) =
Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref())
Peerstate::from_fingerprint(context, &context.sql, as_str(fingerprint))
{
if peerstate.set_verified(1, fingerprint.as_ref(), 2) {
if peerstate.set_verified(1, as_str(fingerprint), 2) {
peerstate.prefer_encrypt = EncryptPreference::Mutual;
peerstate.to_save = Some(ToSave::All);
peerstate.save_to_db(&context.sql, false);
@@ -955,40 +883,43 @@ unsafe fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>)
* Tools: Misc.
******************************************************************************/
// TODO should return bool
unsafe fn encrypted_and_signed(
mimeparser: &dc_mimeparser_t,
expected_fingerprint: impl AsRef<str>,
) -> bool {
expected_fingerprint: *const libc::c_char,
) -> libc::c_int {
if 0 == mimeparser.e2ee_helper.encrypted {
warn!(mimeparser.context, 0, "Message not encrypted.",);
return false;
return 0i32;
}
if mimeparser.e2ee_helper.signatures.len() <= 0 {
warn!(mimeparser.context, 0, "Message not signed.",);
return false;
return 0i32;
}
if expected_fingerprint.as_ref().is_empty() {
if expected_fingerprint.is_null() {
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
return false;
return 0i32;
}
if !mimeparser
.e2ee_helper
.signatures
.contains(expected_fingerprint.as_ref())
.contains(as_str(expected_fingerprint))
{
warn!(
mimeparser.context,
0,
"Message does not match expected fingerprint {}.",
expected_fingerprint.as_ref(),
as_str(expected_fingerprint),
);
return false;
return 0;
}
true
1
}
pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) {
let mut contact_chat_id = 0;
// - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal
// - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes
// together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother
@@ -1005,17 +936,22 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
)
.unwrap_or_default();
if contact_id > 0 {
let (contact_chat_id, _) =
chat::create_or_lookup_by_contact_id(context, contact_id as u32, Blocked::Deaddrop)
.unwrap_or_default();
dc_create_or_lookup_nchat_by_contact_id(
context,
contact_id as u32,
2,
&mut contact_chat_id,
0 as *mut libc::c_int,
);
let peeraddr: &str = match peerstate.addr {
Some(ref addr) => &addr,
None => "",
};
let msg = context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr);
chat::add_device_msg(context, contact_chat_id, msg);
let msg = CString::new(
context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr),
)
.unwrap();
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,

View File

@@ -311,7 +311,8 @@ mod tests {
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 plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -328,16 +329,17 @@ mod tests {
fn test_simplify_html_encoded() {
unsafe {
let mut simplify = Simplify::new();
let html =
b"&lt;&gt;&quot;&apos;&amp; &auml;&Auml;&ouml;&Ouml;&uuml;&Uuml;&szlig; foo&AElig;&ccedil;&Ccedil; &diams;&lrm;&rlm;&zwnj;&noent;&zwj;\x00"
let html: *const libc::c_char =
b"&lt;&gt;&quot;&apos;&amp; &auml;&Auml;&ouml;&Ouml;&uuml;&Uuml;&szlig; foo&AElig;&ccedil;&Ccedil; &diams;&noent;&lrm;&rlm;&zwnj;&zwj;\x00"
as *const u8 as *const libc::c_char;
let plain = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
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}"
strcmp(plain,
b"<>\"\'& \xc3\xa4\xc3\x84\xc3\xb6\xc3\x96\xc3\xbc\xc3\x9c\xc3\x9f foo\xc3\x86\xc3\xa7\xc3\x87 \xe2\x99\xa6&noent;\x00"
as *const u8 as *const libc::c_char),
0,
);
free(plain as *mut libc::c_void);

View File

@@ -10,7 +10,7 @@ use crate::types::*;
use crate::x::*;
#[inline]
fn isalnum(c: libc::c_int) -> libc::c_int {
pub fn isalnum(c: libc::c_int) -> libc::c_int {
if c < std::u8::MAX as libc::c_int {
(c as u8 as char).is_ascii_alphanumeric() as libc::c_int
} else {
@@ -18,6 +18,48 @@ fn isalnum(c: libc::c_int) -> libc::c_int {
}
}
pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char {
let mut pstr: *const libc::c_char = to_encode;
if to_encode.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
let buf: *mut libc::c_char =
malloc(strlen(to_encode).wrapping_mul(3).wrapping_add(1)) as *mut libc::c_char;
let mut pbuf: *mut libc::c_char = buf;
assert!(!buf.is_null());
while 0 != *pstr {
if 0 != isalnum(*pstr as libc::c_int)
|| *pstr as libc::c_int == '-' as i32
|| *pstr as libc::c_int == '_' as i32
|| *pstr as libc::c_int == '.' as i32
|| *pstr as libc::c_int == '~' as i32
{
let fresh0 = pbuf;
pbuf = pbuf.offset(1);
*fresh0 = *pstr
} else if *pstr as libc::c_int == ' ' as i32 {
let fresh1 = pbuf;
pbuf = pbuf.offset(1);
*fresh1 = '+' as i32 as libc::c_char
} else {
let fresh2 = pbuf;
pbuf = pbuf.offset(1);
*fresh2 = '%' as i32 as libc::c_char;
let fresh3 = pbuf;
pbuf = pbuf.offset(1);
*fresh3 = int_2_uppercase_hex((*pstr as libc::c_int >> 4i32) as libc::c_char);
let fresh4 = pbuf;
pbuf = pbuf.offset(1);
*fresh4 = int_2_uppercase_hex((*pstr as libc::c_int & 15i32) as libc::c_char)
}
pstr = pstr.offset(1isize)
}
*pbuf = '\u{0}' as i32 as libc::c_char;
buf
}
/* ******************************************************************************
* URL encoding and decoding, RFC 3986
******************************************************************************/
@@ -289,8 +331,7 @@ pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_
out
}
#[cfg(test)]
unsafe fn dc_encode_modified_utf7(
pub unsafe fn dc_encode_modified_utf7(
mut to_encode: *const libc::c_char,
change_spaces: libc::c_int,
) -> *mut libc::c_char {
@@ -434,15 +475,13 @@ unsafe fn dc_encode_modified_utf7(
******************************************************************************/
// UTF7 modified base64 alphabet
#[cfg(test)]
static mut BASE64CHARS: [libc::c_char; 65] = [
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 44, 0,
];
#[cfg(test)]
unsafe fn dc_decode_modified_utf7(
pub unsafe fn dc_decode_modified_utf7(
to_decode: *const libc::c_char,
change_spaces: libc::c_int,
) -> *mut libc::c_char {
@@ -652,11 +691,11 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc:
}
}
free(charset as *mut libc::c_void);
if !decoded.is_null() {
return if !decoded.is_null() {
decoded
} else {
dc_strdup(to_decode)
}
};
}
unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
@@ -671,7 +710,6 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
#[cfg(test)]
mod tests {
use super::*;
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use std::ffi::CStr;
#[test]
@@ -851,15 +889,14 @@ mod tests {
#[test]
fn test_dc_urlencode_urldecode() {
unsafe {
let buf1 = percent_encode(b"Bj\xc3\xb6rn Petersen", NON_ALPHANUMERIC)
.to_string()
.strdup();
let buf1 =
dc_urlencode(b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char);
assert_eq!(
CStr::from_ptr(buf1 as *const libc::c_char)
.to_str()
.unwrap(),
"Bj%C3%B6rn%20Petersen"
"Bj%C3%B6rn+Petersen"
);
let buf2 = dc_urldecode(buf1);

View File

@@ -4,7 +4,7 @@ use crate::sql;
// Token namespaces
#[allow(non_camel_case_types)]
type dc_tokennamespc_t = usize;
pub type dc_tokennamespc_t = usize;
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;

File diff suppressed because it is too large Load Diff

View File

@@ -18,10 +18,6 @@ pub enum Error {
Io(std::io::Error),
#[fail(display = "{:?}", _0)]
Message(String),
#[fail(display = "{:?}", _0)]
Image(image_meta::ImageError),
#[fail(display = "{:?}", _0)]
Utf8(std::str::Utf8Error),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -50,18 +46,6 @@ impl From<std::io::Error> for Error {
}
}
impl From<std::str::Utf8Error> for Error {
fn from(err: std::str::Utf8Error) -> Error {
Error::Utf8(err)
}
}
impl From<image_meta::ImageError> for Error {
fn from(err: image_meta::ImageError) -> Error {
Error::Image(err)
}
}
#[macro_export]
macro_rules! bail {
($e:expr) => {

View File

@@ -1,9 +1,6 @@
use std::ffi::CString;
use std::net;
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc, Condvar, Mutex, RwLock,
};
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::time::{Duration, SystemTime};
use crate::constants::*;
@@ -13,18 +10,19 @@ use crate::dc_tools::CStringExt;
use crate::oauth2::dc_get_oauth2_access_token;
use crate::types::*;
const DC_IMAP_SEEN: usize = 0x0001;
const DC_REGENERATE: usize = 0x01;
pub const DC_IMAP_SEEN: usize = 0x0001;
pub const DC_REGENERATE: usize = 0x01;
const DC_SUCCESS: usize = 3;
const DC_ALREADY_DONE: usize = 2;
const DC_RETRY_LATER: usize = 1;
const DC_FAILED: usize = 0;
pub const DC_SUCCESS: usize = 3;
pub const DC_ALREADY_DONE: usize = 2;
pub const DC_RETRY_LATER: usize = 1;
pub const DC_FAILED: usize = 0;
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
const FETCH_FLAGS: &str = "(FLAGS)";
const PREFETCH_FLAGS: &'static str = "(UID ENVELOPE)";
const BODY_FLAGS: &'static str = "(FLAGS BODY.PEEK[])";
const FETCH_FLAGS: &'static str = "(FLAGS)";
#[repr(C)]
pub struct Imap {
config: Arc<RwLock<ImapConfig>>,
watch: Arc<(Mutex<bool>, Condvar)>,
@@ -37,8 +35,6 @@ pub struct Imap {
session: Arc<Mutex<Option<Session>>>,
stream: Arc<RwLock<Option<net::TcpStream>>>,
connected: Arc<Mutex<bool>>,
should_reconnect: AtomicBool,
}
struct OAuth2 {
@@ -59,13 +55,13 @@ impl imap::Authenticator for OAuth2 {
}
#[derive(Debug)]
enum FolderMeaning {
pub enum FolderMeaning {
Unknown,
SentObjects,
Other,
}
enum Client {
pub enum Client {
Secure(
imap::Client<native_tls::TlsStream<net::TcpStream>>,
net::TcpStream,
@@ -73,12 +69,12 @@ enum Client {
Insecure(imap::Client<net::TcpStream>, net::TcpStream),
}
enum Session {
pub enum Session {
Secure(imap::Session<native_tls::TlsStream<net::TcpStream>>),
Insecure(imap::Session<net::TcpStream>),
}
enum IdleHandle<'a> {
pub enum IdleHandle<'a> {
Secure(imap::extensions::idle::Handle<'a, native_tls::TlsStream<net::TcpStream>>),
Insecure(imap::extensions::idle::Handle<'a, net::TcpStream>),
}
@@ -203,8 +199,8 @@ impl Session {
pub fn create<S: AsRef<str>>(&mut self, mailbox_name: S) -> imap::error::Result<()> {
match self {
Session::Secure(i) => i.create(mailbox_name),
Session::Insecure(i) => i.create(mailbox_name),
Session::Secure(i) => i.subscribe(mailbox_name),
Session::Insecure(i) => i.subscribe(mailbox_name),
}
}
@@ -264,8 +260,8 @@ impl Session {
pub fn idle(&mut self) -> imap::error::Result<IdleHandle> {
match self {
Session::Secure(i) => i.idle().map(IdleHandle::Secure),
Session::Insecure(i) => i.idle().map(IdleHandle::Insecure),
Session::Secure(i) => i.idle().map(|h| IdleHandle::Secure(h)),
Session::Insecure(i) => i.idle().map(|h| IdleHandle::Insecure(h)),
}
}
@@ -307,7 +303,7 @@ impl Session {
}
}
struct ImapConfig {
pub struct ImapConfig {
pub addr: String,
pub imap_server: String,
pub imap_port: u16,
@@ -317,6 +313,7 @@ struct ImapConfig {
pub selected_folder: Option<String>,
pub selected_mailbox: Option<imap::types::Mailbox>,
pub selected_folder_needs_expunge: bool,
pub should_reconnect: bool,
pub can_idle: bool,
pub has_xlist: bool,
pub imap_delimiter: char,
@@ -335,6 +332,7 @@ impl Default for ImapConfig {
selected_folder: None,
selected_mailbox: None,
selected_folder_needs_expunge: false,
should_reconnect: false,
can_idle: false,
has_xlist: false,
imap_delimiter: '.',
@@ -362,7 +360,6 @@ impl Imap {
precheck_imf,
receive_imf,
connected: Arc::new(Mutex::new(false)),
should_reconnect: AtomicBool::new(false),
}
}
@@ -371,7 +368,7 @@ impl Imap {
}
pub fn should_reconnect(&self) -> bool {
self.should_reconnect.load(Ordering::Relaxed)
self.config.read().unwrap().should_reconnect
}
fn setup_handle_if_needed(&self, context: &Context) -> bool {
@@ -384,7 +381,7 @@ impl Imap {
}
if self.is_connected() && self.stream.read().unwrap().is_some() {
self.should_reconnect.store(false, Ordering::Relaxed);
self.config.write().unwrap().should_reconnect = false;
return true;
}
@@ -454,7 +451,7 @@ impl Imap {
}
};
self.should_reconnect.store(false, Ordering::Relaxed);
self.config.write().unwrap().should_reconnect = false;
match login_res {
Ok((session, stream)) => {
@@ -542,7 +539,7 @@ impl Imap {
let mut config = self.config.write().unwrap();
config.addr = addr.to_string();
config.imap_server = imap_server.to_string();
config.imap_port = imap_port;
config.imap_port = imap_port.into();
config.imap_user = imap_user.to_string();
config.imap_pw = imap_pw.to_string();
config.server_flags = server_flags;
@@ -553,12 +550,14 @@ impl Imap {
return false;
}
let (teardown, can_idle, has_xlist) = match &mut *self.session.lock().unwrap() {
let teardown: bool;
match &mut *self.session.lock().unwrap() {
Some(ref mut session) => {
if let Ok(caps) = session.capabilities() {
if !context.sql.is_open() {
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
(true, false, false)
teardown = true;
} else {
let can_idle = caps.has("IDLE");
let has_xlist = caps.has("XLIST");
@@ -575,23 +574,24 @@ impl Imap {
lp.mail_user,
caps_list,
);
(false, can_idle, has_xlist)
self.config.write().unwrap().can_idle = can_idle;
self.config.write().unwrap().has_xlist = has_xlist;
*self.connected.lock().unwrap() = true;
teardown = false;
}
} else {
(true, false, false)
teardown = true;
}
}
None => (true, false, false),
};
None => {
teardown = true;
}
}
if teardown {
self.unsetup_handle(context);
self.free_connect_params();
false
} else {
self.config.write().unwrap().can_idle = can_idle;
self.config.write().unwrap().has_xlist = has_xlist;
*self.connected.lock().unwrap() = true;
true
}
}
@@ -689,8 +689,9 @@ impl Imap {
err
);
self.config.write().unwrap().selected_folder = None;
self.should_reconnect.store(true, Ordering::Relaxed);
let mut config = self.config.write().unwrap();
config.selected_folder = None;
config.should_reconnect = true;
return 0;
}
}
@@ -776,7 +777,7 @@ impl Imap {
match session.fetch(set, PREFETCH_FLAGS) {
Ok(list) => list,
Err(_err) => {
self.should_reconnect.store(true, Ordering::Relaxed);
self.config.write().unwrap().should_reconnect = true;
info!(
context,
0,
@@ -821,7 +822,7 @@ impl Imap {
match session.uid_fetch(set, PREFETCH_FLAGS) {
Ok(list) => list,
Err(err) => {
warn!(context, 0, "failed to fetch uids: {}", err);
eprintln!("fetch err: {:?}", err);
return 0;
}
}
@@ -927,13 +928,15 @@ impl Imap {
return 0;
}
let mut retry_later = false;
let set = format!("{}", server_uid);
let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, BODY_FLAGS) {
Ok(msgs) => msgs,
Err(err) => {
self.should_reconnect.store(true, Ordering::Relaxed);
self.config.write().unwrap().should_reconnect = true;
warn!(
context,
0,
@@ -943,11 +946,17 @@ impl Imap {
self.should_reconnect(),
err
);
return 0;
if self.should_reconnect() {
// maybe we should also retry on other errors, however, we should check this carefully, as this may result in a dead lock!
retry_later = true;
}
return if retry_later { 0 } else { 1 };
}
}
} else {
return 1;
return if retry_later { 0 } else { 1 };
};
if msgs.is_empty() {
@@ -995,7 +1004,11 @@ impl Imap {
}
}
1
if retry_later {
0
} else {
1
}
}
pub fn idle(&self, context: &Context) {
@@ -1060,7 +1073,7 @@ impl Imap {
context,
0, "IMAP-IDLE wait cancelled, we will reconnect soon."
);
self.should_reconnect.store(true, Ordering::Relaxed);
self.config.write().unwrap().should_reconnect = true;
}
_ => {
warn!(context, 0, "IMAP-IDLE returns unknown value: {}", err);
@@ -1563,23 +1576,20 @@ impl Imap {
info!(context, 0, "MVBOX-folder created.",);
}
Err(err) => {
eprintln!("create error: {:?}", err);
warn!(
context,
0,
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})",
err
0, "Cannot create MVBOX-folder, using trying INBOX subfolder."
);
match session.create(&fallback_folder) {
Ok(_) => {
mvbox_folder = Some(fallback_folder);
info!(
context,
0, "MVBOX-folder created as INBOX subfolder. ({})", err
);
info!(context, 0, "MVBOX-folder created as INBOX subfolder.",);
}
Err(err) => {
warn!(context, 0, "Cannot create MVBOX-folder. ({})", err);
eprintln!("create error: {:?}", err);
warn!(context, 0, "Cannot create MVBOX-folder.",);
}
}
}

1127
src/job.rs

File diff suppressed because it is too large Load Diff

View File

@@ -1,181 +0,0 @@
use std::sync::{Arc, Condvar, Mutex};
use crate::configure::*;
use crate::context::Context;
use crate::imap::Imap;
pub struct JobThread {
pub name: &'static str,
pub folder_config_name: &'static str,
pub imap: Imap,
pub state: Arc<(Mutex<JobState>, Condvar)>,
}
#[derive(Clone, Debug, Default)]
pub struct JobState {
idle: bool,
jobs_needed: i32,
suspended: bool,
using_handle: bool,
}
impl JobThread {
pub fn new(name: &'static str, folder_config_name: &'static str, imap: Imap) -> Self {
JobThread {
name,
folder_config_name,
imap,
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
}
}
pub fn suspend(&self, context: &Context) {
info!(context, 0, "Suspending {}-thread.", self.name,);
{
self.state.0.lock().unwrap().suspended = true;
}
self.interrupt_idle(context);
loop {
let using_handle = self.state.0.lock().unwrap().using_handle;
if !using_handle {
return;
}
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
}
pub fn unsuspend(&self, context: &Context) {
info!(context, 0, "Unsuspending {}-thread.", self.name);
let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap();
state.suspended = false;
state.idle = true;
cvar.notify_one();
}
pub fn interrupt_idle(&self, context: &Context) {
{
self.state.0.lock().unwrap().jobs_needed = 1;
}
info!(context, 0, "Interrupting {}-IDLE...", self.name);
self.imap.interrupt_idle();
let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap();
state.idle = true;
cvar.notify_one();
}
pub fn fetch(&mut self, context: &Context, use_network: bool) {
{
let &(ref lock, _) = &*self.state.clone();
let mut state = lock.lock().unwrap();
if state.suspended {
return;
}
state.using_handle = true;
}
if use_network {
let start = std::time::Instant::now();
if self.connect_to_imap(context) {
info!(context, 0, "{}-fetch started...", self.name);
self.imap.fetch(context);
if self.imap.should_reconnect() {
info!(context, 0, "{}-fetch aborted, starting over...", self.name,);
self.imap.fetch(context);
}
info!(
context,
0,
"{}-fetch done in {:.3} ms.",
self.name,
start.elapsed().as_millis(),
);
}
}
self.state.0.lock().unwrap().using_handle = false;
}
fn connect_to_imap(&self, context: &Context) -> bool {
if self.imap.is_connected() {
return true;
}
let mut ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
if ret_connected {
if context
.sql
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
self.imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) = context.sql.get_config(context, self.folder_config_name) {
self.imap.set_watch_folder(mvbox_name);
} else {
self.imap.disconnect(context);
ret_connected = false;
}
}
ret_connected
}
pub fn idle(&self, context: &Context, use_network: bool) {
{
let &(ref lock, ref cvar) = &*self.state.clone();
let mut state = lock.lock().unwrap();
if 0 != state.jobs_needed {
info!(
context,
0,
"{}-IDLE will not be started as it was interrupted while not ideling.",
self.name,
);
state.jobs_needed = 0;
return;
}
if state.suspended {
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
state.using_handle = true;
if !use_network {
state.using_handle = false;
while !state.idle {
state = cvar.wait(state).unwrap();
}
state.idle = false;
return;
}
}
self.connect_to_imap(context);
info!(context, 0, "{}-IDLE started...", self.name,);
self.imap.idle(context);
info!(context, 0, "{}-IDLE ended.", self.name);
self.state.0.lock().unwrap().using_handle = false;
}
}

View File

@@ -89,7 +89,7 @@ impl Key {
}
pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option<Self> {
if bytes.is_empty() {
if 0 == bytes.len() {
return None;
}
let res: Result<Key, _> = match key_type {
@@ -283,7 +283,7 @@ impl Key {
Key::Public(_) => None,
Key::Secret(k) => {
let pub_key = k.public_key();
pub_key.sign(k, || "".into()).map(Key::Public).ok()
pub_key.sign(k, || "".into()).map(|k| Key::Public(k)).ok()
}
}
}

View File

@@ -1,9 +1,4 @@
#![deny(clippy::correctness)]
// TODO: make all of these errors, such that clippy actually passes.
#![warn(clippy::all, clippy::perf, clippy::not_unsafe_ptr_arg_deref)]
// This is nice, but for now just annoying.
#![allow(clippy::unreadable_literal)]
#![feature(ptr_wrapping_offset_from)]
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
#[macro_use]
extern crate failure_derive;
@@ -22,47 +17,50 @@ mod log;
#[macro_use]
pub mod error;
mod aheader;
pub mod chat;
pub mod aheader;
pub mod chatlist;
pub mod config;
pub mod configure;
pub mod constants;
pub mod contact;
pub mod context;
mod imap;
pub mod job;
mod job_thread;
pub mod imap;
pub mod key;
pub mod keyring;
pub mod lot;
pub mod message;
pub mod oauth2;
mod param;
pub mod param;
pub mod peerstate;
pub mod pgp;
pub mod qr;
mod smtp;
pub mod smtp;
pub mod sql;
mod stock;
pub mod stock;
pub mod types;
pub mod x;
pub mod dc_array;
mod dc_dehtml;
mod dc_e2ee;
pub mod dc_chat;
pub mod dc_configure;
pub mod dc_dehtml;
pub mod dc_e2ee;
pub mod dc_imex;
pub mod dc_job;
pub mod dc_jobthread;
pub mod dc_location;
mod dc_loginparam;
mod dc_mimefactory;
pub mod dc_loginparam;
pub mod dc_lot;
pub mod dc_mimefactory;
pub mod dc_mimeparser;
mod dc_move;
pub mod dc_move;
pub mod dc_msg;
pub mod dc_qr;
pub mod dc_receive_imf;
pub mod dc_saxparser;
pub mod dc_securejoin;
mod dc_simplify;
mod dc_strencode;
mod dc_token;
pub mod dc_simplify;
pub mod dc_strencode;
pub mod dc_token;
pub mod dc_tools;
pub use self::constants::*;
#[cfg(test)]
mod test_utils;
pub mod test_utils;

View File

@@ -1,109 +0,0 @@
use deltachat_derive::{FromSql, ToSql};
/// An object containing a set of values.
/// The meaning of the values is defined by the function returning the object.
/// Lot objects are created
/// eg. by chatlist.get_summary() or dc_msg_get_summary().
///
/// _Lot_ is used in the meaning _heap_ here.
#[derive(Default, Debug, Clone)]
pub struct Lot {
pub(crate) text1_meaning: Meaning,
pub(crate) text1: Option<String>,
pub(crate) text2: Option<String>,
pub(crate) timestamp: i64,
pub(crate) state: LotState,
pub(crate) id: u32,
pub(crate) fingerprint: Option<String>,
pub(crate) invitenumber: Option<String>,
pub(crate) auth: Option<String>,
}
#[repr(u8)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum Meaning {
None = 0,
Text1Draft = 1,
Text1Username = 2,
Text1Self = 3,
}
impl Default for Meaning {
fn default() -> Self {
Meaning::None
}
}
impl Lot {
pub fn new() -> Self {
Default::default()
}
pub fn get_text1(&self) -> Option<&str> {
self.text1.as_ref().map(|s| s.as_str())
}
pub fn get_text2(&self) -> Option<&str> {
self.text2.as_ref().map(|s| s.as_str())
}
pub fn get_text1_meaning(&self) -> Meaning {
self.text1_meaning
}
pub fn get_state(&self) -> LotState {
self.state
}
pub fn get_id(&self) -> u32 {
self.id
}
pub fn get_timestamp(&self) -> i64 {
self.timestamp
}
}
#[repr(i32)]
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
pub enum LotState {
// Default
Undefined = 0,
// Qr States
/// id=contact
QrAskVerifyContact = 200,
/// text1=groupname
QrAskVerifyGroup = 202,
/// id=contact
QrFprOk = 210,
/// id=contact
QrFprMissmatch = 220,
/// test1=formatted fingerprint
QrFprWithoutAddr = 230,
/// id=contact
QrAddr = 320,
/// text1=text
QrText = 330,
/// text1=URL
QrUrl = 332,
/// text1=error string
QrError = 400,
// Message States
MsgInFresh = 10,
MsgInNoticed = 13,
MsgInSeen = 16,
MsgOutPreparing = 18,
MsgOutDraft = 19,
MsgOutPending = 20,
MsgOutFailed = 24,
MsgOutDelivered = 26,
MsgOutMdnRcvd = 28,
}
impl Default for LotState {
fn default() -> Self {
LotState::Undefined
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,7 +23,7 @@ const OAUTH2_YANDEX: Oauth2 = Oauth2 {
};
#[derive(Debug, Clone, PartialEq, Eq)]
struct Oauth2 {
pub struct Oauth2 {
client_id: &'static str,
get_code: &'static str,
init_token: &'static str,

View File

@@ -113,7 +113,7 @@ impl str::FromStr for Params {
continue;
}
// TODO: probably nicer using a regex
ensure!(pair.len() > 1, "Invalid key pair: '{}'", pair);
ensure!(pair.len() > 2, "Invalid key pair: '{}'", pair);
let mut split = pair.splitn(2, '=');
let key = split.next();
let value = split.next();
@@ -235,12 +235,4 @@ mod tests {
assert!(p1.is_empty());
assert_eq!(p1.len(), 0)
}
#[test]
fn test_regression() {
let p1: Params = "a=cli%40deltachat.de\nn=\ni=TbnwJ6lSvD5\ns=0ejvbdFSQxB"
.parse()
.unwrap();
assert_eq!(p1.get(Param::Forwarded).unwrap(), "cli%40deltachat.de");
}
}

View File

@@ -4,9 +4,9 @@ use std::fmt;
use num_traits::FromPrimitive;
use crate::aheader::*;
use crate::chat::*;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
use crate::key::*;
use crate::sql::{self, Sql};
@@ -85,7 +85,7 @@ pub enum DegradeEvent {
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum VerifiedKey {
pub enum VerifiedKey {
Gossip,
Public,
None,
@@ -466,7 +466,7 @@ impl<'a> Peerstate<'a> {
}
if self.to_save == Some(ToSave::All) || create {
reset_gossiped_timestamp(self.context, 0);
dc_reset_gossiped_timestamp(self.context, 0);
}
success

View File

@@ -2,7 +2,6 @@ use std::collections::HashSet;
use std::convert::TryInto;
use std::ffi::CStr;
use std::io::Cursor;
use std::ptr;
use pgp::composed::{
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
@@ -30,19 +29,19 @@ pub unsafe fn dc_split_armored_data(
let mut line: *mut libc::c_char = buf;
let mut p1: *mut libc::c_char = buf;
let mut p2: *mut libc::c_char;
let mut headerline: *mut libc::c_char = ptr::null_mut();
let mut base64: *mut libc::c_char = ptr::null_mut();
let mut headerline: *mut libc::c_char = 0 as *mut libc::c_char;
let mut base64: *mut libc::c_char = 0 as *mut libc::c_char;
if !ret_headerline.is_null() {
*ret_headerline = 0 as *const libc::c_char
}
if !ret_setupcodebegin.is_null() {
*ret_setupcodebegin = ptr::null_mut();
*ret_setupcodebegin = 0 as *const libc::c_char
}
if !ret_preferencrypt.is_null() {
*ret_preferencrypt = ptr::null();
*ret_preferencrypt = 0 as *const libc::c_char
}
if !ret_base64.is_null() {
*ret_base64 = ptr::null();
*ret_base64 = 0 as *const libc::c_char
}
if !(buf.is_null() || ret_headerline.is_null()) {
dc_remove_cr_chars(buf);
@@ -144,7 +143,7 @@ pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
.key_type(PgpKeyType::Rsa(2048))
.can_create_certificates(true)
.can_sign(true)
.primary_user_id(user_id)
.primary_user_id(user_id.into())
.passphrase(None)
.preferred_symmetric_algorithms(smallvec![
SymmetricKeyAlgorithm::AES256,

469
src/qr.rs
View File

@@ -1,469 +0,0 @@
use lazy_static::lazy_static;
use percent_encoding::percent_decode_str;
use crate::chat;
use crate::constants::Blocked;
use crate::contact::*;
use crate::context::Context;
use crate::error::Error;
use crate::key::dc_format_fingerprint;
use crate::key::*;
use crate::lot::{Lot, LotState};
use crate::param::*;
use crate::peerstate::*;
const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase
const MAILTO_SCHEME: &str = "mailto:";
const MATMSG_SCHEME: &str = "MATMSG:";
const VCARD_SCHEME: &str = "BEGIN:VCARD";
const SMTP_SCHEME: &str = "SMTP:";
const HTTP_SCHEME: &str = "http://";
const HTTPS_SCHEME: &str = "https://";
// Make it easy to convert errors into the final `Lot`.
impl Into<Lot> for Error {
fn into(self) -> Lot {
let mut l = Lot::new();
l.state = LotState::QrError;
l.text1 = Some(self.to_string());
l
}
}
/// Check a scanned QR code.
/// The function should be called after a QR code is scanned.
/// The function takes the raw text scanned and checks what can be done with it.
pub fn check_qr(context: &Context, qr: impl AsRef<str>) -> Lot {
let qr = qr.as_ref();
info!(context, 0, "Scanned QR code: {}", qr);
if qr.starts_with(OPENPGP4FPR_SCHEME) {
decode_openpgp(context, qr)
} else if qr.starts_with(MAILTO_SCHEME) {
decode_mailto(context, qr)
} else if qr.starts_with(SMTP_SCHEME) {
decode_smtp(context, qr)
} else if qr.starts_with(MATMSG_SCHEME) {
decode_matmsg(context, qr)
} else if qr.starts_with(VCARD_SCHEME) {
decode_vcard(context, qr)
} else if qr.starts_with(HTTP_SCHEME) || qr.starts_with(HTTPS_SCHEME) {
Lot::from_url(qr)
} else {
Lot::from_text(qr)
}
}
/// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH`
/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH`
fn decode_openpgp(context: &Context, qr: &str) -> Lot {
let payload = &qr[OPENPGP4FPR_SCHEME.len()..];
let (fingerprint, fragment) = match payload.find('#').map(|offset| {
let (fp, rest) = payload.split_at(offset);
// need to remove the # from the fragment
(fp, &rest[1..])
}) {
Some(pair) => pair,
None => return format_err!("Invalid OPENPGP4FPR found").into(),
};
dbg!(fingerprint);
dbg!(fragment);
// replace & with \n to match expected param format
let fragment = fragment.replace('&', "\n");
dbg!(&fragment);
// Then parse the parameters
let param: Params = match fragment.parse() {
Ok(params) => params,
Err(err) => return err.into(),
};
dbg!(&param);
let addr = if let Some(addr) = param.get(Param::Forwarded) {
match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
}
} else {
return format_err!("Missing address").into();
};
// what is up with that param name?
let name = if let Some(encoded_name) = param.get(Param::SetLongitude) {
match percent_decode_str(encoded_name).decode_utf8() {
Ok(name) => name.to_string(),
Err(err) => return format_err!("Invalid name: {}", err).into(),
}
} else {
"".to_string()
};
let invitenumber = param.get(Param::ProfileImage).map(|s| s.to_string());
let auth = param.get(Param::Auth).map(|s| s.to_string());
let grpid = param.get(Param::GroupId).map(|s| s.to_string());
let grpname = if grpid.is_some() {
if let Some(encoded_name) = param.get(Param::GroupName) {
match percent_decode_str(encoded_name).decode_utf8() {
Ok(name) => Some(name.to_string()),
Err(err) => return format_err!("Invalid group name: {}", err).into(),
}
} else {
None
}
} else {
None
};
let fingerprint = dc_normalize_fingerprint(fingerprint);
// ensure valid fingerprint
if fingerprint.len() != 40 {
return format_err!("Bad fingerprint length in QR code").into();
}
println!(
"{:?} {:?} {:?} {:?} {:?} {:?} {:?}",
addr, name, invitenumber, auth, grpid, grpname, fingerprint
);
let mut lot = Lot::new();
// retrieve known state for this fingerprint
let peerstate = Peerstate::from_fingerprint(context, &context.sql, &fingerprint);
if invitenumber.is_none() || auth.is_none() {
if let Some(peerstate) = peerstate {
lot.state = LotState::QrFprOk;
let addr = peerstate
.addr
.as_ref()
.map(|s| s.as_str())
.unwrap_or_else(|| "");
lot.id = Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan)
.map(|(id, _)| id)
.unwrap_or_default();
let (id, _) = chat::create_or_lookup_by_contact_id(context, lot.id, Blocked::Deaddrop)
.unwrap_or_default();
chat::add_device_msg(
context,
id,
format!("{} verified.", peerstate.addr.unwrap_or_default()),
);
} else {
lot.state = LotState::QrFprWithoutAddr;
lot.text1 = Some(dc_format_fingerprint(&fingerprint));
}
} else {
if grpid.is_some() && grpname.is_some() {
lot.state = LotState::QrAskVerifyGroup;
lot.text1 = grpname;
lot.text2 = grpid
} else {
lot.state = LotState::QrAskVerifyContact;
}
lot.id = Contact::add_or_lookup(context, &name, &addr, Origin::UnhandledQrScan)
.map(|(id, _)| id)
.unwrap_or_default();
lot.fingerprint = Some(fingerprint);
lot.invitenumber = invitenumber;
lot.auth = auth;
}
lot
}
/// Extract address for the mailto scheme.
///
/// Scheme: `mailto:addr...?subject=...&body=..`
fn decode_mailto(context: &Context, qr: &str) -> Lot {
let payload = &qr[MAILTO_SCHEME.len()..];
let addr = if let Some(query_index) = payload.find('?') {
&payload[..query_index]
} else {
return format_err!("Invalid mailto found").into();
};
let addr = match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
};
let name = "".to_string();
Lot::from_address(context, name, addr)
}
/// Extract address for the smtp scheme.
///
/// Scheme: `SMTP:addr...:subject...:body...`
fn decode_smtp(context: &Context, qr: &str) -> Lot {
let payload = &qr[SMTP_SCHEME.len()..];
let addr = if let Some(query_index) = payload.find(':') {
&payload[..query_index]
} else {
return format_err!("Invalid SMTP found").into();
};
let addr = match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
};
let name = "".to_string();
Lot::from_address(context, name, addr)
}
/// Extract address for the matmsg scheme.
///
/// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;`
///
/// There may or may not be linebreaks after the fields.
fn decode_matmsg(context: &Context, qr: &str) -> Lot {
// Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field.
// we ignore this case.
let addr = if let Some(to_index) = qr.find("TO:") {
let addr = qr[to_index + 3..].trim();
if let Some(semi_index) = addr.find(';') {
addr[..semi_index].trim()
} else {
addr
}
} else {
return format_err!("Invalid MATMSG found").into();
};
let addr = match normalize_address(addr) {
Ok(addr) => addr,
Err(err) => return err.into(),
};
let name = "".to_string();
Lot::from_address(context, name, addr)
}
lazy_static! {
static ref VCARD_NAME_RE: regex::Regex =
regex::Regex::new(r"(?m)^N:([^;]*);([^;\n]*)").unwrap();
static ref VCARD_EMAIL_RE: regex::Regex =
regex::Regex::new(r"(?m)^EMAIL([^:\n]*):([^;\n]*)").unwrap();
}
/// Extract address for the matmsg scheme.
///
/// Scheme: `VCARD:BEGIN\nN:last name;first name;...;\nEMAIL;<type>:addr...;
fn decode_vcard(context: &Context, qr: &str) -> Lot {
let name = VCARD_NAME_RE
.captures(qr)
.map(|caps| {
let last_name = &caps[1];
let first_name = &caps[2];
format!("{} {}", first_name.trim(), last_name.trim())
})
.unwrap_or_default();
let addr = if let Some(caps) = VCARD_EMAIL_RE.captures(qr) {
match normalize_address(caps[2].trim()) {
Ok(addr) => addr,
Err(err) => return err.into(),
}
} else {
return format_err!("Bad e-mail address").into();
};
Lot::from_address(context, name, addr)
}
impl Lot {
pub fn from_text(text: impl AsRef<str>) -> Self {
let mut l = Lot::new();
l.state = LotState::QrText;
l.text1 = Some(text.as_ref().to_string());
l
}
pub fn from_url(url: impl AsRef<str>) -> Self {
let mut l = Lot::new();
l.state = LotState::QrUrl;
l.text1 = Some(url.as_ref().to_string());
l
}
pub fn from_address(context: &Context, name: String, addr: String) -> Self {
let mut l = Lot::new();
l.state = LotState::QrAddr;
l.id = match Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan) {
Ok((id, _)) => id,
Err(err) => return err.into(),
};
l
}
}
/// URL decodes a given address, does basic email validation on the result.
fn normalize_address(addr: &str) -> Result<String, Error> {
// urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases
let new_addr = percent_decode_str(addr).decode_utf8()?;
let new_addr = addr_normalize(&new_addr);
ensure!(may_be_valid_addr(&new_addr), "Bad e-mail address");
Ok(new_addr.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::dummy_context;
#[test]
fn test_decode_http() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "http://www.hello.com");
assert_eq!(res.get_state(), LotState::QrUrl);
assert_eq!(res.get_id(), 0);
assert_eq!(res.get_text1().unwrap(), "http://www.hello.com");
assert!(res.get_text2().is_none());
}
#[test]
fn test_decode_https() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "https://www.hello.com");
assert_eq!(res.get_state(), LotState::QrUrl);
assert_eq!(res.get_id(), 0);
assert_eq!(res.get_text1().unwrap(), "https://www.hello.com");
assert!(res.get_text2().is_none());
}
#[test]
fn test_decode_text() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "I am so cool");
assert_eq!(res.get_state(), LotState::QrText);
assert_eq!(res.get_id(), 0);
assert_eq!(res.get_text1().unwrap(), "I am so cool");
assert!(res.get_text2().is_none());
}
#[test]
fn test_decode_vcard() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD"
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
assert_eq!(contact.get_name(), "First Last");
}
#[test]
fn test_decode_matmsg() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"MATMSG:TO:\n\nstress@test.local ; \n\nSUB:\n\nSubject here\n\nBODY:\n\nhelloworld\n;;",
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
}
#[test]
fn test_decode_mailto() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"mailto:stress@test.local?subject=hello&body=world",
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
}
#[test]
fn test_decode_smtp() {
let ctx = dummy_context();
let res = check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld");
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAddr);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "stress@test.local");
}
#[test]
fn test_decode_openpgp_group() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&g=testtesttest&x=h-0oKQf2CDK&i=9JEXlxAqGM0&s=0V7LzL9cxRL"
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAskVerifyGroup);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "cli@deltachat.de");
}
#[test]
fn test_decode_openpgp_secure_join() {
let ctx = dummy_context();
let res = check_qr(
&ctx.ctx,
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&n=&i=TbnwJ6lSvD5&s=0ejvbdFSQxB"
);
println!("{:?}", res);
assert_eq!(res.get_state(), LotState::QrAskVerifyContact);
assert_ne!(res.get_id(), 0);
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
assert_eq!(contact.get_addr(), "cli@deltachat.de");
}
}

View File

@@ -38,13 +38,16 @@ impl Sql {
info!(context, 0, "Database closed.");
}
pub fn open(
&self,
context: &Context,
dbfile: &std::path::Path,
flags: libc::c_int,
) -> Result<()> {
open(context, self, dbfile, flags)
// return true on success, false on failure
pub fn open(&self, context: &Context, dbfile: &std::path::Path, flags: libc::c_int) -> bool {
match open(context, self, dbfile, flags) {
Ok(_) => true,
Err(Error::SqlAlreadyOpen) => false,
Err(_) => {
self.close(context);
false
}
}
}
pub fn execute<P>(&self, sql: &str, params: P) -> Result<usize>
@@ -224,10 +227,7 @@ impl Sql {
match res {
Ok(_) => Ok(()),
Err(err) => {
error!(
context,
0, "set_config(): Cannot change value for {}: {:?}", key, &err
);
error!(context, 0, "set_config(): Cannot change value. {:?}", &err);
Err(err.into())
}
}
@@ -286,7 +286,7 @@ impl Sql {
fn table_exists(conn: &Connection, name: impl AsRef<str>) -> Result<bool> {
let mut exists = false;
conn.pragma(None, "table_info", &name.as_ref().to_string(), |_row| {
conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| {
// will only be executed if the info was found
exists = true;
Ok(())
@@ -830,7 +830,7 @@ where
&err,
querystr.as_ref()
);
Err(err)
Err(err.into())
}
}
}
@@ -1003,10 +1003,10 @@ pub fn housekeeping(context: &Context) {
let name_f = entry.file_name();
let name_s = name_f.to_string_lossy();
if is_file_in_use(&files_in_use, None, &name_s)
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s)
if is_file_in_use(&mut files_in_use, None, &name_s)
|| is_file_in_use(&mut files_in_use, Some(".increation"), &name_s)
|| is_file_in_use(&mut files_in_use, Some(".waveform"), &name_s)
|| is_file_in_use(&mut files_in_use, Some("-preview.jpg"), &name_s)
{
continue;
}

View File

@@ -232,7 +232,10 @@ mod tests {
use super::*;
use crate::test_utils::*;
use std::ffi::CString;
use crate::constants::DC_CONTACT_ID_SELF;
use crate::context::dc_context_new;
use crate::types::uintptr_t;
use num_traits::ToPrimitive;
@@ -250,18 +253,19 @@ mod tests {
#[test]
fn test_stock_str() {
let t = dummy_context();
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "No messages.");
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(ctx.stock_str(StockMessage::NoMessages), "No messages.");
}
fn test_stock_str_no_fallback_cb(
unsafe extern "C" fn test_stock_str_no_fallback_cb(
_ctx: &Context,
evt: Event,
d1: uintptr_t,
_d2: uintptr_t,
) -> uintptr_t {
if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() {
unsafe { "Hello there".strdup() as usize }
let tmp = CString::new("Hello there").unwrap();
dc_strdup(tmp.as_ptr()) as usize
} else {
0
}
@@ -269,16 +273,16 @@ mod tests {
#[test]
fn test_stock_str_no_fallback() {
let t = test_context(Some(Box::new(test_stock_str_no_fallback_cb)));
let t = test_context(Some(test_stock_str_no_fallback_cb));
assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there");
}
#[test]
fn test_stock_string_repl_str() {
let t = dummy_context();
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
// uses %1$s substitution
assert_eq!(
t.ctx.stock_string_repl_str(StockMessage::Member, "42"),
ctx.stock_string_repl_str(StockMessage::Member, "42"),
"42 member(s)"
);
// We have no string using %1$d to test...
@@ -286,38 +290,36 @@ mod tests {
#[test]
fn test_stock_string_repl_int() {
let t = dummy_context();
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
t.ctx.stock_string_repl_int(StockMessage::Member, 42),
ctx.stock_string_repl_int(StockMessage::Member, 42),
"42 member(s)"
);
}
#[test]
fn test_stock_string_repl_str2() {
let t = dummy_context();
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
t.ctx
.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"),
ctx.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"),
"Response from foo: bar"
);
}
#[test]
fn test_stock_system_msg_simple() {
let t = dummy_context();
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
t.ctx
.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0),
ctx.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0),
"Location streaming enabled."
)
}
#[test]
fn test_stock_system_msg_add_member_by_me() {
let t = dummy_context();
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
assert_eq!(
t.ctx.stock_system_msg(
ctx.stock_system_msg(
StockMessage::MsgAddMember,
"alice@example.com",
"",

View File

@@ -9,8 +9,9 @@ use tempfile::{tempdir, TempDir};
use crate::config::Config;
use crate::constants::{Event, KeyType};
use crate::context::{Context, ContextCallback};
use crate::context::{dc_context_new, dc_open, Context};
use crate::key;
use crate::types::dc_callback_t;
/// A Context and temporary directory.
///
@@ -27,15 +28,18 @@ pub struct TestContext {
/// "db.sqlite" in the [TestContext.dir] directory.
///
/// [Context]: crate::context::Context
pub fn test_context(callback: Option<Box<ContextCallback>>) -> TestContext {
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
let cb: Box<ContextCallback> = match callback {
Some(cb) => cb,
None => Box::new(|_, _, _, _| 0),
};
let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap();
TestContext { ctx: ctx, dir: dir }
pub fn test_context(cb: Option<dc_callback_t>) -> TestContext {
unsafe {
let mut ctx = dc_context_new(cb, std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
assert!(
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
"Failed to open {}",
dbfile.display(),
);
TestContext { ctx: ctx, dir: dir }
}
}
/// Return a dummy [TestContext].
@@ -47,8 +51,13 @@ pub fn dummy_context() -> TestContext {
test_context(None)
}
pub fn logging_cb(_ctx: &Context, evt: Event, _d1: uintptr_t, d2: uintptr_t) -> uintptr_t {
let to_str = unsafe { |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap() };
pub unsafe extern "C" fn logging_cb(
_ctx: &Context,
evt: Event,
_d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
match evt {
Event::INFO => println!("I: {}", to_str(d2)),
Event::WARNING => println!("W: {}", to_str(d2)),

View File

@@ -1,9 +1,21 @@
#![allow(non_camel_case_types)]
use crate::constants::Event;
use crate::context::Context;
pub use mmime::clist::*;
pub use rusqlite::ffi::*;
/// Callback function that should be given to dc_context_new().
///
/// @memberof Context
/// @param context The context object as returned by dc_context_new().
/// @param event one of the @ref DC_EVENT constants
/// @param data1 depends on the event parameter
/// @param data2 depends on the event parameter
/// @return return 0 unless stated otherwise in the event parameter documentation
pub type dc_callback_t =
unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t;
pub type dc_receive_imf_t = unsafe fn(
_: &Context,
_: *const libc::c_char,
@@ -22,9 +34,14 @@ pub type dc_precheck_imf_t =
pub type dc_set_config_t = fn(_: &Context, _: &str, _: Option<&str>) -> ();
pub type dc_get_config_t = fn(_: &Context, _: &str) -> Option<String>;
pub type sqlite_int64 = i64;
pub type sqlite3_int64 = sqlite_int64;
pub type int32_t = i32;
pub type int64_t = i64;
pub type uintptr_t = libc::uintptr_t;
pub type size_t = libc::size_t;
pub type ssize_t = libc::ssize_t;
pub type uint32_t = libc::c_uint;
pub type uint8_t = libc::c_uchar;
pub type uint16_t = libc::c_ushort;

View File

@@ -54,6 +54,27 @@ pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char
}
}
pub(crate) unsafe fn strncasecmp(
s1: *const libc::c_char,
s2: *const libc::c_char,
n: libc::size_t,
) -> libc::c_int {
let s1 = std::ffi::CStr::from_ptr(s1)
.to_string_lossy()
.to_lowercase();
let s2 = std::ffi::CStr::from_ptr(s2)
.to_string_lossy()
.to_lowercase();
let m1 = std::cmp::min(n, s1.len());
let m2 = std::cmp::min(n, s2.len());
if s1[..m1] == s2[..m2] {
0
} else {
1
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -6,13 +6,19 @@ use std::ffi::CString;
use mmime::mailimf_types::*;
use tempfile::{tempdir, TempDir};
use deltachat::chat::{self, Chat};
use deltachat::config;
use deltachat::constants::*;
use deltachat::contact::*;
use deltachat::context::*;
use deltachat::dc_chat::*;
use deltachat::dc_configure::*;
use deltachat::dc_imex::*;
use deltachat::dc_location::*;
use deltachat::dc_lot::*;
use deltachat::dc_mimeparser::*;
use deltachat::dc_qr::*;
use deltachat::dc_saxparser::*;
use deltachat::dc_securejoin::*;
use deltachat::dc_tools::*;
use deltachat::key::*;
use deltachat::keyring::*;
@@ -33,100 +39,125 @@ static mut S_EM_SETUPFILE: *const libc::c_char =
as *const u8 as *const libc::c_char;
unsafe fn stress_functions(context: &Context) {
if dc_file_exist(context, "$BLOBDIR/foobar")
|| dc_file_exist(context, "$BLOBDIR/dada")
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
{
dc_delete_file(context, "$BLOBDIR/foobar");
dc_delete_file(context, "$BLOBDIR/dada");
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
dc_delete_file(context, "$BLOBDIR/foobar-folder");
}
dc_write_file(
context,
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
7i32 as size_t,
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
starttag_cb: None,
endtag_cb: None,
text_cb: None,
userdata: 0 as *mut libc::c_void,
};
dc_saxparser_init(&mut saxparser, 0 as *mut libc::c_void);
dc_saxparser_parse(
&mut saxparser,
b"<tag attr=val=\x00" as *const u8 as *const libc::c_char,
);
assert!(dc_file_exist(context, "$BLOBDIR/foobar",));
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx"));
assert_eq!(
dc_get_filebytes(context, "$BLOBDIR/foobar",),
7i32 as libc::c_ulonglong
dc_saxparser_parse(
&mut saxparser,
b"<tag attr=\"val\"=\x00" as *const u8 as *const libc::c_char,
);
let abs_path: *mut libc::c_char = dc_mprintf(
b"%s/%s\x00" as *const u8 as *const libc::c_char,
context.get_blobdir(),
b"foobar\x00" as *const u8 as *const libc::c_char,
);
assert!(dc_is_blobdir_path(context, as_str(abs_path)));
assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo"));
assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo"));
assert!(dc_file_exist(context, as_path(abs_path)));
free(abs_path as *mut libc::c_void);
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
let mut buf_bytes: size_t = 0;
assert_eq!(
dc_read_file(
if 0 != dc_is_open(context) {
if dc_file_exist(context, "$BLOBDIR/foobar")
|| dc_file_exist(context, "$BLOBDIR/dada")
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
{
dc_delete_file(context, "$BLOBDIR/foobar");
dc_delete_file(context, "$BLOBDIR/dada");
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
dc_delete_file(context, "$BLOBDIR/foobar-folder");
}
dc_write_file(
context,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
&mut buf,
&mut buf_bytes,
),
1
);
assert_eq!(buf_bytes, 7);
assert_eq!(
std::str::from_utf8(std::slice::from_raw_parts(buf as *const u8, buf_bytes)).unwrap(),
"content"
);
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
7i32 as size_t,
);
assert!(dc_file_exist(context, "$BLOBDIR/foobar",));
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx"));
assert_eq!(
dc_get_filebytes(context, "$BLOBDIR/foobar",),
7i32 as libc::c_ulonglong
);
free(buf as *mut _);
assert!(dc_delete_file(context, "$BLOBDIR/foobar"));
assert!(dc_delete_file(context, "$BLOBDIR/dada"));
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
assert!(dc_delete_file(context, "$BLOBDIR/foobar-folder"));
let fn0: *mut libc::c_char = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
);
assert!(!fn0.is_null());
assert_eq!(
strcmp(
let abs_path: *mut libc::c_char = dc_mprintf(
b"%s/%s\x00" as *const u8 as *const libc::c_char,
context.get_blobdir(),
b"foobar\x00" as *const u8 as *const libc::c_char,
);
assert!(dc_is_blobdir_path(context, abs_path));
assert!(dc_is_blobdir_path(
context,
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
));
assert!(!dc_is_blobdir_path(
context,
b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
));
assert!(dc_file_exist(context, as_path(abs_path)));
free(abs_path as *mut libc::c_void);
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
let mut buf_bytes: size_t = 0;
assert_eq!(
dc_read_file(
context,
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
&mut buf,
&mut buf_bytes,
),
1
);
assert_eq!(buf_bytes, 7);
assert_eq!(
std::str::from_utf8(std::slice::from_raw_parts(buf as *const u8, buf_bytes)).unwrap(),
"content"
);
free(buf as *mut _);
assert!(dc_delete_file(context, "$BLOBDIR/foobar"));
assert!(dc_delete_file(context, "$BLOBDIR/dada"));
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
assert!(dc_delete_file(context, "$BLOBDIR/foobar-folder"));
let fn0: *mut libc::c_char = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
);
assert!(!fn0.is_null());
assert_eq!(
strcmp(
fn0,
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
),
0
);
dc_write_file(
context,
fn0,
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
),
0
);
dc_write_file(
context,
fn0,
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
7i32 as size_t,
);
let fn1: *mut libc::c_char = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
);
assert!(!fn1.is_null());
assert_eq!(
strcmp(
fn1,
b"$BLOBDIR/foobar-1.dadada\x00" as *const u8 as *const libc::c_char,
),
0
);
assert!(dc_delete_file(context, as_path(fn0)));
free(fn0 as *mut libc::c_void);
free(fn1 as *mut libc::c_void);
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
7i32 as size_t,
);
let fn1: *mut libc::c_char = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
b"foobar.dadada\x00" as *const u8 as *const libc::c_char,
);
assert!(!fn1.is_null());
assert_eq!(
strcmp(
fn1,
b"$BLOBDIR/foobar-1.dadada\x00" as *const u8 as *const libc::c_char,
),
0
);
assert!(dc_delete_file(context, as_path(fn0)));
free(fn0 as *mut libc::c_void);
free(fn1 as *mut libc::c_void);
}
let res = context.get_config(config::Config::SysConfigKeys).unwrap();
@@ -406,55 +437,63 @@ unsafe fn stress_functions(context: &Context) {
0
);
free(buf_1 as *mut libc::c_void);
if 0 != dc_is_configured(context) {
let setupcode = dc_create_setup_code(context);
let setupcode_c = CString::yolo(setupcode.clone());
let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
let setupfile_c = CString::yolo(setupfile);
let payload: *mut libc::c_char;
let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
assert!(payload.is_null());
assert!(!dc_split_armored_data(
payload,
&mut headerline_2,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
));
assert!(!headerline_2.is_null());
assert_eq!(
strcmp(
headerline_2,
b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
),
0
);
free(payload as *mut libc::c_void);
}
// Cant check, no configured context
// assert!(dc_is_configured(context) != 0, "Missing configured context");
if 0 != dc_is_configured(context) {
let qr: *mut libc::c_char = dc_get_securejoin_qr(context, 0i32 as uint32_t);
assert!(
!(strlen(qr) > 55
&& strncmp(
qr,
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
12,
) == 0i32
&& strncmp(
&mut *qr.offset(52isize),
b"#a=\x00" as *const u8 as *const libc::c_char,
3,
) == 0i32)
);
let mut res: *mut dc_lot_t = dc_check_qr(context, qr);
assert!(res.is_null());
assert!(!((*res).state == 200i32 || (*res).state == 220i32 || (*res).state == 230i32));
// let setupcode = dc_create_setup_code(context);
// let setupcode_c = CString::yolo(setupcode.clone());
// let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
// let setupfile_c = CString::yolo(setupfile);
// let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
// 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,
// ));
// assert!(!headerline_2.is_null());
// assert_eq!(
// strcmp(
// headerline_2,
// b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
// ),
// 0
// );
// free(payload as *mut libc::c_void);
// Cant check, no configured context
// assert!(dc_is_configured(context) != 0, "missing configured context");
// let qr = dc_get_securejoin_qr(context, 0);
// assert!(!qr.is_null(), "Invalid qr code generated");
// let qr_r = as_str(qr);
// assert!(qr_r.len() > 55);
// assert!(qr_r.starts_with("OPENPGP4FPR:"));
// let res = dc_check_qr(context, qr);
// let s = res.get_state();
// assert!(
// s == QrState::AskVerifyContact
// || s == QrState::FprMissmatch
// || s == QrState::FprWithoutAddr
// );
// free(qr.cast());
dc_lot_unref(res);
free(qr as *mut libc::c_void);
res =
dc_check_qr(context,
b"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD\x00"
as *const u8 as *const libc::c_char);
assert!(res.is_null());
assert!(!((*res).state == 320i32));
assert!(!((*res).id != 0i32 as libc::c_uint));
dc_lot_unref(res);
};
}
#[test]
@@ -623,7 +662,12 @@ fn test_encryption_decryption() {
}
}
fn cb(_context: &Context, _event: Event, _data1: uintptr_t, _data2: uintptr_t) -> uintptr_t {
unsafe extern "C" fn cb(
_context: &Context,
_event: Event,
_data1: uintptr_t,
_data2: uintptr_t,
) -> uintptr_t {
0
}
@@ -633,13 +677,55 @@ struct TestContext {
dir: TempDir,
}
fn create_test_context() -> TestContext {
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap();
assert!(
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
"Failed to open {}",
dbfile.display()
);
TestContext { ctx: ctx, dir: dir }
}
#[test]
fn test_dc_kml_parse() {
unsafe {
let context = create_test_context();
let xml: *const libc::c_char =
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</wHeN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></Placemark>\n</Document>\n</kml>\x00"
as *const u8 as *const libc::c_char;
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
assert!(!kml.addr.is_null());
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
let locations_ref = &kml.locations.as_ref().unwrap();
assert_eq!(locations_ref.len(), 2);
assert!(locations_ref[0].latitude > 53.6f64);
assert!(locations_ref[0].latitude < 53.8f64);
assert!(locations_ref[0].longitude > 9.3f64);
assert!(locations_ref[0].longitude < 9.5f64);
assert!(locations_ref[0].accuracy > 31.9f64);
assert!(locations_ref[0].accuracy < 32.1f64);
assert_eq!(locations_ref[0].timestamp, 1551906597);
assert!(locations_ref[1].latitude > 63.6f64);
assert!(locations_ref[1].latitude < 63.8f64);
assert!(locations_ref[1].longitude > 19.3f64);
assert!(locations_ref[1].longitude < 19.5f64);
assert!(locations_ref[1].accuracy > 2.4f64);
assert!(locations_ref[1].accuracy < 2.6f64);
assert_eq!(locations_ref[1].timestamp, 1544739072);
dc_kml_unref(&mut kml);
}
}
#[test]
fn test_dc_mimeparser_with_context() {
unsafe {
@@ -673,7 +759,7 @@ fn test_dc_mimeparser_with_context() {
#[test]
fn test_dc_get_oauth2_url() {
let ctx = create_test_context();
let ctx = unsafe { create_test_context() };
let addr = "dignifiedquire@gmail.com";
let redirect_uri = "chat.delta:/com.b44t.messenger";
let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri);
@@ -683,7 +769,7 @@ fn test_dc_get_oauth2_url() {
#[test]
fn test_dc_get_oauth2_addr() {
let ctx = create_test_context();
let ctx = unsafe { create_test_context() };
let addr = "dignifiedquire@gmail.com";
let code = "fail";
let res = dc_get_oauth2_addr(&ctx.ctx, addr, code);
@@ -693,7 +779,7 @@ fn test_dc_get_oauth2_addr() {
#[test]
fn test_dc_get_oauth2_token() {
let ctx = create_test_context();
let ctx = unsafe { create_test_context() };
let addr = "dignifiedquire@gmail.com";
let code = "fail";
let res = dc_get_oauth2_access_token(&ctx.ctx, addr, code, 0);
@@ -711,18 +797,20 @@ fn test_stress_tests() {
#[test]
fn test_get_contacts() {
let context = create_test_context();
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
assert_eq!(contacts.len(), 0);
unsafe {
let context = create_test_context();
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
assert_eq!(contacts.len(), 0);
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(id, 0);
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(id, 0);
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
assert_eq!(contacts.len(), 1);
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
assert_eq!(contacts.len(), 1);
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
assert_eq!(contacts.len(), 0);
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
assert_eq!(contacts.len(), 0);
}
}
#[test]
@@ -732,14 +820,29 @@ fn test_chat() {
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
assert_ne!(contact1, 0);
let chat_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
assert!(chat_id > 9, "chat_id too small {}", chat_id);
let chat = Chat::load_from_db(&context.ctx, chat_id).unwrap();
let chat = dc_chat_new(&context.ctx);
assert!(dc_chat_load_from_db(chat, chat_id));
let chat2_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
assert_eq!(chat2_id, chat_id);
let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap();
let chat2 = dc_chat_new(&context.ctx);
assert!(dc_chat_load_from_db(chat2, chat2_id));
assert_eq!(chat2.name, chat.name);
assert_eq!(as_str((*chat2).name), as_str((*chat).name));
}
}
#[test]
fn test_wrong_db() {
unsafe {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None);
assert!(!res);
}
}