mirror of
https://github.com/chatmail/core.git
synced 2026-04-04 14:32:11 +03:00
Compare commits
47 Commits
encryption
...
feat_ui_co
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ab014f96f | ||
|
|
70a69d5313 | ||
|
|
056faad029 | ||
|
|
aefddf7f5e | ||
|
|
5ce27b16f1 | ||
|
|
8302d6833d | ||
|
|
649c2eb676 | ||
|
|
a0b5e32f98 | ||
|
|
e73671a6ff | ||
|
|
9503aca78d | ||
|
|
dfaa8e4529 | ||
|
|
cbc3579e9a | ||
|
|
dd2e3d35fd | ||
|
|
ca9dccfcd7 | ||
|
|
64b00fce7d | ||
|
|
177ab0229a | ||
|
|
68b5e34fed | ||
|
|
2eda839303 | ||
|
|
71a01d3002 | ||
|
|
995548002b | ||
|
|
38ad16887b | ||
|
|
c20e8f7613 | ||
|
|
5bd4606854 | ||
|
|
e7b198849d | ||
|
|
3ab0d74af2 | ||
|
|
7ed5a8e72f | ||
|
|
57daa0f7f0 | ||
|
|
2dd3f169db | ||
|
|
b97b618b4b | ||
|
|
bb12488200 | ||
|
|
706a97b013 | ||
|
|
1cdb9c733a | ||
|
|
ffc525af9e | ||
|
|
1576dc1d13 | ||
|
|
4fbb5fbb25 | ||
|
|
e9da21a02e | ||
|
|
6c4d7ad8cc | ||
|
|
f1c026c5ec | ||
|
|
3d61c06ea9 | ||
|
|
22c1ee1f55 | ||
|
|
971960a242 | ||
|
|
69f8973339 | ||
|
|
bf1d9b6d06 | ||
|
|
6a2368f83c | ||
|
|
d0960f7f7f | ||
|
|
e5ad697466 | ||
|
|
188eab5faf |
@@ -41,7 +41,8 @@ if [ -n "$TESTS" ]; then
|
||||
# see https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||
# unset DCC_PY_LIVECONFIG
|
||||
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py35,py36,py37,auditwheels
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py35,py36,py37,auditwheels -- -k "not qr"
|
||||
tox --workdir "$TOXWORKDIR" -e py35,py36,py37 -- -k "qr"
|
||||
popd
|
||||
fi
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,10 +28,10 @@ use num_traits::FromPrimitive;
|
||||
/// Argument is a bitmask, executing single or multiple actions in one call.
|
||||
/// e.g. bitmask 7 triggers actions definded with bits 1, 2 and 4.
|
||||
pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
info!(context, 0, "Resetting tables ({})...", bits);
|
||||
info!(context, "Resetting tables ({})...", bits);
|
||||
if 0 != bits & 1 {
|
||||
sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]).unwrap();
|
||||
info!(context, 0, "(1) Jobs reset.");
|
||||
info!(context, "(1) Jobs reset.");
|
||||
}
|
||||
if 0 != bits & 2 {
|
||||
sql::execute(
|
||||
@@ -41,11 +41,11 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
info!(context, 0, "(2) Peerstates reset.");
|
||||
info!(context, "(2) Peerstates reset.");
|
||||
}
|
||||
if 0 != bits & 4 {
|
||||
sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]).unwrap();
|
||||
info!(context, 0, "(4) Private keypairs reset.");
|
||||
info!(context, "(4) Private keypairs reset.");
|
||||
}
|
||||
if 0 != bits & 8 {
|
||||
sql::execute(
|
||||
@@ -84,7 +84,7 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]).unwrap();
|
||||
info!(context, 0, "(8) Rest but server config reset.");
|
||||
info!(context, "(8) Rest but server config reset.");
|
||||
}
|
||||
|
||||
context.call_cb(Event::MSGS_CHANGED, 0, 0);
|
||||
@@ -122,7 +122,7 @@ unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) ->
|
||||
/// @return 1=success, 0=error.
|
||||
unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
|
||||
if !context.sql.is_open() {
|
||||
error!(context, 0, "Import: Database not opened.");
|
||||
error!(context, "Import: Database not opened.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
} else {
|
||||
let rs = context.sql.get_config(context, "import_spec");
|
||||
if rs.is_none() {
|
||||
error!(context, 0, "Import: No file or folder given.");
|
||||
error!(context, "Import: No file or folder given.");
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
ok_to_continue = true;
|
||||
@@ -166,7 +166,6 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
if dir.is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import: Cannot open directory \"{}\".",
|
||||
as_str(real_spec),
|
||||
);
|
||||
@@ -182,7 +181,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
let name = name_f.to_string_lossy();
|
||||
if name.ends_with(".eml") {
|
||||
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
|
||||
info!(context, 0, "Import: {}", path_plus_name);
|
||||
info!(context, "Import: {}", path_plus_name);
|
||||
let path_plus_name_c = CString::yolo(path_plus_name);
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
|
||||
read_cnt += 1
|
||||
@@ -195,7 +194,6 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
if ok_to_continue2 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Import: {} items read from \"{}\".",
|
||||
read_cnt,
|
||||
as_str(real_spec)
|
||||
@@ -228,7 +226,6 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
let msgtext = dc_msg_get_text(msg);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{} [{}]",
|
||||
prefix.as_ref(),
|
||||
dc_msg_get_id(msg) as libc::c_int,
|
||||
@@ -268,7 +265,6 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
if msg_id == 9 as libc::c_uint {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"--------------------------------------------------------------------------------"
|
||||
);
|
||||
|
||||
@@ -276,7 +272,7 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
} else if msg_id > 0 {
|
||||
if lines_out == 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
context,
|
||||
"--------------------------------------------------------------------------------",
|
||||
);
|
||||
lines_out += 1
|
||||
@@ -288,7 +284,7 @@ unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) -> Result<(), Error
|
||||
if lines_out > 0 {
|
||||
info!(
|
||||
context,
|
||||
0, "--------------------------------------------------------------------------------"
|
||||
"--------------------------------------------------------------------------------"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
@@ -305,7 +301,7 @@ unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
let name = contact.get_name();
|
||||
let addr = contact.get_addr();
|
||||
let verified_state = contact.is_verified();
|
||||
let verified_state = contact.is_verified(context);
|
||||
let verified_str = if VerifiedStatus::Unverified != verified_state {
|
||||
if verified_state == VerifiedStatus::BidirectVerified {
|
||||
" √√"
|
||||
@@ -337,7 +333,7 @@ unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
);
|
||||
}
|
||||
|
||||
info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2);
|
||||
info!(context, "Contact#{}: {}{}", contact_id, line, line2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -480,7 +476,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
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 setupcodebegin = dc_msg_get_setupcodebegin(context, &msg);
|
||||
println!(
|
||||
"The setup code for setup message Msg#{} starts with: {}",
|
||||
msg_id,
|
||||
@@ -496,7 +492,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
!arg1.is_empty() && !arg2.is_empty(),
|
||||
"Arguments <msg-id> <setup-code> expected"
|
||||
);
|
||||
if 0 == dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
|
||||
if !dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
|
||||
bail!("Continue key transfer failed");
|
||||
}
|
||||
}
|
||||
@@ -554,9 +550,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"get" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <key> missing.");
|
||||
let key = config::Config::from_str(&arg1)?;
|
||||
let val = context.get_config(key);
|
||||
println!("{}={:?}", key, val);
|
||||
let val = context.get_config_from_str(&arg1);
|
||||
println!("{}={:?}", &arg1.to_string(), val);
|
||||
}
|
||||
"info" => {
|
||||
println!("{}", to_string(dc_get_info(context)));
|
||||
@@ -579,17 +574,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let cnt = chatlist.len();
|
||||
if cnt > 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
context,
|
||||
"================================================================================"
|
||||
);
|
||||
|
||||
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_subtitle = chat.get_subtitle(context);
|
||||
let temp_name = chat.get_name();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}] [{} fresh]",
|
||||
chat_prefix(&chat),
|
||||
chat.get_id(),
|
||||
@@ -597,7 +591,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
temp_subtitle,
|
||||
chat::get_fresh_msg_cnt(context, chat.get_id()),
|
||||
);
|
||||
let lot = chatlist.get_summary(i, Some(&chat));
|
||||
let lot = chatlist.get_summary(context, i, Some(&chat));
|
||||
let statestr = if chat.is_archived() {
|
||||
" [Archived]"
|
||||
} else {
|
||||
@@ -614,7 +608,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let text2 = lot.get_text2();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}{}{}{} [{}]{}",
|
||||
text1.unwrap_or(""),
|
||||
if text1.is_some() { ": " } else { "" },
|
||||
@@ -628,13 +621,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
},
|
||||
);
|
||||
info!(
|
||||
context, 0,
|
||||
context,
|
||||
"================================================================================"
|
||||
);
|
||||
}
|
||||
}
|
||||
if location::is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
info!(context, 0, "Location streaming enabled.");
|
||||
info!(context, "Location streaming enabled.");
|
||||
}
|
||||
println!("{} chats", cnt);
|
||||
}
|
||||
@@ -653,11 +646,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let sel_chat = sel_chat.as_ref().unwrap();
|
||||
|
||||
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
|
||||
let temp2 = sel_chat.get_subtitle();
|
||||
let temp2 = sel_chat.get_subtitle(context);
|
||||
let temp_name = sel_chat.get_name();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}]{}",
|
||||
chat_prefix(sel_chat),
|
||||
sel_chat.get_id(),
|
||||
@@ -753,7 +745,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
|
||||
info!(context, 0, "Memberlist:");
|
||||
info!(context, "Memberlist:");
|
||||
|
||||
log_contactlist(context, &contacts);
|
||||
println!(
|
||||
@@ -781,7 +773,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let marker = location.marker.as_ref().unwrap_or(&default_marker);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
|
||||
location.location_id,
|
||||
dc_timestamp_to_str(location.timestamp),
|
||||
@@ -795,7 +786,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
}
|
||||
if locations.is_empty() {
|
||||
info!(context, 0, "No locations.");
|
||||
info!(context, "No locations.");
|
||||
}
|
||||
}
|
||||
"sendlocations" => {
|
||||
@@ -844,14 +835,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "No file given.");
|
||||
|
||||
let mut msg = dc_msg_new(
|
||||
context,
|
||||
if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
} else {
|
||||
Viewtype::File
|
||||
},
|
||||
);
|
||||
let mut msg = dc_msg_new(if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
} else {
|
||||
Viewtype::File
|
||||
});
|
||||
dc_msg_set_file(&mut msg, arg1_c, ptr::null());
|
||||
if !arg2.is_empty() {
|
||||
dc_msg_set_text(&mut msg, arg2_c);
|
||||
@@ -876,7 +864,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
if !arg1.is_empty() {
|
||||
let mut draft = dc_msg_new(context, Viewtype::Text);
|
||||
let mut draft = dc_msg_new(Viewtype::Text);
|
||||
dc_msg_set_text(&mut draft, arg1_c);
|
||||
chat::set_draft(
|
||||
context,
|
||||
|
||||
@@ -495,7 +495,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
configure(&ctx.read().unwrap());
|
||||
}
|
||||
"oauth2" => {
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(&config::Config::Addr) {
|
||||
let oauth2_url = dc_get_oauth2_url(
|
||||
&ctx.read().unwrap(),
|
||||
&addr,
|
||||
|
||||
@@ -101,7 +101,7 @@ fn main() {
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(0, None);
|
||||
let summary = chats.get_summary(&ctx, 0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
|
||||
@@ -65,7 +65,7 @@ Afterwards ``which python`` tells you that it comes out of the "venv"
|
||||
directory that contains all python install artifacts. Let's first
|
||||
install test tools::
|
||||
|
||||
pip install pytest pytest-timeout requests
|
||||
pip install pytest pytest-timeout pytest-rerunfailures requests
|
||||
|
||||
then cargo-build and install the deltachat bindings::
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ passenv =
|
||||
DCC_PY_LIVECONFIG
|
||||
deps =
|
||||
pytest
|
||||
pytest-faulthandler
|
||||
pytest-rerunfailures
|
||||
pytest-timeout
|
||||
pytest-xdist
|
||||
auditwheel
|
||||
pdbpp
|
||||
requests
|
||||
|
||||
@@ -51,11 +54,12 @@ commands =
|
||||
|
||||
|
||||
[pytest]
|
||||
addopts = -v -rs
|
||||
addopts = -v -rs --reruns 3 --reruns-delay 2
|
||||
python_files = tests/test_*.py
|
||||
norecursedirs = .tox
|
||||
xfail_strict=true
|
||||
timeout = 60
|
||||
timeout = 60
|
||||
timeout_method = thread
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
|
||||
134
src/chat.rs
134
src/chat.rs
@@ -21,8 +21,7 @@ use std::ptr;
|
||||
/// and are not updated on database changes;
|
||||
/// if you want an update, you have to recreate the object.
|
||||
#[derive(Clone)]
|
||||
pub struct Chat<'a> {
|
||||
pub context: &'a Context,
|
||||
pub struct Chat {
|
||||
pub id: u32,
|
||||
pub typ: Chattype,
|
||||
pub name: String,
|
||||
@@ -34,8 +33,8 @@ pub struct Chat<'a> {
|
||||
is_sending_locations: bool,
|
||||
}
|
||||
|
||||
impl<'a> Chat<'a> {
|
||||
pub fn load_from_db(context: &'a Context, chat_id: u32) -> Result<Self, Error> {
|
||||
impl Chat {
|
||||
pub fn load_from_db(context: &Context, chat_id: u32) -> Result<Self, Error> {
|
||||
let res = context.sql.query_row(
|
||||
"SELECT c.id,c.type,c.name, c.grpid,c.param,c.archived, \
|
||||
c.blocked, c.gossiped_timestamp, c.locations_send_until \
|
||||
@@ -43,7 +42,6 @@ impl<'a> Chat<'a> {
|
||||
params![chat_id as i32],
|
||||
|row| {
|
||||
let c = Chat {
|
||||
context,
|
||||
id: row.get(0)?,
|
||||
typ: row.get(1)?,
|
||||
name: row.get::<_, String>(2)?,
|
||||
@@ -65,7 +63,7 @@ impl<'a> Chat<'a> {
|
||||
_ => {
|
||||
error!(
|
||||
context,
|
||||
0, "chat: failed to load from db {}: {:?}", chat_id, err
|
||||
"chat: failed to load from db {}: {:?}", chat_id, err
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
@@ -73,23 +71,23 @@ impl<'a> Chat<'a> {
|
||||
Ok(mut chat) => {
|
||||
match chat.id {
|
||||
DC_CHAT_ID_DEADDROP => {
|
||||
chat.name = chat.context.stock_str(StockMessage::DeadDrop).into();
|
||||
chat.name = context.stock_str(StockMessage::DeadDrop).into();
|
||||
}
|
||||
DC_CHAT_ID_ARCHIVED_LINK => {
|
||||
let tempname = chat.context.stock_str(StockMessage::ArchivedChats);
|
||||
let cnt = dc_get_archived_cnt(chat.context);
|
||||
let tempname = context.stock_str(StockMessage::ArchivedChats);
|
||||
let cnt = dc_get_archived_cnt(context);
|
||||
chat.name = format!("{} ({})", tempname, cnt);
|
||||
}
|
||||
DC_CHAT_ID_STARRED => {
|
||||
chat.name = chat.context.stock_str(StockMessage::StarredMsgs).into();
|
||||
chat.name = context.stock_str(StockMessage::StarredMsgs).into();
|
||||
}
|
||||
_ => {
|
||||
if chat.typ == Chattype::Single {
|
||||
let contacts = get_chat_contacts(chat.context, chat.id);
|
||||
let contacts = get_chat_contacts(context, chat.id);
|
||||
let mut chat_name = "Err [Name not found]".to_owned();
|
||||
|
||||
if !(*contacts).is_empty() {
|
||||
if let Ok(contact) = Contact::get_by_id(chat.context, contacts[0]) {
|
||||
if let Ok(contact) = Contact::get_by_id(context, contacts[0]) {
|
||||
chat_name = contact.get_display_name().to_owned();
|
||||
}
|
||||
}
|
||||
@@ -98,7 +96,7 @@ impl<'a> Chat<'a> {
|
||||
}
|
||||
|
||||
if chat.param.exists(Param::Selftalk) {
|
||||
chat.name = chat.context.stock_str(StockMessage::SelfMsg).into();
|
||||
chat.name = context.stock_str(StockMessage::SelfMsg).into();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,10 +109,10 @@ impl<'a> Chat<'a> {
|
||||
self.param.exists(Param::Selftalk)
|
||||
}
|
||||
|
||||
pub fn update_param(&mut self) -> Result<(), Error> {
|
||||
pub fn update_param(&mut self, context: &Context) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
self.context,
|
||||
&self.context.sql,
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET param=? WHERE id=?",
|
||||
params![self.param.to_string(), self.id as i32],
|
||||
)
|
||||
@@ -132,22 +130,18 @@ impl<'a> Chat<'a> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn get_subtitle(&self) -> String {
|
||||
pub fn get_subtitle(&self, context: &Context) -> String {
|
||||
// returns either the address or the number of chat members
|
||||
|
||||
if self.typ == Chattype::Single && self.param.exists(Param::Selftalk) {
|
||||
return self
|
||||
.context
|
||||
.stock_str(StockMessage::SelfTalkSubTitle)
|
||||
.into();
|
||||
return context.stock_str(StockMessage::SelfTalkSubTitle).into();
|
||||
}
|
||||
|
||||
if self.typ == Chattype::Single {
|
||||
return self
|
||||
.context
|
||||
return context
|
||||
.sql
|
||||
.query_row_col(
|
||||
self.context,
|
||||
context,
|
||||
"SELECT c.addr FROM chats_contacts cc \
|
||||
LEFT JOIN contacts c ON c.id=cc.contact_id \
|
||||
WHERE cc.chat_id=?;",
|
||||
@@ -159,11 +153,10 @@ impl<'a> Chat<'a> {
|
||||
|
||||
if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup {
|
||||
if self.id == 1 {
|
||||
return self.context.stock_str(StockMessage::DeadDrop).into();
|
||||
return context.stock_str(StockMessage::DeadDrop).into();
|
||||
}
|
||||
let cnt = get_chat_contact_cnt(self.context, self.id);
|
||||
return self
|
||||
.context
|
||||
let cnt = get_chat_contact_cnt(context, self.id);
|
||||
return context
|
||||
.stock_string_repl_int(StockMessage::Member, cnt)
|
||||
.into();
|
||||
}
|
||||
@@ -171,10 +164,10 @@ impl<'a> Chat<'a> {
|
||||
return "Err".into();
|
||||
}
|
||||
|
||||
pub fn get_parent_mime_headers(&self) -> Option<(String, String, String)> {
|
||||
pub fn get_parent_mime_headers(&self, context: &Context) -> Option<(String, String, String)> {
|
||||
let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?));
|
||||
let params = params![self.id as i32, DC_CONTACT_ID_SELF as i32];
|
||||
let sql = &self.context.sql;
|
||||
let sql = &context.sql;
|
||||
let main_query = "SELECT rfc724_mid, mime_in_reply_to, mime_references \
|
||||
FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT max(timestamp) \
|
||||
FROM msgs WHERE chat_id=?1 AND from_id!=?2);";
|
||||
@@ -187,16 +180,16 @@ impl<'a> Chat<'a> {
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn get_profile_image(&self) -> Option<PathBuf> {
|
||||
pub fn get_profile_image(&self, context: &Context) -> Option<PathBuf> {
|
||||
if let Some(image_rel) = self.param.get(Param::ProfileImage) {
|
||||
if !image_rel.is_empty() {
|
||||
return Some(dc_get_abs_path_safe(self.context, image_rel));
|
||||
return Some(dc_get_abs_path_safe(context, image_rel));
|
||||
}
|
||||
} else if self.typ == Chattype::Single {
|
||||
let contacts = get_chat_contacts(self.context, self.id);
|
||||
let contacts = get_chat_contacts(context, self.id);
|
||||
if !contacts.is_empty() {
|
||||
if let Ok(contact) = Contact::get_by_id(self.context, contacts[0]) {
|
||||
return contact.get_profile_image();
|
||||
if let Ok(contact) = Contact::get_by_id(context, contacts[0]) {
|
||||
return contact.get_profile_image(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,13 +197,13 @@ impl<'a> Chat<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_color(&self) -> u32 {
|
||||
pub fn get_color(&self, context: &Context) -> u32 {
|
||||
let mut color = 0;
|
||||
|
||||
if self.typ == Chattype::Single {
|
||||
let contacts = get_chat_contacts(self.context, self.id);
|
||||
let contacts = get_chat_contacts(context, self.id);
|
||||
if !contacts.is_empty() {
|
||||
if let Ok(contact) = Contact::get_by_id(self.context, contacts[0]) {
|
||||
if let Ok(contact) = Contact::get_by_id(context, contacts[0]) {
|
||||
color = contact.get_color();
|
||||
}
|
||||
}
|
||||
@@ -260,7 +253,7 @@ impl<'a> Chat<'a> {
|
||||
|| self.typ == Chattype::Group
|
||||
|| self.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
error!(context, 0, "Cannot send to chat type #{}.", self.typ,);
|
||||
error!(context, "Cannot send to chat type #{}.", self.typ,);
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
@@ -296,7 +289,7 @@ impl<'a> Chat<'a> {
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot send message, contact for chat #{} not found.", self.id,
|
||||
"Cannot send message, contact for chat #{} not found.", self.id,
|
||||
);
|
||||
return Ok(0);
|
||||
}
|
||||
@@ -304,7 +297,7 @@ impl<'a> Chat<'a> {
|
||||
if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup {
|
||||
if self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 {
|
||||
self.param.remove(Param::Unpromoted);
|
||||
self.update_param().unwrap();
|
||||
self.update_param(context).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +331,6 @@ impl<'a> Chat<'a> {
|
||||
if prefer_encrypted != 1 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"[autocrypt] peerstate for {} is {}",
|
||||
state,
|
||||
if prefer_encrypted == 0 {
|
||||
@@ -350,7 +342,7 @@ impl<'a> Chat<'a> {
|
||||
all_mutual = 0;
|
||||
}
|
||||
} else {
|
||||
info!(context, 0, "[autocrypt] no peerstate for {}", state,);
|
||||
info!(context, "[autocrypt] no peerstate for {}", state,);
|
||||
can_encrypt = 0;
|
||||
all_mutual = 0;
|
||||
}
|
||||
@@ -360,7 +352,7 @@ impl<'a> Chat<'a> {
|
||||
match res {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "chat: failed to load peerstates: {:?}", err);
|
||||
warn!(context, "chat: failed to load peerstates: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +370,7 @@ impl<'a> Chat<'a> {
|
||||
msg.param.remove(Param::ErroneousE2ee);
|
||||
if !self.is_self_talk() {
|
||||
if let Some((parent_rfc724_mid, parent_in_reply_to, parent_references)) =
|
||||
self.get_parent_mime_headers()
|
||||
self.get_parent_mime_headers(context)
|
||||
{
|
||||
if !parent_rfc724_mid.is_empty() {
|
||||
new_in_reply_to = parent_rfc724_mid.clone();
|
||||
@@ -464,13 +456,12 @@ impl<'a> Chat<'a> {
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot send message, cannot insert to database (chat #{}).",
|
||||
self.id,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
error!(context, 0, "Cannot send message, not configured.",);
|
||||
error!(context, "Cannot send message, not configured.",);
|
||||
}
|
||||
|
||||
Ok(msg_id)
|
||||
@@ -541,7 +532,7 @@ pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, E
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
0, "Cannot create chat, contact {} does not exist.", contact_id,
|
||||
"Cannot create chat, contact {} does not exist.", contact_id,
|
||||
);
|
||||
return Err(err);
|
||||
} else {
|
||||
@@ -637,7 +628,7 @@ pub fn get_by_contact_id(context: &Context, contact_id: u32) -> Result<u32, Erro
|
||||
pub fn prepare_msg<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
msg: &mut Message<'a>,
|
||||
msg: &mut Message,
|
||||
) -> Result<u32, Error> {
|
||||
ensure!(
|
||||
chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
||||
@@ -667,13 +658,8 @@ pub fn msgtype_has_file(msgtype: Viewtype) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_msg_common<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
msg: &mut Message<'a>,
|
||||
) -> Result<u32, Error> {
|
||||
fn prepare_msg_common(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u32, Error> {
|
||||
msg.id = 0;
|
||||
msg.context = context;
|
||||
|
||||
if msg.type_0 == Viewtype::Text {
|
||||
// the caller should check if the message text is empty
|
||||
@@ -718,7 +704,7 @@ fn prepare_msg_common<'a>(
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0, "Attaching \"{}\" for message type #{}.", &path_filename, msg.type_0
|
||||
"Attaching \"{}\" for message type #{}.", &path_filename, msg.type_0
|
||||
);
|
||||
} else {
|
||||
bail!("Cannot send messages of type #{}.", msg.type_0);
|
||||
@@ -751,7 +737,7 @@ fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) -> boo
|
||||
match packed.parse::<Params>() {
|
||||
Ok(param) => param.exists(Param::GuranteeE2ee),
|
||||
Err(err) => {
|
||||
error!(context, 0, "invalid params stored: '{}', {:?}", packed, err);
|
||||
error!(context, "invalid params stored: '{}', {:?}", packed, err);
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -789,11 +775,7 @@ pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> {
|
||||
/// However, this does not imply, the message really reached the recipient -
|
||||
/// sending may be delayed eg. due to network problems. However, from your
|
||||
/// view, you're done with the message. Sooner or later it will find its way.
|
||||
pub fn send_msg<'a>(
|
||||
context: &'a Context,
|
||||
chat_id: u32,
|
||||
msg: &mut Message<'a>,
|
||||
) -> Result<u32, Error> {
|
||||
pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u32, Error> {
|
||||
if msg.state != MessageState::OutPreparing {
|
||||
// automatically prepare normal messages
|
||||
prepare_msg_common(context, chat_id, msg)?;
|
||||
@@ -837,7 +819,7 @@ pub fn send_msg<'a>(
|
||||
}
|
||||
}
|
||||
msg.param.remove(Param::PrepForwards);
|
||||
dc_msg_save_param_to_disk(msg);
|
||||
dc_msg_save_param_to_disk(context, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -855,7 +837,7 @@ pub unsafe fn send_text_msg(
|
||||
chat_id
|
||||
);
|
||||
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
let mut msg = dc_msg_new(Viewtype::Text);
|
||||
msg.text = Some(text_to_send);
|
||||
send_msg(context, chat_id, &mut msg)
|
||||
}
|
||||
@@ -1297,7 +1279,7 @@ pub unsafe fn create_group_chat(
|
||||
|
||||
if chat_id != 0 {
|
||||
if 0 != add_to_chat_contacts_table(context, chat_id, 1) {
|
||||
let mut draft_msg = dc_msg_new(context, Viewtype::Text);
|
||||
let mut draft_msg = dc_msg_new(Viewtype::Text);
|
||||
dc_msg_set_text(&mut draft_msg, draft_txt.as_ptr());
|
||||
set_draft_raw(context, chat_id, Some(&mut draft_msg));
|
||||
}
|
||||
@@ -1342,7 +1324,7 @@ pub fn add_contact_to_chat_ex(
|
||||
if contact.is_err() || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return 0;
|
||||
}
|
||||
let mut msg = unsafe { dc_msg_new_untyped(context) };
|
||||
let mut msg = dc_msg_new_untyped();
|
||||
|
||||
reset_gossiped_timestamp(context, chat_id);
|
||||
let contact = contact.unwrap();
|
||||
@@ -1365,7 +1347,7 @@ pub fn add_contact_to_chat_ex(
|
||||
&& chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1
|
||||
{
|
||||
chat.param.remove(Param::Unpromoted);
|
||||
chat.update_param().unwrap();
|
||||
chat.update_param(context).unwrap();
|
||||
}
|
||||
let self_addr = context
|
||||
.sql
|
||||
@@ -1383,9 +1365,9 @@ pub fn add_contact_to_chat_ex(
|
||||
} else {
|
||||
// else continue and send status mail
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
if contact.is_verified() != VerifiedStatus::BidirectVerified {
|
||||
if contact.is_verified(context) != VerifiedStatus::BidirectVerified {
|
||||
error!(
|
||||
context, 0,
|
||||
context,
|
||||
"Only bidirectional verified contacts can be added to verified groups."
|
||||
);
|
||||
OK_TO_CONTINUE = false;
|
||||
@@ -1451,7 +1433,7 @@ pub fn set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) {
|
||||
if 0 != chat_id {
|
||||
info!(
|
||||
context,
|
||||
0, "set gossiped_timestamp for chat #{} to {}.", chat_id, timestamp,
|
||||
"set gossiped_timestamp for chat #{} to {}.", chat_id, timestamp,
|
||||
);
|
||||
|
||||
sql::execute(
|
||||
@@ -1464,7 +1446,7 @@ pub fn set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0, "set gossiped_timestamp for all chats to {}.", timestamp,
|
||||
"set gossiped_timestamp for all chats to {}.", timestamp,
|
||||
);
|
||||
sql::execute(
|
||||
context,
|
||||
@@ -1491,7 +1473,7 @@ pub unsafe fn remove_contact_from_chat(
|
||||
"Cannot remove special contact"
|
||||
);
|
||||
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
let mut msg = dc_msg_new_untyped();
|
||||
let mut success = false;
|
||||
|
||||
/* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */
|
||||
@@ -1590,7 +1572,7 @@ pub unsafe fn set_chat_name(
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
|
||||
|
||||
let chat = Chat::load_from_db(context, chat_id)?;
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
let mut msg = dc_msg_new_untyped();
|
||||
|
||||
if real_group_exists(context, chat_id) {
|
||||
if &chat.name == new_name.as_ref() {
|
||||
@@ -1684,9 +1666,9 @@ pub fn set_chat_profile_image(
|
||||
}
|
||||
|
||||
chat.param.set(Param::ProfileImage, &new_image_rel);
|
||||
if chat.update_param().is_ok() {
|
||||
if chat.update_param(context).is_ok() {
|
||||
if chat.is_promoted() {
|
||||
let mut msg = unsafe { dc_msg_new_untyped(context) };
|
||||
let mut msg = dc_msg_new_untyped();
|
||||
msg.param.set_int(Param::Cmd, DC_CMD_GROUPIMAGE_CHANGED);
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
@@ -1781,7 +1763,7 @@ pub unsafe fn forward_msgs(
|
||||
msg.param.set(Param::PrepForwards, new_msg_id.to_string());
|
||||
}
|
||||
|
||||
dc_msg_save_param_to_disk(&mut msg);
|
||||
dc_msg_save_param_to_disk(context, &mut msg);
|
||||
msg.param = save_param;
|
||||
} else {
|
||||
msg.state = MessageState::OutPending;
|
||||
|
||||
@@ -31,17 +31,12 @@ use crate::stock::StockMessage;
|
||||
/// first entry and only present on new messages, there is the rough idea that it can be optionally always
|
||||
/// present and sorted into the list by date. Rendering the deaddrop in the described way
|
||||
/// would not add extra work in the UI then.
|
||||
pub struct Chatlist<'a> {
|
||||
context: &'a Context,
|
||||
pub struct Chatlist {
|
||||
/// Stores pairs of `chat_id, message_id`
|
||||
ids: Vec<(u32, u32)>,
|
||||
}
|
||||
|
||||
impl<'a> Chatlist<'a> {
|
||||
pub fn get_context(&self) -> &Context {
|
||||
self.context
|
||||
}
|
||||
|
||||
impl Chatlist {
|
||||
/// Get a list of chats.
|
||||
/// The list can be filtered by query parameters.
|
||||
///
|
||||
@@ -85,7 +80,7 @@ impl<'a> Chatlist<'a> {
|
||||
/// `query_contact_id`: An optional contact ID for filtering the list. Only chats including this contact ID
|
||||
/// are returned.
|
||||
pub fn try_load(
|
||||
context: &'a Context,
|
||||
context: &Context,
|
||||
listflags: usize,
|
||||
query: Option<&str>,
|
||||
query_contact_id: Option<u32>,
|
||||
@@ -200,7 +195,7 @@ impl<'a> Chatlist<'a> {
|
||||
ids.push((DC_CHAT_ID_ARCHIVED_LINK, 0));
|
||||
}
|
||||
|
||||
Ok(Chatlist { context, ids })
|
||||
Ok(Chatlist { ids })
|
||||
}
|
||||
|
||||
/// Find out the number of chats.
|
||||
@@ -247,7 +242,7 @@ impl<'a> Chatlist<'a> {
|
||||
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
|
||||
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
|
||||
// 0 if not applicable.
|
||||
pub fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
|
||||
pub fn get_summary(&self, context: &Context, index: usize, chat: Option<&Chat>) -> Lot {
|
||||
// The summary is created by the chat, not by the last message.
|
||||
// This is because we may want to display drafts here or stuff as
|
||||
// "is typing".
|
||||
@@ -263,7 +258,7 @@ impl<'a> Chatlist<'a> {
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
|
||||
if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
} else {
|
||||
@@ -275,11 +270,11 @@ impl<'a> Chatlist<'a> {
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if 0 != lastmsg_id {
|
||||
if let Ok(lastmsg) = dc_msg_load_from_db(self.context, lastmsg_id) {
|
||||
if let Ok(lastmsg) = dc_msg_load_from_db(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();
|
||||
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();
|
||||
}
|
||||
|
||||
Some(lastmsg)
|
||||
@@ -294,14 +289,9 @@ impl<'a> Chatlist<'a> {
|
||||
ret.text2 = None;
|
||||
} else if lastmsg.is_none() || lastmsg.as_ref().unwrap().from_id == DC_CONTACT_ID_UNDEFINED
|
||||
{
|
||||
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
|
||||
ret.text2 = Some(context.stock_str(StockMessage::NoMessages).to_string());
|
||||
} else {
|
||||
ret.fill(
|
||||
&mut lastmsg.unwrap(),
|
||||
chat,
|
||||
lastcontact.as_ref(),
|
||||
self.context,
|
||||
);
|
||||
ret.fill(&mut lastmsg.unwrap(), chat, lastcontact.as_ref(), context);
|
||||
}
|
||||
|
||||
ret
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use strum::{EnumProperty, IntoEnumIterator};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
|
||||
|
||||
@@ -8,9 +10,11 @@ use crate::error::Error;
|
||||
use crate::job::*;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
pub const CONFIG_UI_PREFIX: &str = "ui.";
|
||||
|
||||
/// The available configuration keys.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, Display, EnumString, AsRefStr, EnumIter, EnumProperty,
|
||||
Debug, Clone, PartialEq, Eq, Display, EnumString, AsRefStr, EnumIter, EnumProperty,
|
||||
)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum Config {
|
||||
@@ -65,11 +69,17 @@ pub enum Config {
|
||||
SysMsgsizeMaxRecommended,
|
||||
#[strum(serialize = "sys.config_keys")]
|
||||
SysConfigKeys,
|
||||
Ui(String)
|
||||
}
|
||||
|
||||
impl Context {
|
||||
|
||||
pub fn get_config_from_str(&self, key: &str) -> Option<String> {
|
||||
self.get_config(&config_from_str(key))
|
||||
}
|
||||
|
||||
/// Get a configuration key. Returns `None` if no value is set, and no default value found.
|
||||
pub fn get_config(&self, key: Config) -> Option<String> {
|
||||
pub fn get_config(&self, key: &Config) -> Option<String> {
|
||||
let value = match key {
|
||||
Config::Selfavatar => {
|
||||
let rel_path = self.sql.get_config(self, key);
|
||||
@@ -78,6 +88,7 @@ impl Context {
|
||||
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
|
||||
Config::SysConfigKeys => Some(get_config_keys_string()),
|
||||
Config::Ui(key) => self.sql.get_config(self, format!("{}{}", CONFIG_UI_PREFIX, key)),
|
||||
_ => self.sql.get_config(self, key),
|
||||
};
|
||||
|
||||
@@ -92,6 +103,13 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_config_from_str(&self, key: &str, value: Option<&str>) -> Result<(), &str> {
|
||||
if self.sql.set_config(self, config_from_str(key), value).is_err() {
|
||||
return Err("Sql error");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the given config key.
|
||||
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
|
||||
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> {
|
||||
@@ -125,7 +143,11 @@ impl Context {
|
||||
};
|
||||
|
||||
self.sql.set_config(self, key, val)
|
||||
}
|
||||
},
|
||||
Config::Ui(key) => {
|
||||
let key = format!("{}{}", CONFIG_UI_PREFIX, key);
|
||||
self.sql.set_config(self, key, value)
|
||||
},
|
||||
_ => self.sql.set_config(self, key, value),
|
||||
}
|
||||
}
|
||||
@@ -142,12 +164,23 @@ fn get_config_keys_string() -> String {
|
||||
format!(" {} ", keys)
|
||||
}
|
||||
|
||||
fn config_from_str(key: &str) -> Result<Config, &str> {
|
||||
if key.starts_with(CONFIG_UI_PREFIX) {
|
||||
Config::Ui(key[CONFIG_UI_PREFIX.len()-1..].to_string())
|
||||
} else {
|
||||
if let Ok(config) = Config::from_str(key) {
|
||||
config
|
||||
} else {
|
||||
Err("invalid key")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use crate::test_utils::*;
|
||||
|
||||
#[test]
|
||||
fn test_to_string() {
|
||||
@@ -165,4 +198,20 @@ mod tests {
|
||||
fn test_default_prop() {
|
||||
assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_from_str() {
|
||||
assert_eq!(config_from_str("addr"), Config::Addr);
|
||||
assert_eq!(config_from_str("addrxyz"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_config_from_str() {
|
||||
let t = dummy_context();
|
||||
assert_eq!(t.ctx.set_config_from_str("addr", Some("foo@bar.bar")), Ok(()));
|
||||
assert_eq!(t.ctx.get_config_from_str("addr").unwrap(), "foo@bar.bar");
|
||||
|
||||
assert_eq!(t.ctx.set_config_from_str("ui.desktop.some_string", Some("foobar")), Ok(()));
|
||||
assert_eq!(t.ctx.get_config_from_str("ui.desktop.some_string").unwrap(), "foobar");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,6 @@ pub unsafe fn moz_autoconfigure(
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
@@ -87,7 +86,7 @@ pub unsafe fn moz_autoconfigure(
|
||||
|| moz_ac.out.send_port == 0
|
||||
{
|
||||
let r = moz_ac.out.to_string();
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
warn!(context, "Bad or incomplete autoconfig: {}", r,);
|
||||
free(xml_raw as *mut libc::c_void);
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ pub unsafe fn outlk_autodiscover(
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
@@ -109,7 +108,7 @@ pub unsafe fn outlk_autodiscover(
|
||||
|| outlk_ad.out.send_port == 0
|
||||
{
|
||||
let r = outlk_ad.out.to_string();
|
||||
warn!(context, 0, "Bad or incomplete autoconfig: {}", r,);
|
||||
warn!(context, "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);
|
||||
|
||||
@@ -34,10 +34,7 @@ macro_rules! progress {
|
||||
// connect
|
||||
pub unsafe fn configure(context: &Context) {
|
||||
if dc_has_ongoing(context) {
|
||||
warn!(
|
||||
context,
|
||||
0, "There is already another ongoing process running.",
|
||||
);
|
||||
warn!(context, "There is already another ongoing process running.",);
|
||||
return;
|
||||
}
|
||||
job_kill_action(context, Action::ConfigureImap);
|
||||
@@ -45,17 +42,8 @@ pub unsafe fn configure(context: &Context) {
|
||||
}
|
||||
|
||||
/// Check if the context is already configured.
|
||||
pub fn dc_is_configured(context: &Context) -> libc::c_int {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "configured")
|
||||
.unwrap_or_default()
|
||||
> 0
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
pub fn dc_is_configured(context: &Context) -> bool {
|
||||
context.sql.get_config_bool(context, "configured")
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -73,7 +61,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
if dc_alloc_ongoing(context) {
|
||||
ongoing_allocated_here = true;
|
||||
if !context.sql.is_open() {
|
||||
error!(context, 0, "Cannot configure, database not opened.",);
|
||||
error!(context, "Cannot configure, database not opened.",);
|
||||
} else {
|
||||
context.inbox.read().unwrap().disconnect(context);
|
||||
context
|
||||
@@ -89,7 +77,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
.imap
|
||||
.disconnect(context);
|
||||
context.smtp.clone().lock().unwrap().disconnect();
|
||||
info!(context, 0, "Configure ...",);
|
||||
info!(context, "Configure ...",);
|
||||
|
||||
let s_a = context.running_state.clone();
|
||||
let s = s_a.read().unwrap();
|
||||
@@ -113,7 +101,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
1 => {
|
||||
progress!(context, 1);
|
||||
if param.addr.is_empty() {
|
||||
error!(context, 0, "Please enter an email address.",);
|
||||
error!(context, "Please enter an email address.",);
|
||||
}
|
||||
!param.addr.is_empty()
|
||||
}
|
||||
@@ -146,7 +134,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string();
|
||||
true
|
||||
} else {
|
||||
error!(context, 0, "Bad email-address.");
|
||||
error!(context, "Bad email-address.");
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -256,7 +244,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
12 => {
|
||||
progress!(context, 500);
|
||||
if let Some(ref cfg) = param_autoconfig {
|
||||
info!(context, 0, "Got autoconfig: {}", &cfg);
|
||||
info!(context, "Got autoconfig: {}", &cfg);
|
||||
if !cfg.mail_user.is_empty() {
|
||||
param.mail_user = cfg.mail_user.clone();
|
||||
}
|
||||
@@ -340,7 +328,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
|| param.send_pw.is_empty()
|
||||
|| param.server_flags == 0
|
||||
{
|
||||
error!(context, 0, "Account settings incomplete.");
|
||||
error!(context, "Account settings incomplete.");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
@@ -357,7 +345,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
ok_to_continue8 = true;
|
||||
break;
|
||||
}
|
||||
info!(context, 0, "Trying: {}", ¶m);
|
||||
info!(context, "Trying: {}", ¶m);
|
||||
|
||||
if context.inbox.read().unwrap().connect(context, ¶m) {
|
||||
ok_to_continue8 = true;
|
||||
@@ -375,7 +363,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
progress!(context, 650 + username_variation * 30);
|
||||
param.server_flags &= !(0x100 | 0x200 | 0x400);
|
||||
param.server_flags |= 0x100;
|
||||
info!(context, 0, "Trying: {}", ¶m);
|
||||
info!(context, "Trying: {}", ¶m);
|
||||
|
||||
if context.inbox.read().unwrap().connect(context, ¶m) {
|
||||
ok_to_continue8 = true;
|
||||
@@ -388,7 +376,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
}
|
||||
progress!(context, 660 + username_variation * 30);
|
||||
param.mail_port = 143;
|
||||
info!(context, 0, "Trying: {}", ¶m);
|
||||
info!(context, "Trying: {}", ¶m);
|
||||
|
||||
if context.inbox.read().unwrap().connect(context, ¶m) {
|
||||
ok_to_continue8 = true;
|
||||
@@ -443,7 +431,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
param.server_flags &= !(0x10000 | 0x20000 | 0x40000);
|
||||
param.server_flags |= 0x10000;
|
||||
param.send_port = 587;
|
||||
info!(context, 0, "Trying: {}", ¶m);
|
||||
info!(context, "Trying: {}", ¶m);
|
||||
|
||||
if !context
|
||||
.smtp
|
||||
@@ -459,7 +447,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
param.server_flags &= !(0x10000 | 0x20000 | 0x40000);
|
||||
param.server_flags |= 0x10000;
|
||||
param.send_port = 25;
|
||||
info!(context, 0, "Trying: {}", ¶m);
|
||||
info!(context, "Trying: {}", ¶m);
|
||||
|
||||
if !context
|
||||
.smtp
|
||||
@@ -518,7 +506,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
)
|
||||
.ok();
|
||||
|
||||
context.sql.set_config_int(context, "configured", 1).ok();
|
||||
context.sql.set_config_bool(context, "configured", true);
|
||||
true
|
||||
}
|
||||
18 => {
|
||||
@@ -528,13 +516,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
// (~30 seconds on a Moto G4 play) and might looks as if message sending is always that slow.
|
||||
e2ee::ensure_secret_key_exists(context);
|
||||
success = true;
|
||||
info!(context, 0, "Configure completed.");
|
||||
info!(context, "Configure completed.");
|
||||
progress!(context, 940);
|
||||
break; // We are done here
|
||||
}
|
||||
|
||||
_ => {
|
||||
error!(context, 0, "Internal error: step counter out of bound",);
|
||||
error!(context, "Internal error: step counter out of bound",);
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -582,10 +570,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) {
|
||||
|
||||
pub fn dc_alloc_ongoing(context: &Context) -> bool {
|
||||
if dc_has_ongoing(context) {
|
||||
warn!(
|
||||
context,
|
||||
0, "There is already another ongoing process running.",
|
||||
);
|
||||
warn!(context, "There is already another ongoing process running.",);
|
||||
|
||||
false
|
||||
} else {
|
||||
@@ -628,7 +613,7 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_
|
||||
.unwrap_or_default()
|
||||
== 0
|
||||
{
|
||||
warn!(context, 0, "Not configured, cannot connect.",);
|
||||
warn!(context, "Not configured, cannot connect.",);
|
||||
} else {
|
||||
let param = LoginParam::from_database(context, "configured_");
|
||||
// the trailing underscore is correct
|
||||
@@ -651,15 +636,15 @@ pub fn dc_stop_ongoing_process(context: &Context) {
|
||||
let mut s = s_a.write().unwrap();
|
||||
|
||||
if s.ongoing_running && !s.shall_stop_ongoing {
|
||||
info!(context, 0, "Signaling the ongoing process to stop ASAP.",);
|
||||
info!(context, "Signaling the ongoing process to stop ASAP.",);
|
||||
s.shall_stop_ongoing = true;
|
||||
} else {
|
||||
info!(context, 0, "No ongoing process to stop.",);
|
||||
info!(context, "No ongoing process to stop.",);
|
||||
};
|
||||
}
|
||||
|
||||
pub fn read_autoconf_file(context: &Context, url: &str) -> *mut libc::c_char {
|
||||
info!(context, 0, "Testing {} ...", url);
|
||||
info!(context, "Testing {} ...", url);
|
||||
|
||||
match reqwest::Client::new()
|
||||
.get(url)
|
||||
@@ -668,7 +653,7 @@ pub fn read_autoconf_file(context: &Context, url: &str) -> *mut libc::c_char {
|
||||
{
|
||||
Ok(res) => unsafe { res.strdup() },
|
||||
Err(_err) => {
|
||||
info!(context, 0, "Can\'t read file.",);
|
||||
info!(context, "Can\'t read file.",);
|
||||
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
@@ -31,8 +31,7 @@ const DC_ORIGIN_MIN_CONTACT_LIST: i32 = 0x100;
|
||||
/// For this purpose, internally, two names are tracked -
|
||||
/// authorized-name and given-name.
|
||||
/// By default, these names are equal, but functions working with contact names
|
||||
pub struct Contact<'a> {
|
||||
context: &'a Context,
|
||||
pub struct Contact {
|
||||
/// The contact ID.
|
||||
///
|
||||
/// Special message IDs:
|
||||
@@ -139,16 +138,15 @@ pub enum VerifiedStatus {
|
||||
BidirectVerified = 2,
|
||||
}
|
||||
|
||||
impl<'a> Contact<'a> {
|
||||
pub fn load_from_db(context: &'a Context, contact_id: u32) -> Result<Self> {
|
||||
impl Contact {
|
||||
pub fn load_from_db(context: &Context, contact_id: u32) -> Result<Self> {
|
||||
if contact_id == DC_CONTACT_ID_SELF {
|
||||
let contact = Contact {
|
||||
context,
|
||||
id: contact_id,
|
||||
name: context.stock_str(StockMessage::SelfMsg).into(),
|
||||
authname: "".into(),
|
||||
addr: context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.get_config(&Config::ConfiguredAddr)
|
||||
.unwrap_or_default(),
|
||||
blocked: false,
|
||||
origin: Origin::Unknown,
|
||||
@@ -162,7 +160,6 @@ impl<'a> Contact<'a> {
|
||||
params![contact_id as i32],
|
||||
|row| {
|
||||
let contact = Self {
|
||||
context,
|
||||
id: contact_id,
|
||||
name: row.get::<_, String>(0)?,
|
||||
authname: row.get::<_, String>(4)?,
|
||||
@@ -181,7 +178,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
|
||||
/// Check if a contact is blocked.
|
||||
pub fn is_blocked_load(context: &'a Context, id: u32) -> bool {
|
||||
pub fn is_blocked_load(context: &Context, id: u32) -> bool {
|
||||
Self::load_from_db(context, id)
|
||||
.map(|contact| contact.blocked)
|
||||
.unwrap_or_default()
|
||||
@@ -260,7 +257,7 @@ impl<'a> Contact<'a> {
|
||||
|
||||
let addr_normalized = addr_normalize(addr.as_ref());
|
||||
let addr_self = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.get_config(&Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
if addr_normalized == addr_self {
|
||||
@@ -298,7 +295,7 @@ impl<'a> Contact<'a> {
|
||||
|
||||
let addr = addr_normalize(addr.as_ref());
|
||||
let addr_self = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.get_config(&Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
if addr == addr_self {
|
||||
@@ -308,7 +305,6 @@ impl<'a> Contact<'a> {
|
||||
if !may_be_valid_addr(&addr) {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Bad address \"{}\" for contact \"{}\".",
|
||||
addr,
|
||||
if !name.as_ref().is_empty() {
|
||||
@@ -404,7 +400,7 @@ impl<'a> Contact<'a> {
|
||||
row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr);
|
||||
sth_modified = Modifier::Created;
|
||||
} else {
|
||||
error!(context, 0, "Cannot add contact.");
|
||||
error!(context, "Cannot add contact.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +456,7 @@ impl<'a> Contact<'a> {
|
||||
query: Option<impl AsRef<str>>,
|
||||
) -> Result<Vec<u32>> {
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.get_config(&Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut add_self = false;
|
||||
@@ -503,7 +499,7 @@ impl<'a> Contact<'a> {
|
||||
},
|
||||
)?;
|
||||
|
||||
let self_name = context.get_config(Config::Displayname).unwrap_or_default();
|
||||
let self_name = context.get_config(&Config::Displayname).unwrap_or_default();
|
||||
let self_name2 = context.stock_str(StockMessage::SelfMsg);
|
||||
|
||||
if let Some(query) = query {
|
||||
@@ -683,7 +679,7 @@ impl<'a> Contact<'a> {
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => {
|
||||
error!(context, 0, "delete_contact {} failed ({})", contact_id, err);
|
||||
error!(context, "delete_contact {} failed ({})", contact_id, err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
@@ -691,7 +687,7 @@ impl<'a> Contact<'a> {
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "could not delete contact {}, there are {} messages with it", contact_id, count_msgs
|
||||
"could not delete contact {}, there are {} messages with it", contact_id, count_msgs
|
||||
);
|
||||
bail!("Could not delete contact with messages in it");
|
||||
}
|
||||
@@ -767,9 +763,9 @@ impl<'a> Contact<'a> {
|
||||
/// Get the contact's profile image.
|
||||
/// This is the image set by each remote user on their own
|
||||
/// using dc_set_config(context, "selfavatar", image).
|
||||
pub fn get_profile_image(&self) -> Option<PathBuf> {
|
||||
pub fn get_profile_image(&self, context: &Context) -> Option<PathBuf> {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
if let Some(p) = self.context.get_config(Config::Selfavatar) {
|
||||
if let Some(p) = context.get_config(&Config::Selfavatar) {
|
||||
return Some(PathBuf::from(p));
|
||||
}
|
||||
}
|
||||
@@ -790,14 +786,18 @@ impl<'a> Contact<'a> {
|
||||
///
|
||||
/// The UI may draw a checkbox or something like that beside verified contacts.
|
||||
///
|
||||
pub fn is_verified(&self) -> VerifiedStatus {
|
||||
self.is_verified_ex(None)
|
||||
pub fn is_verified(&self, context: &Context) -> VerifiedStatus {
|
||||
self.is_verified_ex(context, None)
|
||||
}
|
||||
|
||||
/// Same as `Contact::is_verified` but allows speeding up things
|
||||
/// by adding the peerstate belonging to the contact.
|
||||
/// If you do not have the peerstate available, it is loaded automatically.
|
||||
pub fn is_verified_ex(&self, peerstate: Option<&Peerstate<'a>>) -> VerifiedStatus {
|
||||
pub fn is_verified_ex(
|
||||
&self,
|
||||
context: &Context,
|
||||
peerstate: Option<&Peerstate>,
|
||||
) -> VerifiedStatus {
|
||||
// We're always sort of secured-verified as we could verify the key on this device any time with the key
|
||||
// on this device
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
@@ -810,7 +810,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let peerstate = Peerstate::from_addr(self.context, &self.context.sql, &self.addr);
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, &self.addr);
|
||||
if let Some(ps) = peerstate {
|
||||
if ps.verified_key().is_some() {
|
||||
return VerifiedStatus::BidirectVerified;
|
||||
@@ -1026,7 +1026,7 @@ pub fn addr_cmp(addr1: impl AsRef<str>, addr2: impl AsRef<str>) -> bool {
|
||||
pub fn addr_equals_self(context: &Context, addr: impl AsRef<str>) -> bool {
|
||||
if !addr.as_ref().is_empty() {
|
||||
let normalized_addr = addr_normalize(addr.as_ref());
|
||||
if let Some(self_addr) = context.get_config(Config::ConfiguredAddr) {
|
||||
if let Some(self_addr) = context.get_config(&Config::ConfiguredAddr) {
|
||||
return normalized_addr == self_addr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,17 +208,11 @@ unsafe fn cb_precheck_imf(
|
||||
if *old_server_folder.offset(0isize) as libc::c_int == 0i32
|
||||
&& old_server_uid == 0i32 as libc::c_uint
|
||||
{
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"[move] detected bbc-self {}",
|
||||
as_str(rfc724_mid),
|
||||
);
|
||||
info!(context, "[move] detected bbc-self {}", as_str(rfc724_mid),);
|
||||
mark_seen = 1i32
|
||||
} else if as_str(old_server_folder) != server_folder {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"[move] detected moved message {}",
|
||||
as_str(rfc724_mid),
|
||||
);
|
||||
@@ -258,16 +252,16 @@ fn cb_get_config(context: &Context, key: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_close(context: &Context) {
|
||||
info!(context, 0, "disconnecting INBOX-watch",);
|
||||
info!(context, "disconnecting INBOX-watch",);
|
||||
context.inbox.read().unwrap().disconnect(context);
|
||||
info!(context, 0, "disconnecting sentbox-thread",);
|
||||
info!(context, "disconnecting sentbox-thread",);
|
||||
context
|
||||
.sentbox_thread
|
||||
.read()
|
||||
.unwrap()
|
||||
.imap
|
||||
.disconnect(context);
|
||||
info!(context, 0, "disconnecting mvbox-thread",);
|
||||
info!(context, "disconnecting mvbox-thread",);
|
||||
context
|
||||
.mvbox_thread
|
||||
.read()
|
||||
@@ -275,7 +269,7 @@ pub unsafe fn dc_close(context: &Context) {
|
||||
.imap
|
||||
.disconnect(context);
|
||||
|
||||
info!(context, 0, "disconnecting SMTP");
|
||||
info!(context, "disconnecting SMTP");
|
||||
context.smtp.clone().lock().unwrap().disconnect();
|
||||
|
||||
context.sql.close(context);
|
||||
|
||||
148
src/dc_imex.rs
148
src/dc_imex.rs
@@ -58,7 +58,6 @@ pub unsafe fn dc_imex_has_backup(
|
||||
if dir_iter.is_err() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Backup check: Cannot open directory \"{}\".\x00",
|
||||
dir_name.display(),
|
||||
);
|
||||
@@ -92,7 +91,7 @@ pub unsafe fn dc_imex_has_backup(
|
||||
Some(path) => match path.to_c_string() {
|
||||
Ok(cstr) => dc_strdup(cstr.as_ptr()),
|
||||
Err(err) => {
|
||||
error!(context, 0, "Invalid backup filename: {}", err);
|
||||
error!(context, "Invalid backup filename: {}", err);
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
},
|
||||
@@ -139,7 +138,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
))
|
||||
{
|
||||
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
|
||||
msg = dc_msg_new_untyped(context);
|
||||
msg = dc_msg_new_untyped();
|
||||
msg.type_0 = Viewtype::File;
|
||||
msg.param.set(Param::File, as_str(setup_file_name));
|
||||
|
||||
@@ -156,7 +155,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
.shall_stop_ongoing
|
||||
{
|
||||
if let Ok(msg_id) = chat::send_msg(context, chat_id, &mut msg) {
|
||||
info!(context, 0, "Wait for setup message being sent ...",);
|
||||
info!(context, "Wait for setup message being sent ...",);
|
||||
loop {
|
||||
if context
|
||||
.running_state
|
||||
@@ -170,7 +169,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
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.",);
|
||||
info!(context, "... setup message sent.",);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -208,12 +207,8 @@ pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<Strin
|
||||
_ => Some(("Autocrypt-Prefer-Encrypt", "mutual")),
|
||||
};
|
||||
let private_key_asc = private_key.to_asc(ac_headers);
|
||||
let encr = {
|
||||
let private_key_asc_c = CString::yolo(private_key_asc);
|
||||
let passphrase_c = CString::yolo(passphrase);
|
||||
dc_pgp_symm_encrypt(passphrase_c.as_ptr(), private_key_asc_c.as_bytes())
|
||||
.ok_or(format_err!("Failed to encrypt private key."))?
|
||||
};
|
||||
let encr = dc_pgp_symm_encrypt(&passphrase, private_key_asc.as_bytes())
|
||||
.ok_or(format_err!("Failed to encrypt private key."))?;
|
||||
let replacement = format!(
|
||||
concat!(
|
||||
"-----BEGIN PGP MESSAGE-----\r\n",
|
||||
@@ -268,56 +263,57 @@ pub fn dc_create_setup_code(_context: &Context) -> String {
|
||||
ret
|
||||
}
|
||||
|
||||
// TODO should return bool /rtn
|
||||
pub unsafe fn dc_continue_key_transfer(
|
||||
context: &Context,
|
||||
msg_id: uint32_t,
|
||||
setup_code: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
) -> bool {
|
||||
let mut success = false;
|
||||
let mut filename: *mut libc::c_char = ptr::null_mut();
|
||||
let mut filecontent: *mut libc::c_char = ptr::null_mut();
|
||||
let mut filebytes: size_t = 0i32 as size_t;
|
||||
let mut armored_key: *mut libc::c_char = ptr::null_mut();
|
||||
let mut norm_sc: *mut libc::c_char = ptr::null_mut();
|
||||
if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) {
|
||||
let msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_err()
|
||||
|| !dc_msg_is_setupmessage(msg.as_ref().unwrap())
|
||||
|| {
|
||||
filename = dc_msg_get_file(msg.as_ref().unwrap());
|
||||
filename.is_null()
|
||||
}
|
||||
|| *filename.offset(0isize) as libc::c_int == 0i32
|
||||
{
|
||||
error!(context, 0, "Message is no Autocrypt Setup Message.",);
|
||||
} else if 0
|
||||
== dc_read_file(
|
||||
context,
|
||||
filename,
|
||||
&mut filecontent as *mut *mut libc::c_char as *mut *mut libc::c_void,
|
||||
&mut filebytes,
|
||||
)
|
||||
|| filecontent.is_null()
|
||||
|| filebytes <= 0
|
||||
{
|
||||
error!(context, 0, "Cannot read Autocrypt Setup Message file.",);
|
||||
if msg_id <= 9i32 as libc::c_uint || setup_code.is_null() {
|
||||
return false;
|
||||
}
|
||||
let msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_err()
|
||||
|| !dc_msg_is_setupmessage(msg.as_ref().unwrap())
|
||||
|| {
|
||||
filename = dc_msg_get_file(context, msg.as_ref().unwrap());
|
||||
filename.is_null()
|
||||
}
|
||||
|| *filename.offset(0isize) as libc::c_int == 0i32
|
||||
{
|
||||
error!(context, "Message is no Autocrypt Setup Message.",);
|
||||
} else if 0
|
||||
== dc_read_file(
|
||||
context,
|
||||
filename,
|
||||
&mut filecontent as *mut *mut libc::c_char as *mut *mut libc::c_void,
|
||||
&mut filebytes,
|
||||
)
|
||||
|| filecontent.is_null()
|
||||
|| filebytes <= 0
|
||||
{
|
||||
error!(context, "Cannot read Autocrypt Setup Message file.",);
|
||||
} else {
|
||||
norm_sc = dc_normalize_setup_code(context, setup_code);
|
||||
if norm_sc.is_null() {
|
||||
warn!(context, "Cannot normalize Setup Code.",);
|
||||
} else {
|
||||
norm_sc = dc_normalize_setup_code(context, setup_code);
|
||||
if norm_sc.is_null() {
|
||||
warn!(context, 0, "Cannot normalize Setup Code.",);
|
||||
} else {
|
||||
armored_key = dc_decrypt_setup_file(context, norm_sc, filecontent);
|
||||
if armored_key.is_null() {
|
||||
warn!(context, 0, "Cannot decrypt Autocrypt Setup Message.",);
|
||||
} else if set_self_key(context, armored_key, 1) {
|
||||
/*set default*/
|
||||
/* error already logged */
|
||||
success = 1i32
|
||||
}
|
||||
armored_key = dc_decrypt_setup_file(context, norm_sc, filecontent);
|
||||
if armored_key.is_null() {
|
||||
warn!(context, "Cannot decrypt Autocrypt Setup Message.",);
|
||||
} else if set_self_key(context, armored_key, 1) {
|
||||
/*set default*/
|
||||
/* error already logged */
|
||||
success = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(armored_key as *mut libc::c_void);
|
||||
free(filecontent as *mut libc::c_void);
|
||||
free(filename as *mut libc::c_void);
|
||||
@@ -339,7 +335,7 @@ fn set_self_key(
|
||||
.and_then(|(k, h)| k.split_key().map(|pub_key| (k, pub_key, h)));
|
||||
|
||||
if keys.is_none() {
|
||||
error!(context, 0, "File does not contain a valid private key.",);
|
||||
error!(context, "File does not contain a valid private key.",);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -369,13 +365,13 @@ fn set_self_key(
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
error!(context, 0, "File does not contain a private key.",);
|
||||
error!(context, "File does not contain a private key.",);
|
||||
}
|
||||
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
|
||||
if self_addr.is_none() {
|
||||
error!(context, 0, "Missing self addr");
|
||||
error!(context, "Missing self addr");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -387,7 +383,7 @@ fn set_self_key(
|
||||
set_default,
|
||||
&context.sql,
|
||||
) {
|
||||
error!(context, 0, "Cannot save keypair.");
|
||||
error!(context, "Cannot save keypair.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -446,7 +442,7 @@ pub unsafe fn dc_decrypt_setup_file(
|
||||
{
|
||||
/* decrypt symmetrically */
|
||||
if let Some(plain) = dc_pgp_symm_decrypt(
|
||||
passphrase,
|
||||
as_str(passphrase),
|
||||
std::slice::from_raw_parts(binary as *const u8, binary_bytes),
|
||||
) {
|
||||
let payload_c = CString::new(plain).unwrap();
|
||||
@@ -508,19 +504,18 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
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.",);
|
||||
error!(context, "No Import/export dir/file given.",);
|
||||
} else {
|
||||
info!(context, 0, "Import/export process started.",);
|
||||
info!(context, "Import/export process started.",);
|
||||
context.call_cb(Event::IMEX_PROGRESS, 10 as uintptr_t, 0 as uintptr_t);
|
||||
if !context.sql.is_open() {
|
||||
error!(context, 0, "Import/export: Database not opened.",);
|
||||
error!(context, "Import/export: Database not opened.",);
|
||||
} else {
|
||||
if what == 1 || what == 11 {
|
||||
/* before we export anything, make sure the private key exists */
|
||||
if e2ee::ensure_secret_key_exists(context).is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import/export: Cannot create private key or private key not available.",
|
||||
);
|
||||
ok_to_continue = false;
|
||||
@@ -532,25 +527,25 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
match what {
|
||||
1 => {
|
||||
if 0 != export_self_keys(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
info!(context, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if 0 != import_self_keys(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
info!(context, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
11 => {
|
||||
if 0 != export_backup(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
info!(context, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
12 => {
|
||||
if 0 != import_backup(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
info!(context, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
@@ -577,7 +572,6 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char) -> libc::c_int {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Import \"{}\" to \"{}\".",
|
||||
as_str(backup_to_import),
|
||||
context
|
||||
@@ -586,8 +580,8 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
.map_or("<<None>>", |p| p.to_str().unwrap())
|
||||
);
|
||||
|
||||
if 0 != dc_is_configured(context) {
|
||||
error!(context, 0, "Cannot import backups to accounts in use.");
|
||||
if dc_is_configured(context) {
|
||||
error!(context, "Cannot import backups to accounts in use.");
|
||||
return 0;
|
||||
}
|
||||
&context.sql.close(&context);
|
||||
@@ -595,7 +589,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
if dc_file_exist(context, context.get_dbfile().unwrap()) {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot import backups: Cannot delete the old file.",
|
||||
"Cannot import backups: Cannot delete the old file.",
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
@@ -622,7 +616,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
.unwrap_or_default() as usize;
|
||||
info!(
|
||||
context,
|
||||
0, "***IMPORT-in-progress: total_files_cnt={:?}", total_files_cnt,
|
||||
"***IMPORT-in-progress: total_files_cnt={:?}", total_files_cnt,
|
||||
);
|
||||
|
||||
let res = context.sql.query_map(
|
||||
@@ -670,7 +664,6 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
}
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Storage full? Cannot write file {} with {} bytes.",
|
||||
&pathNfilename,
|
||||
file_blob.len(),
|
||||
@@ -717,7 +710,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
let buffer = CString::yolo(res);
|
||||
let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer.as_ptr());
|
||||
if dest_pathNfilename.is_null() {
|
||||
error!(context, 0, "Cannot get backup file name.",);
|
||||
error!(context, "Cannot get backup file name.",);
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -729,7 +722,6 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
let mut closed = true;
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Backup \"{}\" to \"{}\".",
|
||||
context
|
||||
.get_dbfile()
|
||||
@@ -773,7 +765,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
total_files_cnt += dir_handle.filter(|r| r.is_ok()).count();
|
||||
|
||||
info!(context, 0, "EXPORT: total_files_cnt={}", total_files_cnt);
|
||||
info!(context, "EXPORT: total_files_cnt={}", total_files_cnt);
|
||||
if total_files_cnt > 0 {
|
||||
// scan directory, pass 2: copy files
|
||||
if let Ok(dir_handle) = std::fs::read_dir(dir) {
|
||||
@@ -819,7 +811,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
{
|
||||
continue;
|
||||
} else {
|
||||
info!(context, 0, "EXPORTing filename={}", name);
|
||||
info!(context, "EXPORTing filename={}", name);
|
||||
let curr_pathNfilename = format!(
|
||||
"{}/{}",
|
||||
as_str(context.get_blobdir()),
|
||||
@@ -835,7 +827,6 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
if stmt.execute(params![name, buf]).is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Disk full? Cannot add file \"{}\" to backup.",
|
||||
&curr_pathNfilename,
|
||||
);
|
||||
@@ -855,13 +846,12 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot copy from blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir()),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
info!(context, 0, "Backup: No files to copy.",);
|
||||
info!(context, "Backup: No files to copy.",);
|
||||
ok_to_continue = true;
|
||||
}
|
||||
if ok_to_continue {
|
||||
@@ -880,7 +870,6 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Backup: Cannot get info for blob-directory \"{}\".",
|
||||
as_str(context.get_blobdir())
|
||||
);
|
||||
@@ -945,7 +934,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
dir_name,
|
||||
name_c.as_ptr(),
|
||||
);
|
||||
info!(context, 0, "Checking: {}", as_str(path_plus_name));
|
||||
info!(context, "Checking: {}", as_str(path_plus_name));
|
||||
free(buf as *mut libc::c_void);
|
||||
buf = ptr::null_mut();
|
||||
if 0 == dc_read_file(
|
||||
@@ -989,7 +978,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
{
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Treating \"{}\" as a legacy private key.",
|
||||
as_str(path_plus_name),
|
||||
);
|
||||
@@ -1003,7 +991,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
if imported_cnt == 0i32 {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"No private keys found in \"{}\".",
|
||||
as_str(dir_name),
|
||||
);
|
||||
@@ -1011,7 +998,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import: Cannot open directory \"{}\".",
|
||||
as_str(dir_name),
|
||||
);
|
||||
@@ -1111,10 +1097,10 @@ unsafe fn export_key_to_asc_file(
|
||||
id,
|
||||
)
|
||||
}
|
||||
info!(context, 0, "Exporting key {}", as_str(file_name),);
|
||||
info!(context, "Exporting key {}", as_str(file_name),);
|
||||
dc_delete_file(context, as_path(file_name));
|
||||
if !key.write_asc_to_file(file_name, context) {
|
||||
error!(context, 0, "Cannot write key to {}", as_str(file_name),);
|
||||
error!(context, "Cannot write key to {}", as_str(file_name),);
|
||||
} else {
|
||||
context.call_cb(
|
||||
Event::IMEX_FILE_WRITTEN,
|
||||
|
||||
@@ -37,8 +37,8 @@ pub struct dc_mimefactory_t<'a> {
|
||||
pub timestamp: i64,
|
||||
pub rfc724_mid: *mut libc::c_char,
|
||||
pub loaded: dc_mimefactory_loaded_t,
|
||||
pub msg: Message<'a>,
|
||||
pub chat: Option<Chat<'a>>,
|
||||
pub msg: Message,
|
||||
pub chat: Option<Chat>,
|
||||
pub increation: libc::c_int,
|
||||
pub in_reply_to: *mut libc::c_char,
|
||||
pub references: *mut libc::c_char,
|
||||
@@ -225,7 +225,7 @@ pub unsafe fn dc_mimefactory_load_msg(
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
0, "mimefactory: failed to load mime_in_reply_to: {:?}", err
|
||||
"mimefactory: failed to load mime_in_reply_to: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -335,7 +335,10 @@ pub unsafe fn dc_mimefactory_load_mdn<'a>(
|
||||
}
|
||||
|
||||
// TODO should return bool /rtn
|
||||
pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_int {
|
||||
pub unsafe fn dc_mimefactory_render(
|
||||
context: &Context,
|
||||
factory: &mut dc_mimefactory_t,
|
||||
) -> libc::c_int {
|
||||
let subject: *mut mailimf_subject;
|
||||
let mut ok_to_continue = true;
|
||||
let imf_fields: *mut mailimf_fields;
|
||||
@@ -586,8 +589,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
}
|
||||
if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
|
||||
info!(
|
||||
msg.context,
|
||||
0,
|
||||
context,
|
||||
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
|
||||
"vg-member-added",
|
||||
);
|
||||
@@ -655,8 +657,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
let step = msg.param.get(Param::Arg).unwrap_or_default().strdup();
|
||||
if strlen(step) > 0 {
|
||||
info!(
|
||||
msg.context,
|
||||
0,
|
||||
context,
|
||||
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
|
||||
as_str(step),
|
||||
);
|
||||
@@ -726,13 +727,14 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
}
|
||||
}
|
||||
if let Some(grpimage) = grpimage {
|
||||
info!(factory.context, 0, "setting group image '{}'", grpimage);
|
||||
let mut meta = dc_msg_new_untyped(factory.context);
|
||||
info!(factory.context, "setting group image '{}'", grpimage);
|
||||
let mut meta = dc_msg_new_untyped();
|
||||
meta.type_0 = Viewtype::Image;
|
||||
meta.param.set(Param::File, grpimage);
|
||||
|
||||
let mut filename_as_sent = ptr::null_mut();
|
||||
meta_part = build_body_file(
|
||||
context,
|
||||
&meta,
|
||||
b"group-image\x00" as *const u8 as *const libc::c_char,
|
||||
&mut filename_as_sent,
|
||||
@@ -835,7 +837,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
|
||||
/* add attachment part */
|
||||
if chat::msgtype_has_file(factory.msg.type_0) {
|
||||
if !is_file_size_okay(&factory.msg) {
|
||||
if !is_file_size_okay(context, &factory.msg) {
|
||||
let error: *mut libc::c_char = dc_mprintf(
|
||||
b"Message exceeds the recommended %i MB.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
@@ -846,7 +848,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
let file_part: *mut mailmime =
|
||||
build_body_file(&factory.msg, ptr::null(), ptr::null_mut());
|
||||
build_body_file(context, &factory.msg, ptr::null(), ptr::null_mut());
|
||||
if !file_part.is_null() {
|
||||
mailmime_smart_add_part(message, file_part);
|
||||
parts += 1
|
||||
@@ -894,12 +896,9 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
mailmime_smart_add_part(message, kml_mime_part);
|
||||
}
|
||||
|
||||
if location::is_sending_locations_to_chat(
|
||||
factory.msg.context,
|
||||
factory.msg.chat_id,
|
||||
) {
|
||||
if location::is_sending_locations_to_chat(context, factory.msg.chat_id) {
|
||||
if let Ok((kml_file, last_added_location_id)) =
|
||||
location::get_kml(factory.msg.context, factory.msg.chat_id)
|
||||
location::get_kml(context, factory.msg.chat_id)
|
||||
{
|
||||
let content_type = mailmime_content_new_with_str(
|
||||
b"application/vnd.google-earth.kml+xml\x00" as *const u8
|
||||
@@ -952,7 +951,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
.stock_str(StockMessage::EncryptedMsg)
|
||||
.into_owned()
|
||||
} else {
|
||||
to_string(dc_msg_get_summarytext(&mut factory.msg, 32))
|
||||
to_string(dc_msg_get_summarytext(context, &mut factory.msg, 32))
|
||||
};
|
||||
let p2 = factory
|
||||
.context
|
||||
@@ -996,7 +995,8 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
e.as_ptr(),
|
||||
);
|
||||
} else {
|
||||
subject_str = get_subject(factory.chat.as_ref(), &mut factory.msg, afwd_email)
|
||||
subject_str =
|
||||
get_subject(context, factory.chat.as_ref(), &mut factory.msg, afwd_email)
|
||||
}
|
||||
subject = mailimf_subject_new(dc_encode_header_words(subject_str));
|
||||
mailimf_fields_add(
|
||||
@@ -1062,6 +1062,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
}
|
||||
|
||||
unsafe fn get_subject(
|
||||
context: &Context,
|
||||
chat: Option<&Chat>,
|
||||
msg: &mut Message,
|
||||
afwd_email: libc::c_int,
|
||||
@@ -1071,7 +1072,6 @@ unsafe fn get_subject(
|
||||
}
|
||||
|
||||
let chat = chat.unwrap();
|
||||
let context = chat.context;
|
||||
let ret: *mut libc::c_char;
|
||||
|
||||
let raw_subject = {
|
||||
@@ -1136,6 +1136,7 @@ unsafe fn build_body_text(text: *mut libc::c_char) -> *mut mailmime {
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn build_body_file(
|
||||
context: &Context,
|
||||
msg: &Message,
|
||||
mut base_name: *const libc::c_char,
|
||||
ret_file_name_as_sent: *mut *mut libc::c_char,
|
||||
@@ -1288,7 +1289,7 @@ unsafe fn build_body_file(
|
||||
) as *mut libc::c_void,
|
||||
);
|
||||
mime_sub = mailmime_new_empty(content, mime_fields);
|
||||
mailmime_set_body_file(mime_sub, dc_get_abs_path(msg.context, path_filename));
|
||||
mailmime_set_body_file(mime_sub, dc_get_abs_path(context, path_filename));
|
||||
if !ret_file_name_as_sent.is_null() {
|
||||
*ret_file_name_as_sent = dc_strdup(filename_to_send)
|
||||
}
|
||||
@@ -1305,10 +1306,10 @@ unsafe fn build_body_file(
|
||||
/*******************************************************************************
|
||||
* Render
|
||||
******************************************************************************/
|
||||
unsafe fn is_file_size_okay(msg: &Message) -> bool {
|
||||
unsafe fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
||||
let mut file_size_okay = true;
|
||||
let path = msg.param.get(Param::File).unwrap_or_default();
|
||||
let bytes = dc_get_filebytes(msg.context, &path);
|
||||
let bytes = dc_get_filebytes(context, &path);
|
||||
|
||||
if bytes > (49 * 1024 * 1024 / 4 * 3) {
|
||||
file_size_okay = false;
|
||||
|
||||
@@ -132,15 +132,9 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m
|
||||
dc_mimeparser_parse_mime_recursive(mimeparser_ref, mimeparser_ref.mimeroot);
|
||||
let field: *mut mailimf_field = dc_mimeparser_lookup_field(&mimeparser, "Subject");
|
||||
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||
let decoded = dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value);
|
||||
if decoded.is_null()
|
||||
/* XXX: can it happen? */
|
||||
{
|
||||
mimeparser.subject = None
|
||||
} else {
|
||||
mimeparser.subject = Some(to_string(decoded));
|
||||
free(decoded.cast());
|
||||
}
|
||||
let subj = (*(*field).fld_data.fld_subject).sbj_value;
|
||||
|
||||
mimeparser.subject = as_opt_str(subj).map(dc_decode_header_words_safe);
|
||||
}
|
||||
if !dc_mimeparser_lookup_optional_field(&mut mimeparser, "Chat-Version").is_null() {
|
||||
mimeparser.is_send_by_messenger = true
|
||||
@@ -257,7 +251,7 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m
|
||||
msg_c,
|
||||
);
|
||||
free(msg_c.cast());
|
||||
part.msg = Some(to_string(new_txt));
|
||||
part.msg = Some(to_string_lossy(new_txt));
|
||||
free(new_txt.cast());
|
||||
break;
|
||||
}
|
||||
@@ -460,7 +454,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
{
|
||||
info!(
|
||||
mimeparser.context,
|
||||
0, "Protected headers found in text/rfc822-headers attachment: Will be ignored.",
|
||||
"Protected headers found in text/rfc822-headers attachment: Will be ignored.",
|
||||
);
|
||||
return 0i32;
|
||||
}
|
||||
@@ -474,7 +468,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
) != MAILIMF_NO_ERROR as libc::c_int
|
||||
|| mimeparser.header_protected.is_null()
|
||||
{
|
||||
warn!(mimeparser.context, 0, "Protected headers parsing error.",);
|
||||
warn!(mimeparser.context, "Protected headers parsing error.",);
|
||||
} else {
|
||||
hash_header(
|
||||
&mut mimeparser.header,
|
||||
@@ -485,7 +479,6 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
} else {
|
||||
info!(
|
||||
mimeparser.context,
|
||||
0,
|
||||
"Protected headers found in MIME header: Will be ignored as we already found an outer one."
|
||||
);
|
||||
}
|
||||
@@ -667,7 +660,6 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
if plain_cnt == 1i32 && html_cnt == 1i32 {
|
||||
warn!(
|
||||
mimeparser.context,
|
||||
0i32,
|
||||
"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted."
|
||||
);
|
||||
skip_part = html_part
|
||||
@@ -1072,7 +1064,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
);
|
||||
|
||||
let (res, _, _) = encoding.decode(data);
|
||||
info!(mimeparser.context, 0, "decoded message: '{}'", res);
|
||||
info!(mimeparser.context, "decoded message: '{}'", res);
|
||||
if res.is_empty() {
|
||||
/* no error - but nothing to add */
|
||||
ok_to_continue = false;
|
||||
@@ -1085,7 +1077,6 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
} else {
|
||||
warn!(
|
||||
mimeparser.context,
|
||||
0,
|
||||
"Cannot convert {} bytes from \"{}\" to \"utf-8\".",
|
||||
decoded_data_bytes as libc::c_int,
|
||||
as_str(charset),
|
||||
@@ -1117,7 +1108,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
part.msg_raw = {
|
||||
let raw_c =
|
||||
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
|
||||
let raw = to_string(raw_c);
|
||||
let raw = to_string_lossy(raw_c);
|
||||
free(raw_c.cast());
|
||||
Some(raw)
|
||||
};
|
||||
@@ -1175,7 +1166,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
9,
|
||||
) == 0i32
|
||||
{
|
||||
filename_parts += &to_string(
|
||||
filename_parts += &to_string_lossy(
|
||||
(*(*dsp_param).pa_data.pa_parameter).pa_value,
|
||||
);
|
||||
} else if (*dsp_param).pa_type
|
||||
@@ -1649,7 +1640,7 @@ pub unsafe fn dc_mimeparser_repl_msg_by_error(
|
||||
}
|
||||
let part = &mut mimeparser.parts[0];
|
||||
part.type_0 = Viewtype::Text;
|
||||
part.msg = Some(format!("[{}]", to_string(error_msg)));
|
||||
part.msg = Some(format!("[{}]", to_string_lossy(error_msg)));
|
||||
mimeparser.parts.truncate(1);
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ pub unsafe fn dc_receive_imf(
|
||||
) {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Receiving message {}/{}...",
|
||||
if !server_folder.as_ref().is_empty() {
|
||||
server_folder.as_ref()
|
||||
@@ -61,7 +60,7 @@ pub unsafe fn dc_receive_imf(
|
||||
|
||||
if mime_parser.header.is_empty() {
|
||||
// Error - even adding an empty record won't help as we do not know the message ID
|
||||
info!(context, 0, "No header.");
|
||||
info!(context, "No header.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,7 +188,7 @@ pub unsafe fn dc_receive_imf(
|
||||
&mut created_db_entries,
|
||||
&mut create_event_to_send,
|
||||
) {
|
||||
info!(context, 0, "{}", err);
|
||||
info!(context, "{}", err);
|
||||
|
||||
cleanup(
|
||||
context,
|
||||
@@ -243,7 +242,6 @@ pub unsafe fn dc_receive_imf(
|
||||
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"received message {} has Message-Id: {}",
|
||||
server_uid,
|
||||
to_string(rfc724_mid)
|
||||
@@ -447,10 +445,7 @@ unsafe fn add_parts(
|
||||
// check if the message belongs to a mailing list
|
||||
if dc_mimeparser_is_mailinglist_message(mime_parser) {
|
||||
*chat_id = 3;
|
||||
info!(
|
||||
context,
|
||||
0, "Message belongs to a mailing list and is ignored.",
|
||||
);
|
||||
info!(context, "Message belongs to a mailing list and is ignored.",);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,7 +477,7 @@ unsafe fn add_parts(
|
||||
Contact::scaleup_origin_by_id(context, *from_id, Origin::IncomingReplyTo);
|
||||
info!(
|
||||
context,
|
||||
0, "Message is a reply to a known message, mark sender as known.",
|
||||
"Message is a reply to a known message, mark sender as known.",
|
||||
);
|
||||
if !incoming_origin.is_verified() {
|
||||
*incoming_origin = Origin::IncomingReplyTo;
|
||||
@@ -642,7 +637,8 @@ unsafe fn add_parts(
|
||||
}
|
||||
}
|
||||
if part.type_0 == Viewtype::Text {
|
||||
let msg_raw = CString::yolo(part.msg_raw.as_ref().unwrap().clone());
|
||||
let msg_raw =
|
||||
CString::yolo(part.msg_raw.as_ref().cloned().unwrap_or_default());
|
||||
let subject_c = CString::yolo(
|
||||
mime_parser
|
||||
.subject
|
||||
@@ -723,7 +719,7 @@ unsafe fn add_parts(
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "Message has {} parts and is assigned to chat #{}.", icnt, *chat_id,
|
||||
"Message has {} parts and is assigned to chat #{}.", icnt, *chat_id,
|
||||
);
|
||||
|
||||
// check event to send
|
||||
@@ -961,7 +957,7 @@ fn save_locations(
|
||||
insert_msg_id,
|
||||
newest_location_id,
|
||||
) {
|
||||
error!(context, 0, "Failed to set msg_location_id: {:?}", err);
|
||||
error!(context, "Failed to set msg_location_id: {:?}", err);
|
||||
}
|
||||
}
|
||||
send_event = true;
|
||||
@@ -1316,7 +1312,7 @@ unsafe fn create_or_lookup_group(
|
||||
if !X_MrAddToGrp.is_null() || !X_MrRemoveFromGrp.is_null() {
|
||||
recreate_member_list = 1;
|
||||
} else if 0 != X_MrGrpNameChanged && !grpname.is_null() && strlen(grpname) < 200 {
|
||||
info!(context, 0, "updating grpname for chat {}", chat_id);
|
||||
info!(context, "updating grpname for chat {}", chat_id);
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
@@ -1331,7 +1327,7 @@ unsafe fn create_or_lookup_group(
|
||||
if !X_MrGrpImageChanged.is_empty() {
|
||||
info!(
|
||||
context,
|
||||
0, "grp-image-change {} chat {}", X_MrGrpImageChanged, chat_id
|
||||
"grp-image-change {} chat {}", X_MrGrpImageChanged, chat_id
|
||||
);
|
||||
let mut changed = false;
|
||||
let mut grpimage = "".to_string();
|
||||
@@ -1345,20 +1341,20 @@ unsafe fn create_or_lookup_group(
|
||||
.get(Param::File)
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
info!(context, 0, "found image {:?}", grpimage);
|
||||
info!(context, "found image {:?}", grpimage);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
info!(context, 0, "New group image set to '{}'.", grpimage);
|
||||
info!(context, "New group image set to '{}'.", grpimage);
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
|
||||
if grpimage.is_empty() {
|
||||
chat.param.remove(Param::ProfileImage);
|
||||
} else {
|
||||
chat.param.set(Param::ProfileImage, grpimage);
|
||||
}
|
||||
chat.update_param().unwrap();
|
||||
chat.update_param(context).unwrap();
|
||||
send_EVENT_CHAT_MODIFIED = 1;
|
||||
}
|
||||
}
|
||||
@@ -1740,7 +1736,7 @@ unsafe fn check_verified_properties(
|
||||
) -> libc::c_int {
|
||||
let verify_fail = |reason: String| {
|
||||
*failure_reason = format!("{}. See \"Info\" for details.", reason).strdup();
|
||||
warn!(context, 0, "{}", reason);
|
||||
warn!(context, "{}", reason);
|
||||
};
|
||||
|
||||
let contact = match Contact::load_from_db(context, from_id) {
|
||||
@@ -1764,7 +1760,8 @@ unsafe fn check_verified_properties(
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr());
|
||||
|
||||
if peerstate.is_none()
|
||||
|| contact.is_verified_ex(peerstate.as_ref()) != VerifiedStatus::BidirectVerified
|
||||
|| contact.is_verified_ex(context, peerstate.as_ref())
|
||||
!= VerifiedStatus::BidirectVerified
|
||||
{
|
||||
verify_fail("The sender of this message is not verified.".into());
|
||||
return 0;
|
||||
@@ -1811,13 +1808,7 @@ unsafe fn check_verified_properties(
|
||||
|| peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint
|
||||
&& peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint
|
||||
{
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{} has verfied {}.",
|
||||
contact.get_addr(),
|
||||
to_addr,
|
||||
);
|
||||
info!(context, "{} has verfied {}.", contact.get_addr(), to_addr,);
|
||||
let fp = peerstate.gossip_key_fingerprint.clone();
|
||||
if let Some(fp) = fp {
|
||||
peerstate.set_verified(0, &fp, 2);
|
||||
|
||||
@@ -12,6 +12,20 @@ use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
/**
|
||||
* Encode non-ascii-strings as `=?UTF-8?Q?Bj=c3=b6rn_Petersen?=`.
|
||||
* Belongs to RFC 2047: https://tools.ietf.org/html/rfc2047
|
||||
*
|
||||
* We do not fold at position 72; this would result in empty words as `=?utf-8?Q??=` which are correct,
|
||||
* but cannot be displayed by some mail programs (eg. Android Stock Mail).
|
||||
* however, this is not needed, as long as _one_ word is not longer than 72 characters.
|
||||
* _if_ it is, the display may get weird. This affects the subject only.
|
||||
* the best solution wor all this would be if libetpan encodes the line as only libetpan knowns when a header line is full.
|
||||
*
|
||||
* @param to_encode Null-terminated UTF-8-string to encode.
|
||||
* @return Returns the encoded string which must be free()'d when no longed needed.
|
||||
* On errors, NULL is returned.
|
||||
*/
|
||||
pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut ok_to_continue = true;
|
||||
let mut ret_str: *mut libc::c_char = ptr::null_mut();
|
||||
@@ -224,6 +238,31 @@ pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_
|
||||
out
|
||||
}
|
||||
|
||||
pub fn dc_decode_header_words_safe(input: &str) -> String {
|
||||
static FROM_ENCODING: &[u8] = b"iso-8859-1\x00";
|
||||
static TO_ENCODING: &[u8] = b"utf-8\x00";
|
||||
let mut out = ptr::null_mut();
|
||||
let mut cur_token = 0;
|
||||
let input_c = CString::yolo(input);
|
||||
unsafe {
|
||||
let r = mailmime_encoded_phrase_parse(
|
||||
FROM_ENCODING.as_ptr().cast(),
|
||||
input_c.as_ptr(),
|
||||
input.len(),
|
||||
&mut cur_token,
|
||||
TO_ENCODING.as_ptr().cast(),
|
||||
&mut out,
|
||||
);
|
||||
if r as u32 != MAILIMF_NO_ERROR || out.is_null() {
|
||||
input.to_string()
|
||||
} else {
|
||||
let res = to_string(out);
|
||||
free(out.cast());
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_needs_ext_header(to_check: impl AsRef<str>) -> bool {
|
||||
let to_check = to_check.as_ref();
|
||||
|
||||
|
||||
@@ -843,16 +843,19 @@ pub fn dc_get_filebytes(context: &Context, path: impl AsRef<std::path::Path>) ->
|
||||
|
||||
pub fn dc_delete_file(context: &Context, path: impl AsRef<std::path::Path>) -> bool {
|
||||
let path_abs = dc_get_abs_path_safe(context, &path);
|
||||
let res = if path_abs.is_file() {
|
||||
fs::remove_file(path_abs)
|
||||
} else {
|
||||
fs::remove_dir_all(path_abs)
|
||||
};
|
||||
if !path_abs.is_file() {
|
||||
warn!(
|
||||
context,
|
||||
"Will not delete directory \"{}\".",
|
||||
path.as_ref().display()
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
match res {
|
||||
match fs::remove_file(path_abs) {
|
||||
Ok(_) => true,
|
||||
Err(_err) => {
|
||||
warn!(context, 0, "Cannot delete \"{}\".", path.as_ref().display());
|
||||
warn!(context, "Cannot delete \"{}\".", path.as_ref().display());
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -870,7 +873,6 @@ pub fn dc_copy_file(
|
||||
Err(_) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot copy \"{}\" to \"{}\".",
|
||||
src.as_ref().display(),
|
||||
dest.as_ref().display(),
|
||||
@@ -888,7 +890,6 @@ pub fn dc_create_folder(context: &Context, path: impl AsRef<std::path::Path>) ->
|
||||
Err(_err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create directory \"{}\".",
|
||||
path.as_ref().display(),
|
||||
);
|
||||
@@ -921,7 +922,6 @@ pub fn dc_write_file_safe<P: AsRef<std::path::Path>>(
|
||||
if let Err(_err) = fs::write(&path_abs, buf) {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot write {} bytes to \"{}\".",
|
||||
buf.len(),
|
||||
path.as_ref().display(),
|
||||
@@ -959,7 +959,6 @@ pub fn dc_read_file_safe<P: AsRef<std::path::Path>>(context: &Context, path: P)
|
||||
Err(_err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot read \"{}\" or file is empty.",
|
||||
path.as_ref().display()
|
||||
);
|
||||
|
||||
11
src/e2ee.rs
11
src/e2ee.rs
@@ -91,7 +91,7 @@ impl E2eeHelper {
|
||||
|
||||
if let Some(addr) = addr {
|
||||
let pubkey_ret = load_or_generate_self_public_key(context, &addr).map_err(|err| {
|
||||
error!(context, 0, "Failed to load public key: {}", err);
|
||||
error!(context, "Failed to load public key: {}", err);
|
||||
err
|
||||
});
|
||||
if let Ok(public_key) = pubkey_ret {
|
||||
@@ -113,7 +113,7 @@ impl E2eeHelper {
|
||||
let peerstate = peerstate.unwrap();
|
||||
info!(
|
||||
context,
|
||||
0, "dc_e2ee_encrypt {} has peerstate", recipient_addr
|
||||
"dc_e2ee_encrypt {} has peerstate", recipient_addr
|
||||
);
|
||||
if let Some(key) = peerstate.peek_key(min_verified as usize) {
|
||||
keyring.add_owned(key.clone());
|
||||
@@ -122,7 +122,6 @@ impl E2eeHelper {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"dc_e2ee_encrypt {} HAS NO peerstate {}",
|
||||
recipient_addr,
|
||||
peerstate.is_some()
|
||||
@@ -608,7 +607,7 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
||||
let start = std::time::Instant::now();
|
||||
info!(
|
||||
context,
|
||||
0, "Generating keypair with {} bits, e={} ...", 2048, 65537,
|
||||
"Generating keypair with {} bits, e={} ...", 2048, 65537,
|
||||
);
|
||||
match dc_pgp_create_keypair(&self_addr) {
|
||||
Some((public_key, private_key)) => {
|
||||
@@ -623,7 +622,6 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
||||
true => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Keypair generated in {:.3}s.",
|
||||
start.elapsed().as_secs()
|
||||
);
|
||||
@@ -691,7 +689,6 @@ unsafe fn update_gossip_peerstates(
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.",
|
||||
&header.addr,
|
||||
);
|
||||
@@ -1028,7 +1025,7 @@ unsafe fn contains_report(mime: *mut mailmime) -> bool {
|
||||
/// [Config::ConfiguredAddr] is configured, this address is returned.
|
||||
pub fn ensure_secret_key_exists(context: &Context) -> Result<String> {
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.get_config(&Config::ConfiguredAddr)
|
||||
.ok_or(format_err!(concat!(
|
||||
"Failed to get self address, ",
|
||||
"cannot ensure secret key if not configured."
|
||||
|
||||
98
src/imap.rs
98
src/imap.rs
@@ -472,12 +472,9 @@ impl Imap {
|
||||
}
|
||||
|
||||
fn unsetup_handle(&self, context: &Context) {
|
||||
info!(context, 0, "IMAP unsetup_handle starts");
|
||||
info!(context, "IMAP unsetup_handle starts");
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 1 (closing down stream)."
|
||||
);
|
||||
info!(context, "IMAP unsetup_handle step 1 (closing down stream).");
|
||||
let stream = self.stream.write().unwrap().take();
|
||||
if let Some(stream) = stream {
|
||||
if let Err(err) = stream.shutdown(net::Shutdown::Both) {
|
||||
@@ -487,7 +484,7 @@ impl Imap {
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
"IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
);
|
||||
if let Some(mut session) = self.session.lock().unwrap().take() {
|
||||
if let Err(err) = session.close() {
|
||||
@@ -495,10 +492,10 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "IMAP unsetup_handle step 3 (clearing config).");
|
||||
info!(context, "IMAP unsetup_handle step 3 (clearing config).");
|
||||
self.config.write().unwrap().selected_folder = None;
|
||||
self.config.write().unwrap().selected_mailbox = None;
|
||||
info!(context, 0, "IMAP unsetup_handle step 4 (disconnected).",);
|
||||
info!(context, "IMAP unsetup_handle step 4 (disconnected).",);
|
||||
}
|
||||
|
||||
fn free_connect_params(&self) {
|
||||
@@ -551,7 +548,7 @@ impl Imap {
|
||||
Some(ref mut session) => match session.capabilities() {
|
||||
Ok(caps) => {
|
||||
if !context.sql.is_open() {
|
||||
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
warn!(context, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
(true, false, false)
|
||||
} else {
|
||||
let can_idle = caps.has_str("IDLE");
|
||||
@@ -571,7 +568,7 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
info!(context, 0, "CAPABILITY command error: {}", err);
|
||||
info!(context, "CAPABILITY command error: {}", err);
|
||||
(true, false, false)
|
||||
}
|
||||
},
|
||||
@@ -647,7 +644,7 @@ impl Imap {
|
||||
// deselect existing folder, if needed (it's also done implicitly by SELECT, however, without EXPUNGE then)
|
||||
if self.config.read().unwrap().selected_folder_needs_expunge {
|
||||
if let Some(ref folder) = self.config.read().unwrap().selected_folder {
|
||||
info!(context, 0, "Expunge messages in \"{}\".", folder);
|
||||
info!(context, "Expunge messages in \"{}\".", folder);
|
||||
|
||||
// A CLOSE-SELECT is considerably faster than an EXPUNGE-SELECT, see
|
||||
// https://tools.ietf.org/html/rfc3501#section-6.4.2
|
||||
@@ -677,7 +674,6 @@ impl Imap {
|
||||
Err(err) => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder: {}; {:?}.",
|
||||
folder.as_ref(),
|
||||
err
|
||||
@@ -714,7 +710,6 @@ impl Imap {
|
||||
if !self.is_connected() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot fetch from \"{}\" - not connected.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -725,7 +720,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(&folder)) == 0 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder \"{}\" for fetching.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -742,7 +736,6 @@ impl Imap {
|
||||
if mailbox.uid_validity.is_none() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot get UIDVALIDITY for folder \"{}\".",
|
||||
folder.as_ref(),
|
||||
);
|
||||
@@ -754,7 +747,7 @@ impl Imap {
|
||||
// first time this folder is selected or UIDVALIDITY has changed, init lastseenuid and save it to config
|
||||
|
||||
if mailbox.exists == 0 {
|
||||
info!(context, 0, "Folder \"{}\" is empty.", folder.as_ref());
|
||||
info!(context, "Folder \"{}\" is empty.", folder.as_ref());
|
||||
|
||||
// set lastseenuid=0 for empty folders.
|
||||
// id we do not do this here, we'll miss the first message
|
||||
@@ -773,7 +766,6 @@ impl Imap {
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"No result returned for folder \"{}\".",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -796,7 +788,6 @@ impl Imap {
|
||||
self.set_config_last_seen_uid(context, &folder, uid_validity, last_seen_uid);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"lastseenuid initialized to {} for {}@{}",
|
||||
last_seen_uid,
|
||||
folder.as_ref(),
|
||||
@@ -815,7 +806,7 @@ impl Imap {
|
||||
match session.uid_fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(list) => list,
|
||||
Err(err) => {
|
||||
warn!(context, 0, "failed to fetch uids: {}", err);
|
||||
warn!(context, "failed to fetch uids: {}", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -843,7 +834,6 @@ impl Imap {
|
||||
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Read error for message {} from \"{}\", trying over later.",
|
||||
message_id,
|
||||
folder.as_ref()
|
||||
@@ -855,7 +845,6 @@ impl Imap {
|
||||
// check failed
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Skipping message {} from \"{}\" by precheck.",
|
||||
message_id,
|
||||
folder.as_ref(),
|
||||
@@ -876,7 +865,6 @@ impl Imap {
|
||||
if read_errors > 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"{} mails read from \"{}\" with {} errors.",
|
||||
read_cnt,
|
||||
folder.as_ref(),
|
||||
@@ -885,7 +873,6 @@ impl Imap {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{} mails read from \"{}\".",
|
||||
read_cnt,
|
||||
folder.as_ref()
|
||||
@@ -930,7 +917,6 @@ impl Imap {
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Error on fetching message #{} from folder \"{}\"; retry={}; error={}.",
|
||||
server_uid,
|
||||
folder.as_ref(),
|
||||
@@ -947,7 +933,6 @@ impl Imap {
|
||||
if msgs.is_empty() {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Message #{} does not exist in folder \"{}\".",
|
||||
server_uid,
|
||||
folder.as_ref()
|
||||
@@ -1001,7 +986,7 @@ impl Imap {
|
||||
|
||||
let watch_folder = self.config.read().unwrap().watch_folder.clone();
|
||||
if self.select_folder(context, watch_folder.as_ref()) == 0 {
|
||||
warn!(context, 0, "IMAP-IDLE not setup.",);
|
||||
warn!(context, "IMAP-IDLE not setup.",);
|
||||
|
||||
return self.fake_idle(context);
|
||||
}
|
||||
@@ -1011,7 +996,7 @@ impl Imap {
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
let v = self.watch.clone();
|
||||
|
||||
info!(context, 0, "IMAP-IDLE SPAWNING");
|
||||
info!(context, "IMAP-IDLE SPAWNING");
|
||||
std::thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*v;
|
||||
if let Some(ref mut session) = &mut *session.lock().unwrap() {
|
||||
@@ -1046,18 +1031,15 @@ impl Imap {
|
||||
|
||||
let handle_res = |res| match res {
|
||||
Ok(()) => {
|
||||
info!(context, 0, "IMAP-IDLE has data.");
|
||||
info!(context, "IMAP-IDLE has data.");
|
||||
}
|
||||
Err(err) => match err {
|
||||
imap::error::Error::ConnectionLost => {
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP-IDLE wait cancelled, we will reconnect soon."
|
||||
);
|
||||
info!(context, "IMAP-IDLE wait cancelled, we will reconnect soon.");
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
}
|
||||
_ => {
|
||||
warn!(context, 0, "IMAP-IDLE returns unknown value: {}", err);
|
||||
warn!(context, "IMAP-IDLE returns unknown value: {}", err);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -1073,7 +1055,7 @@ impl Imap {
|
||||
if let Ok(res) = worker.as_ref().unwrap().try_recv() {
|
||||
handle_res(res);
|
||||
} else {
|
||||
info!(context, 0, "IMAP-IDLE interrupted");
|
||||
info!(context, "IMAP-IDLE interrupted");
|
||||
}
|
||||
|
||||
drop(worker.take());
|
||||
@@ -1091,7 +1073,7 @@ impl Imap {
|
||||
let fake_idle_start_time = SystemTime::now();
|
||||
let mut wait_long = false;
|
||||
|
||||
info!(context, 0, "IMAP-fake-IDLEing...");
|
||||
info!(context, "IMAP-fake-IDLEing...");
|
||||
|
||||
let mut do_fake_idle = true;
|
||||
while do_fake_idle {
|
||||
@@ -1167,7 +1149,6 @@ impl Imap {
|
||||
} else if folder.as_ref() == dest_folder.as_ref() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Skip moving message; message {}/{} is already in {}...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1178,7 +1159,6 @@ impl Imap {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Moving message {}/{} to {}...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1188,7 +1168,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(folder.as_ref())) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for moving message.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -1202,7 +1181,6 @@ impl Imap {
|
||||
Err(err) => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Cannot move message, fallback to COPY/DELETE {}/{} to {}: {}",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1223,7 +1201,7 @@ impl Imap {
|
||||
Ok(_) => true,
|
||||
Err(err) => {
|
||||
eprintln!("error copy: {:?}", err);
|
||||
info!(context, 0, "Cannot copy message.",);
|
||||
info!(context, "Cannot copy message.",);
|
||||
|
||||
false
|
||||
}
|
||||
@@ -1234,7 +1212,7 @@ impl Imap {
|
||||
|
||||
if copied {
|
||||
if self.add_flag(context, uid, "\\Deleted") == 0 {
|
||||
warn!(context, 0, "Cannot mark message as \"Deleted\".",);
|
||||
warn!(context, "Cannot mark message as \"Deleted\".",);
|
||||
}
|
||||
self.config.write().unwrap().selected_folder_needs_expunge = true;
|
||||
res = DC_SUCCESS;
|
||||
@@ -1271,7 +1249,7 @@ impl Imap {
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
0, "IMAP failed to store: ({}, {}) {:?}", set, query, err
|
||||
"IMAP failed to store: ({}, {}) {:?}", set, query, err
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1294,7 +1272,6 @@ impl Imap {
|
||||
} else if self.is_connected() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Marking message {}/{} as seen...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1303,12 +1280,11 @@ impl Imap {
|
||||
if self.select_folder(context, Some(folder.as_ref())) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for setting SEEN flag.",
|
||||
folder.as_ref(),
|
||||
);
|
||||
} else if self.add_flag(context, uid, "\\Seen") == 0 {
|
||||
warn!(context, 0, "Cannot mark message as seen.",);
|
||||
warn!(context, "Cannot mark message as seen.",);
|
||||
} else {
|
||||
res = DC_SUCCESS
|
||||
}
|
||||
@@ -1335,7 +1311,6 @@ impl Imap {
|
||||
} else if self.is_connected() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Marking message {}/{} as $MDNSent...",
|
||||
folder.as_ref(),
|
||||
uid,
|
||||
@@ -1344,7 +1319,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(folder.as_ref())) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for setting $MDNSent flag.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -1411,16 +1385,16 @@ impl Imap {
|
||||
};
|
||||
|
||||
if res == DC_SUCCESS {
|
||||
info!(context, 0, "$MDNSent just set and MDN will be sent.");
|
||||
info!(context, "$MDNSent just set and MDN will be sent.");
|
||||
} else {
|
||||
info!(context, 0, "$MDNSent already set and MDN already sent.");
|
||||
info!(context, "$MDNSent already set and MDN already sent.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = DC_SUCCESS;
|
||||
info!(
|
||||
context,
|
||||
0, "Cannot store $MDNSent flags, risk sending duplicate MDN.",
|
||||
"Cannot store $MDNSent flags, risk sending duplicate MDN.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1451,7 +1425,6 @@ impl Imap {
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Marking message \"{}\", {}/{} for deletion...",
|
||||
message_id.as_ref(),
|
||||
folder.as_ref(),
|
||||
@@ -1461,7 +1434,6 @@ impl Imap {
|
||||
if self.select_folder(context, Some(&folder)) == 0 {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot select folder {} for deleting message.",
|
||||
folder.as_ref()
|
||||
);
|
||||
@@ -1482,7 +1454,6 @@ impl Imap {
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot delete on IMAP, {}/{} does not match {}.",
|
||||
folder.as_ref(),
|
||||
server_uid,
|
||||
@@ -1496,7 +1467,6 @@ impl Imap {
|
||||
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot delete on IMAP, {}/{} not found.",
|
||||
folder.as_ref(),
|
||||
server_uid,
|
||||
@@ -1508,7 +1478,7 @@ impl Imap {
|
||||
|
||||
// mark the message for deletion
|
||||
if self.add_flag(context, *server_uid, "\\Deleted") == 0 {
|
||||
warn!(context, 0, "Cannot mark message as \"Deleted\".");
|
||||
warn!(context, "Cannot mark message as \"Deleted\".");
|
||||
} else {
|
||||
self.config.write().unwrap().selected_folder_needs_expunge = true;
|
||||
success = true
|
||||
@@ -1528,7 +1498,7 @@ impl Imap {
|
||||
return;
|
||||
}
|
||||
|
||||
info!(context, 0, "Configuring IMAP-folders.");
|
||||
info!(context, "Configuring IMAP-folders.");
|
||||
|
||||
let folders = self.list_folders(context).unwrap();
|
||||
let delimiter = self.config.read().unwrap().imap_delimiter;
|
||||
@@ -1547,21 +1517,19 @@ impl Imap {
|
||||
});
|
||||
|
||||
if mvbox_folder.is_none() && 0 != (flags as usize & DC_CREATE_MVBOX) {
|
||||
info!(context, 0, "Creating MVBOX-folder \"DeltaChat\"...",);
|
||||
info!(context, "Creating MVBOX-folder \"DeltaChat\"...",);
|
||||
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.create("DeltaChat") {
|
||||
Ok(_) => {
|
||||
mvbox_folder = Some("DeltaChat".into());
|
||||
|
||||
info!(context, 0, "MVBOX-folder created.",);
|
||||
info!(context, "MVBOX-folder created.",);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})",
|
||||
err
|
||||
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})", err
|
||||
);
|
||||
|
||||
match session.create(&fallback_folder) {
|
||||
@@ -1569,11 +1537,11 @@ impl Imap {
|
||||
mvbox_folder = Some(fallback_folder);
|
||||
info!(
|
||||
context,
|
||||
0, "MVBOX-folder created as INBOX subfolder. ({})", err
|
||||
"MVBOX-folder created as INBOX subfolder. ({})", err
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "Cannot create MVBOX-folder. ({})", err);
|
||||
warn!(context, "Cannot create MVBOX-folder. ({})", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1619,13 +1587,13 @@ impl Imap {
|
||||
match session.list(Some(""), Some("*")) {
|
||||
Ok(list) => {
|
||||
if list.is_empty() {
|
||||
warn!(context, 0, "Folder list is empty.",);
|
||||
warn!(context, "Folder list is empty.",);
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("list error: {:?}", err);
|
||||
warn!(context, 0, "Cannot get folder list.",);
|
||||
warn!(context, "Cannot get folder list.",);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
69
src/job.rs
69
src/job.rs
@@ -156,12 +156,10 @@ impl Job {
|
||||
/* if there is a msg-id and it does not exist in the db, cancel sending.
|
||||
this happends if dc_delete_msgs() was called
|
||||
before the generated mime was sent out */
|
||||
if 0 != self.foreign_id
|
||||
&& 0 == unsafe { dc_msg_exists(context, self.foreign_id) }
|
||||
{
|
||||
if 0 != self.foreign_id && !dc_msg_exists(context, self.foreign_id) {
|
||||
warn!(
|
||||
context,
|
||||
0, "Message {} for job {} does not exist", self.foreign_id, self.job_id,
|
||||
"Message {} for job {} does not exist", self.foreign_id, self.job_id,
|
||||
);
|
||||
return;
|
||||
};
|
||||
@@ -199,7 +197,7 @@ impl Job {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "Missing recipients for job {}", self.job_id,);
|
||||
warn!(context, "Missing recipients for job {}", self.job_id,);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,7 +277,7 @@ impl Job {
|
||||
if dc_rfc724_mid_cnt(context, msg.rfc724_mid) != 1 {
|
||||
info!(
|
||||
context,
|
||||
0, "The message is deleted from the server when all parts are deleted.",
|
||||
"The message is deleted from the server when all parts are deleted.",
|
||||
);
|
||||
delete_from_server = 0i32
|
||||
}
|
||||
@@ -442,18 +440,17 @@ pub fn perform_imap_fetch(context: &Context) {
|
||||
.unwrap_or_else(|| 1)
|
||||
== 0
|
||||
{
|
||||
info!(context, 0, "INBOX-watch disabled.",);
|
||||
info!(context, "INBOX-watch disabled.",);
|
||||
return;
|
||||
}
|
||||
info!(context, 0, "INBOX-fetch started...",);
|
||||
info!(context, "INBOX-fetch started...",);
|
||||
inbox.fetch(context);
|
||||
if inbox.should_reconnect() {
|
||||
info!(context, 0, "INBOX-fetch aborted, starting over...",);
|
||||
info!(context, "INBOX-fetch aborted, starting over...",);
|
||||
inbox.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"INBOX-fetch done in {:.4} ms.",
|
||||
start.elapsed().as_nanos() as f64 / 1000.0,
|
||||
);
|
||||
@@ -467,13 +464,13 @@ pub fn perform_imap_idle(context: &Context) {
|
||||
if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
|
||||
info!(
|
||||
context,
|
||||
0, "INBOX-IDLE will not be started because of waiting jobs."
|
||||
"INBOX-IDLE will not be started because of waiting jobs."
|
||||
);
|
||||
return;
|
||||
}
|
||||
info!(context, 0, "INBOX-IDLE started...");
|
||||
info!(context, "INBOX-IDLE started...");
|
||||
inbox.idle(context);
|
||||
info!(context, 0, "INBOX-IDLE ended.");
|
||||
info!(context, "INBOX-IDLE ended.");
|
||||
}
|
||||
|
||||
pub fn perform_mvbox_fetch(context: &Context) {
|
||||
@@ -550,16 +547,16 @@ pub fn perform_smtp_jobs(context: &Context) {
|
||||
state.perform_jobs_needed = 0;
|
||||
|
||||
if state.suspended {
|
||||
info!(context, 0, "SMTP-jobs suspended.",);
|
||||
info!(context, "SMTP-jobs suspended.",);
|
||||
return;
|
||||
}
|
||||
state.doing_jobs = true;
|
||||
probe_smtp_network
|
||||
};
|
||||
|
||||
info!(context, 0, "SMTP-jobs started...",);
|
||||
info!(context, "SMTP-jobs started...",);
|
||||
job_perform(context, Thread::Smtp, probe_smtp_network);
|
||||
info!(context, 0, "SMTP-jobs ended.");
|
||||
info!(context, "SMTP-jobs ended.");
|
||||
|
||||
{
|
||||
let &(ref lock, _) = &*context.smtp_state.clone();
|
||||
@@ -570,7 +567,7 @@ pub fn perform_smtp_jobs(context: &Context) {
|
||||
}
|
||||
|
||||
pub fn perform_smtp_idle(context: &Context) {
|
||||
info!(context, 0, "SMTP-idle started...",);
|
||||
info!(context, "SMTP-idle started...",);
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*context.smtp_state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -578,7 +575,7 @@ pub fn perform_smtp_idle(context: &Context) {
|
||||
if state.perform_jobs_needed == 1 {
|
||||
info!(
|
||||
context,
|
||||
0, "SMTP-idle will not be started because of waiting jobs.",
|
||||
"SMTP-idle will not be started because of waiting jobs.",
|
||||
);
|
||||
} else {
|
||||
let dur = get_next_wakeup_time(context, Thread::Smtp);
|
||||
@@ -596,7 +593,7 @@ pub fn perform_smtp_idle(context: &Context) {
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "SMTP-idle ended.",);
|
||||
info!(context, "SMTP-idle ended.",);
|
||||
}
|
||||
|
||||
fn get_next_wakeup_time(context: &Context, thread: Thread) -> Duration {
|
||||
@@ -655,7 +652,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
if mimefactory.is_err() || mimefactory.as_ref().unwrap().from_addr.is_null() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Cannot load data to send, maybe the message is deleted in between.",
|
||||
"Cannot load data to send, maybe the message is deleted in between.",
|
||||
);
|
||||
} else {
|
||||
let mut mimefactory = mimefactory.unwrap();
|
||||
@@ -680,12 +677,12 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
mimefactory.msg.param.set_int(Param::Height, height as i32);
|
||||
}
|
||||
}
|
||||
dc_msg_save_param_to_disk(&mut mimefactory.msg);
|
||||
dc_msg_save_param_to_disk(context, &mut mimefactory.msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* create message */
|
||||
if 0 == dc_mimefactory_render(&mut mimefactory) {
|
||||
if 0 == dc_mimefactory_render(context, &mut mimefactory) {
|
||||
dc_set_msg_failed(context, msg_id, as_opt_str(mimefactory.error));
|
||||
} else if 0
|
||||
!= mimefactory
|
||||
@@ -697,7 +694,6 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"e2e encryption unavailable {} - {:?}",
|
||||
msg_id,
|
||||
mimefactory.msg.param.get_int(Param::GuranteeE2ee),
|
||||
@@ -728,7 +724,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
if let Err(err) =
|
||||
location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time())
|
||||
{
|
||||
error!(context, 0, "Failed to set kml sent_timestamp: {:?}", err);
|
||||
error!(context, "Failed to set kml sent_timestamp: {:?}", err);
|
||||
}
|
||||
if !mimefactory.msg.hidden {
|
||||
if let Err(err) = location::set_msg_location_id(
|
||||
@@ -736,7 +732,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
mimefactory.msg.id,
|
||||
mimefactory.out_last_added_location_id,
|
||||
) {
|
||||
error!(context, 0, "Failed to set msg_location_id: {:?}", err);
|
||||
error!(context, "Failed to set msg_location_id: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -749,7 +745,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
== 0
|
||||
{
|
||||
mimefactory.msg.param.set_int(Param::GuranteeE2ee, 1);
|
||||
dc_msg_save_param_to_disk(&mut mimefactory.msg);
|
||||
dc_msg_save_param_to_disk(context, &mut mimefactory.msg);
|
||||
}
|
||||
success = add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory);
|
||||
}
|
||||
@@ -759,14 +755,14 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
|
||||
}
|
||||
|
||||
pub fn perform_imap_jobs(context: &Context) {
|
||||
info!(context, 0, "dc_perform_imap_jobs starting.",);
|
||||
info!(context, "dc_perform_imap_jobs starting.",);
|
||||
|
||||
let probe_imap_network = *context.probe_imap_network.clone().read().unwrap();
|
||||
*context.probe_imap_network.write().unwrap() = false;
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = false;
|
||||
|
||||
job_perform(context, Thread::Imap, probe_imap_network);
|
||||
info!(context, 0, "dc_perform_imap_jobs ended.",);
|
||||
info!(context, "dc_perform_imap_jobs ended.",);
|
||||
}
|
||||
|
||||
fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
@@ -814,14 +810,13 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
match jobs {
|
||||
Ok(ref _res) => {}
|
||||
Err(ref err) => {
|
||||
info!(context, 0, "query failed: {:?}", err);
|
||||
info!(context, "query failed: {:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
for mut job in jobs.unwrap_or_default() {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-job #{}, action {} started...",
|
||||
if thread == Thread::Imap {
|
||||
"INBOX"
|
||||
@@ -860,7 +855,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
|
||||
match job.action {
|
||||
Action::Unknown => {
|
||||
warn!(context, 0, "Unknown job id found");
|
||||
warn!(context, "Unknown job id found");
|
||||
}
|
||||
Action::SendMsgToSmtp => job.do_DC_JOB_SEND(context),
|
||||
Action::DeleteMsgOnImap => job.do_DC_JOB_DELETE_MSG_ON_IMAP(context),
|
||||
@@ -904,7 +899,6 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
// just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-job #{} not yet ready and will be delayed.",
|
||||
if thread == Thread::Imap {
|
||||
"INBOX"
|
||||
@@ -922,7 +916,6 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
job.update(context);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-job #{} not succeeded on try #{}, retry in ADD_TIME+{} (in {} seconds).",
|
||||
if thread == Thread::Imap {
|
||||
"INBOX"
|
||||
@@ -999,7 +992,7 @@ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
|
||||
|
||||
fn send_mdn(context: &Context, msg_id: uint32_t) {
|
||||
if let Ok(mut mimefactory) = unsafe { dc_mimefactory_load_mdn(context, msg_id) } {
|
||||
if 0 != unsafe { dc_mimefactory_render(&mut mimefactory) } {
|
||||
if 0 != unsafe { dc_mimefactory_render(context, &mut mimefactory) } {
|
||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory);
|
||||
}
|
||||
}
|
||||
@@ -1021,7 +1014,6 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_
|
||||
if pathNfilename.is_null() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Could not find free file name for message with ID <{}>.",
|
||||
to_string(mimefactory.rfc724_mid),
|
||||
);
|
||||
@@ -1037,7 +1029,6 @@ fn add_smtp_job(context: &Context, action: Action, mimefactory: &dc_mimefactory_
|
||||
{
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Could not write message <{}> to \"{}\".",
|
||||
to_string(mimefactory.rfc724_mid),
|
||||
as_str(pathNfilename),
|
||||
@@ -1081,7 +1072,7 @@ pub fn job_add(
|
||||
delay_seconds: i64,
|
||||
) {
|
||||
if action == Action::Unknown {
|
||||
error!(context, 0, "Invalid action passed to job_add");
|
||||
error!(context, "Invalid action passed to job_add");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1110,7 +1101,7 @@ pub fn job_add(
|
||||
}
|
||||
|
||||
pub fn interrupt_smtp_idle(context: &Context) {
|
||||
info!(context, 0, "Interrupting SMTP-idle...",);
|
||||
info!(context, "Interrupting SMTP-idle...",);
|
||||
|
||||
let &(ref lock, ref cvar) = &*context.smtp_state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -1121,7 +1112,7 @@ pub fn interrupt_smtp_idle(context: &Context) {
|
||||
}
|
||||
|
||||
pub fn interrupt_imap_idle(context: &Context) {
|
||||
info!(context, 0, "Interrupting IMAP-IDLE...",);
|
||||
info!(context, "Interrupting IMAP-IDLE...",);
|
||||
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = true;
|
||||
context.inbox.read().unwrap().interrupt_idle();
|
||||
|
||||
@@ -30,7 +30,7 @@ impl JobThread {
|
||||
}
|
||||
|
||||
pub fn suspend(&self, context: &Context) {
|
||||
info!(context, 0, "Suspending {}-thread.", self.name,);
|
||||
info!(context, "Suspending {}-thread.", self.name,);
|
||||
{
|
||||
self.state.0.lock().unwrap().suspended = true;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ impl JobThread {
|
||||
}
|
||||
|
||||
pub fn unsuspend(&self, context: &Context) {
|
||||
info!(context, 0, "Unsuspending {}-thread.", self.name);
|
||||
info!(context, "Unsuspending {}-thread.", self.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -60,7 +60,7 @@ impl JobThread {
|
||||
self.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, 0, "Interrupting {}-IDLE...", self.name);
|
||||
info!(context, "Interrupting {}-IDLE...", self.name);
|
||||
|
||||
self.imap.interrupt_idle();
|
||||
|
||||
@@ -86,16 +86,15 @@ impl JobThread {
|
||||
if use_network {
|
||||
let start = std::time::Instant::now();
|
||||
if self.connect_to_imap(context) {
|
||||
info!(context, 0, "{}-fetch started...", self.name);
|
||||
info!(context, "{}-fetch started...", self.name);
|
||||
self.imap.fetch(context);
|
||||
|
||||
if self.imap.should_reconnect() {
|
||||
info!(context, 0, "{}-fetch aborted, starting over...", self.name,);
|
||||
info!(context, "{}-fetch aborted, starting over...", self.name,);
|
||||
self.imap.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
self.name,
|
||||
start.elapsed().as_millis(),
|
||||
@@ -142,7 +141,6 @@ impl JobThread {
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
self.name,
|
||||
);
|
||||
@@ -172,9 +170,9 @@ impl JobThread {
|
||||
}
|
||||
|
||||
self.connect_to_imap(context);
|
||||
info!(context, 0, "{}-IDLE started...", self.name,);
|
||||
info!(context, "{}-IDLE started...", self.name,);
|
||||
self.imap.idle(context);
|
||||
info!(context, 0, "{}-IDLE ended.", self.name);
|
||||
info!(context, "{}-IDLE ended.", self.name);
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
|
||||
33
src/key.rs
33
src/key.rs
@@ -1,7 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::Cursor;
|
||||
use std::slice;
|
||||
|
||||
use libc;
|
||||
use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey};
|
||||
@@ -106,15 +105,6 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_binary(data: *const u8, len: libc::c_int, key_type: KeyType) -> Option<Self> {
|
||||
if data.is_null() || len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let bytes = unsafe { slice::from_raw_parts(data, len as usize) };
|
||||
Self::from_slice(bytes, key_type)
|
||||
}
|
||||
|
||||
pub fn from_armored_string(
|
||||
data: &str,
|
||||
key_type: KeyType,
|
||||
@@ -245,7 +235,7 @@ impl Key {
|
||||
file_content_c.as_bytes().len(),
|
||||
)
|
||||
} {
|
||||
error!(context, 0, "Cannot write key to {}", to_string(file));
|
||||
error!(context, "Cannot write key to {}", to_string(file));
|
||||
false
|
||||
} else {
|
||||
true
|
||||
@@ -449,6 +439,27 @@ i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
assert_eq!(private_key, private_key2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_slice_bad_data() {
|
||||
let mut bad_data: [u8; 4096] = [0; 4096];
|
||||
|
||||
for i in 0..4096 {
|
||||
bad_data[i] = (i & 0xff) as u8;
|
||||
}
|
||||
|
||||
for j in 0..(4096 / 40) {
|
||||
let bad_key = Key::from_slice(
|
||||
&bad_data[j..j + 4096 / 2 + j],
|
||||
if 0 != j & 1 {
|
||||
KeyType::Public
|
||||
} else {
|
||||
KeyType::Private
|
||||
},
|
||||
);
|
||||
assert!(bad_key.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // is too expensive
|
||||
fn test_ascii_roundtrip() {
|
||||
|
||||
159
src/location.rs
159
src/location.rs
@@ -84,7 +84,6 @@ impl Kml {
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Location parsing: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
@@ -216,7 +215,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
.is_ok()
|
||||
{
|
||||
if 0 != seconds && !is_sending_locations_before {
|
||||
msg = dc_msg_new(context, Viewtype::Text);
|
||||
msg = dc_msg_new(Viewtype::Text);
|
||||
msg.text =
|
||||
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
|
||||
msg.param.set_int(Param::Cmd, 8);
|
||||
@@ -266,16 +265,16 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> l
|
||||
if latitude == 0.0 && longitude == 0.0 {
|
||||
return 1;
|
||||
}
|
||||
let mut continue_streaming = false;
|
||||
|
||||
context.sql.query_map(
|
||||
if let Ok(chats) = context.sql.query_map(
|
||||
"SELECT id FROM chats WHERE locations_send_until>?;",
|
||||
params![time()], |row| row.get::<_, i32>(0),
|
||||
|chats| {
|
||||
let mut continue_streaming = false;
|
||||
|
||||
for chat in chats {
|
||||
let chat_id = chat?;
|
||||
context.sql.execute(
|
||||
params![time()],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|chats| chats.collect::<Result<Vec<_>, _>>().map_err(Into::into),
|
||||
) {
|
||||
for chat_id in chats {
|
||||
if let Err(err) = context.sql.execute(
|
||||
"INSERT INTO locations \
|
||||
(latitude, longitude, accuracy, timestamp, chat_id, from_id) VALUES (?,?,?,?,?,?);",
|
||||
params![
|
||||
@@ -286,16 +285,19 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> l
|
||||
chat_id,
|
||||
1,
|
||||
]
|
||||
)?;
|
||||
) {
|
||||
warn!(context, "failed to store location {:?}", err);
|
||||
} else {
|
||||
continue_streaming = true;
|
||||
}
|
||||
if continue_streaming {
|
||||
context.call_cb(Event::LOCATION_CHANGED, 1, 0);
|
||||
};
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0);
|
||||
Ok(continue_streaming as libc::c_int)
|
||||
}
|
||||
).unwrap_or_default()
|
||||
if continue_streaming {
|
||||
context.call_cb(Event::LOCATION_CHANGED, 1, 0);
|
||||
};
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0);
|
||||
}
|
||||
|
||||
continue_streaming as libc::c_int
|
||||
}
|
||||
|
||||
pub fn get_range(
|
||||
@@ -546,76 +548,81 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
|
||||
let mut continue_streaming: libc::c_int = 1;
|
||||
info!(
|
||||
context,
|
||||
0, " ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
||||
" ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
||||
);
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id, locations_send_begin, locations_last_sent \
|
||||
FROM chats \
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = 1;
|
||||
if let Ok(rows) = context.sql.query_map(
|
||||
"SELECT id, locations_send_begin, locations_last_sent \
|
||||
FROM chats \
|
||||
WHERE locations_send_until>?;",
|
||||
params![now],
|
||||
|row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
let locations_send_begin: i64 = row.get(1)?;
|
||||
let locations_last_sent: i64 = row.get(2)?;
|
||||
continue_streaming = 1;
|
||||
|
||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
||||
if now - locations_last_sent < (60 - 3) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
||||
}
|
||||
},
|
||||
|rows| {
|
||||
context.sql.prepare(
|
||||
"SELECT id \
|
||||
FROM locations \
|
||||
WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND timestamp>? \
|
||||
AND independent=0 \
|
||||
ORDER BY timestamp;",
|
||||
|mut stmt_locations, _| {
|
||||
for (chat_id, locations_send_begin, locations_last_sent) in
|
||||
rows.filter_map(|r| match r {
|
||||
Ok(Some(v)) => Some(v),
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
// TODO: do I need to reset?
|
||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
||||
if now - locations_last_sent < (60 - 3) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
||||
}
|
||||
},
|
||||
|rows| {
|
||||
rows.filter_map(|v| v.transpose())
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
},
|
||||
) {
|
||||
let msgs = context
|
||||
.sql
|
||||
.prepare(
|
||||
"SELECT id \
|
||||
FROM locations \
|
||||
WHERE from_id=? \
|
||||
AND timestamp>=? \
|
||||
AND timestamp>? \
|
||||
AND independent=0 \
|
||||
ORDER BY timestamp;",
|
||||
|mut stmt_locations, _| {
|
||||
let msgs = rows
|
||||
.into_iter()
|
||||
.filter_map(|(chat_id, locations_send_begin, locations_last_sent)| {
|
||||
if !stmt_locations
|
||||
.exists(params![1, locations_send_begin, locations_last_sent,])
|
||||
.unwrap_or_default()
|
||||
{
|
||||
// if there is no new location, there's nothing to send.
|
||||
// however, maybe we want to bypass this test eg. 15 minutes
|
||||
continue;
|
||||
None
|
||||
} else {
|
||||
// pending locations are attached automatically to every message,
|
||||
// so also to this empty text message.
|
||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
||||
//
|
||||
// for optimisation and to avoid flooding the sending queue,
|
||||
// we could sending these messages only if we're really online.
|
||||
// the easiest way to determine this, is to check for an empty message queue.
|
||||
// (might not be 100%, however, as positions are sent combined later
|
||||
// and dc_set_location() is typically called periodically, this is ok)
|
||||
let mut msg = dc_msg_new(Viewtype::Text);
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::Cmd, 9);
|
||||
Some((chat_id, msg))
|
||||
}
|
||||
// pending locations are attached automatically to every message,
|
||||
// so also to this empty text message.
|
||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
||||
//
|
||||
// for optimisation and to avoid flooding the sending queue,
|
||||
// we could sending these messages only if we're really online.
|
||||
// the easiest way to determine this, is to check for an empty message queue.
|
||||
// (might not be 100%, however, as positions are sent combined later
|
||||
// and dc_set_location() is typically called periodically, this is ok)
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
msg.hidden = true;
|
||||
msg.param.set_int(Param::Cmd, 9);
|
||||
// TODO: handle cleanup on error
|
||||
chat::send_msg(context, chat_id as u32, &mut msg).unwrap();
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
.unwrap(); // TODO: Better error handling
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Ok(msgs)
|
||||
},
|
||||
)
|
||||
.unwrap_or_default(); // TODO: Better error handling
|
||||
|
||||
for (chat_id, mut msg) in msgs.into_iter() {
|
||||
// TODO: better error handling
|
||||
chat::send_msg(context, chat_id as u32, &mut msg).unwrap();
|
||||
}
|
||||
}
|
||||
if 0 != continue_streaming {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0x1);
|
||||
}
|
||||
|
||||
57
src/log.rs
57
src/log.rs
@@ -1,46 +1,31 @@
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
info!($ctx, $data1, $msg,)
|
||||
($ctx:expr, $msg:expr) => {
|
||||
info!($ctx, $msg,)
|
||||
};
|
||||
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
log_event!($ctx, $crate::constants::Event::INFO, 0, $msg, $($args),*);
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
warn!($ctx, $data1, $msg,)
|
||||
($ctx:expr, $msg:expr) => {
|
||||
warn!($ctx, $msg,)
|
||||
};
|
||||
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
log_event!($ctx, $crate::constants::Event::WARNING, 0, $msg, $($args),*);
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
error!($ctx, $data1, $msg,)
|
||||
($ctx:expr, $msg:expr) => {
|
||||
error!($ctx, $msg,)
|
||||
};
|
||||
($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
log_event!($ctx, $crate::constants::Event::ERROR, 0, $msg, $($args),*);
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
@@ -49,13 +34,11 @@ macro_rules! log_event {
|
||||
log_event!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($event, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($event, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
|
||||
@@ -137,7 +137,7 @@ impl Lot {
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
/// approx. max. length returned by dc_get_msg_info()
|
||||
#[derive(Clone)]
|
||||
pub struct Message<'a> {
|
||||
pub struct Message {
|
||||
pub id: u32,
|
||||
pub from_id: u32,
|
||||
pub to_id: u32,
|
||||
@@ -150,7 +150,6 @@ pub struct Message<'a> {
|
||||
pub timestamp_sent: i64,
|
||||
pub timestamp_rcvd: i64,
|
||||
pub text: Option<String>,
|
||||
pub context: &'a Context,
|
||||
pub rfc724_mid: *mut libc::c_char,
|
||||
pub in_reply_to: *mut libc::c_char,
|
||||
pub server_folder: Option<String>,
|
||||
@@ -270,7 +269,7 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let p = dc_msg_get_file(&msg);
|
||||
let p = dc_msg_get_file(context, &msg);
|
||||
if !p.is_null() && 0 != *p.offset(0isize) as libc::c_int {
|
||||
ret += &format!(
|
||||
"\nFile: {}, {}, bytes\n",
|
||||
@@ -310,11 +309,11 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch
|
||||
ret.strdup()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_msg_new_untyped<'a>(context: &'a Context) -> Message<'a> {
|
||||
dc_msg_new(context, Viewtype::Unknown)
|
||||
pub fn dc_msg_new_untyped() -> Message {
|
||||
dc_msg_new(Viewtype::Unknown)
|
||||
}
|
||||
|
||||
pub fn dc_msg_new<'a>(context: &'a Context, viewtype: Viewtype) -> Message<'a> {
|
||||
pub fn dc_msg_new(viewtype: Viewtype) -> Message {
|
||||
Message {
|
||||
id: 0,
|
||||
from_id: 0,
|
||||
@@ -328,7 +327,6 @@ pub fn dc_msg_new<'a>(context: &'a Context, viewtype: Viewtype) -> Message<'a> {
|
||||
timestamp_sent: 0,
|
||||
timestamp_rcvd: 0,
|
||||
text: None,
|
||||
context,
|
||||
rfc724_mid: std::ptr::null_mut(),
|
||||
in_reply_to: std::ptr::null_mut(),
|
||||
server_folder: None,
|
||||
@@ -341,7 +339,7 @@ pub fn dc_msg_new<'a>(context: &'a Context, viewtype: Viewtype) -> Message<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for Message<'a> {
|
||||
impl Drop for Message {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
free(self.rfc724_mid.cast());
|
||||
@@ -380,11 +378,11 @@ pub fn dc_msg_guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)>
|
||||
KNOWN.get(extension).map(|x| *x)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_msg_get_file(msg: &Message) -> *mut libc::c_char {
|
||||
pub unsafe fn dc_msg_get_file(context: &Context, msg: &Message) -> *mut libc::c_char {
|
||||
let mut file_abs = ptr::null_mut();
|
||||
|
||||
if let Some(file_rel) = msg.param.get(Param::File) {
|
||||
file_abs = dc_get_abs_path(msg.context, file_rel);
|
||||
file_abs = dc_get_abs_path(context, file_rel);
|
||||
}
|
||||
if !file_abs.is_null() {
|
||||
file_abs
|
||||
@@ -440,7 +438,7 @@ pub fn dc_msg_get_timestamp(msg: &Message) -> i64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_msg_load_from_db<'a>(context: &'a Context, id: u32) -> Result<Message<'a>, Error> {
|
||||
pub fn dc_msg_load_from_db(context: &Context, id: u32) -> Result<Message, Error> {
|
||||
context.sql.query_row(
|
||||
"SELECT \
|
||||
m.id,rfc724_mid,m.mime_in_reply_to,m.server_folder,m.server_uid,m.move_state,m.chat_id, \
|
||||
@@ -451,8 +449,7 @@ pub fn dc_msg_load_from_db<'a>(context: &'a Context, id: u32) -> Result<Message<
|
||||
params![id as i32],
|
||||
|row| {
|
||||
unsafe {
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
msg.context = context;
|
||||
let mut msg = dc_msg_new_untyped();
|
||||
msg.id = row.get::<_, i32>(0)? as u32;
|
||||
msg.rfc724_mid = row.get::<_, String>(1)?.strdup();
|
||||
msg.in_reply_to = match row.get::<_, Option<String>>(2)? {
|
||||
@@ -477,7 +474,7 @@ pub fn dc_msg_load_from_db<'a>(context: &'a Context, id: u32) -> Result<Message<
|
||||
if let Ok(t) = String::from_utf8(buf.to_vec()) {
|
||||
text = t;
|
||||
} else {
|
||||
warn!(context, 0, "dc_msg_load_from_db: could not get text column as non-lossy utf8 id {}", id);
|
||||
warn!(context, "dc_msg_load_from_db: could not get text column as non-lossy utf8 id {}", id);
|
||||
text = String::from_utf8_lossy(buf).into_owned();
|
||||
}
|
||||
} else {
|
||||
@@ -579,7 +576,7 @@ pub fn dc_markseen_msgs(context: &Context, msg_ids: *const u32, msg_cnt: usize)
|
||||
);
|
||||
|
||||
if msgs.is_err() {
|
||||
warn!(context, 0, "markseen_msgs failed: {:?}", msgs);
|
||||
warn!(context, "markseen_msgs failed: {:?}", msgs);
|
||||
return false;
|
||||
}
|
||||
let mut send_event = false;
|
||||
@@ -589,7 +586,7 @@ pub fn dc_markseen_msgs(context: &Context, msg_ids: *const u32, msg_cnt: usize)
|
||||
if curr_blocked == Blocked::Not {
|
||||
if curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed {
|
||||
dc_update_msg_state(context, id, MessageState::InSeen);
|
||||
info!(context, 0, "Seen message #{}.", id);
|
||||
info!(context, "Seen message #{}.", id);
|
||||
|
||||
job_add(
|
||||
context,
|
||||
@@ -643,7 +640,7 @@ pub fn dc_star_msgs(
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn dc_get_msg<'a>(context: &'a Context, msg_id: u32) -> Result<Message<'a>, Error> {
|
||||
pub fn dc_get_msg(context: &Context, msg_id: u32) -> Result<Message, Error> {
|
||||
dc_msg_load_from_db(context, msg_id)
|
||||
}
|
||||
|
||||
@@ -701,11 +698,10 @@ pub unsafe fn dc_msg_get_filename(msg: &Message) -> *mut libc::c_char {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_msg_get_filebytes(msg: &Message) -> uint64_t {
|
||||
pub fn dc_msg_get_filebytes(context: &Context, msg: &Message) -> uint64_t {
|
||||
if let Some(file) = msg.param.get(Param::File) {
|
||||
return dc_get_filebytes(msg.context, &file);
|
||||
return dc_get_filebytes(context, &file);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
@@ -730,14 +726,14 @@ pub fn dc_msg_get_showpadlock(msg: &Message) -> libc::c_int {
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe fn dc_msg_get_summary<'a>(msg: &mut Message<'a>, chat: Option<&Chat<'a>>) -> Lot {
|
||||
pub fn dc_msg_get_summary(context: &Context, msg: &mut Message, chat: Option<&Chat>) -> Lot {
|
||||
let mut ret = Lot::new();
|
||||
|
||||
let chat_loaded: Chat;
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
if let Ok(chat) = Chat::load_from_db(msg.context, msg.chat_id) {
|
||||
if let Ok(chat) = Chat::load_from_db(context, msg.chat_id) {
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
} else {
|
||||
@@ -748,17 +744,18 @@ pub unsafe fn dc_msg_get_summary<'a>(msg: &mut Message<'a>, chat: Option<&Chat<'
|
||||
let contact = if msg.from_id != DC_CONTACT_ID_SELF as libc::c_uint
|
||||
&& ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
Contact::get_by_id((*chat).context, msg.from_id).ok()
|
||||
Contact::get_by_id(context, msg.from_id).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
ret.fill(msg, chat, contact.as_ref(), msg.context);
|
||||
ret.fill(msg, chat, contact.as_ref(), context);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub unsafe fn dc_msg_get_summarytext(
|
||||
context: &Context,
|
||||
msg: &mut Message,
|
||||
approx_characters: usize,
|
||||
) -> *mut libc::c_char {
|
||||
@@ -767,7 +764,7 @@ pub unsafe fn dc_msg_get_summarytext(
|
||||
msg.text.as_ref(),
|
||||
&mut msg.param,
|
||||
approx_characters,
|
||||
msg.context,
|
||||
context,
|
||||
)
|
||||
.strdup()
|
||||
}
|
||||
@@ -898,7 +895,7 @@ pub fn dc_msg_is_setupmessage(msg: &Message) -> bool {
|
||||
msg.param.get_int(Param::Cmd) == Some(6)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_msg_get_setupcodebegin(msg: &Message) -> *mut libc::c_char {
|
||||
pub unsafe fn dc_msg_get_setupcodebegin(context: &Context, msg: &Message) -> *mut libc::c_char {
|
||||
let mut filename: *mut libc::c_char = ptr::null_mut();
|
||||
let mut buf: *mut libc::c_char = ptr::null_mut();
|
||||
let mut buf_bytes: size_t = 0i32 as size_t;
|
||||
@@ -908,11 +905,11 @@ pub unsafe fn dc_msg_get_setupcodebegin(msg: &Message) -> *mut libc::c_char {
|
||||
let mut buf_setupcodebegin: *const libc::c_char = ptr::null();
|
||||
let mut ret: *mut libc::c_char = ptr::null_mut();
|
||||
if dc_msg_is_setupmessage(msg) {
|
||||
filename = dc_msg_get_file(msg);
|
||||
filename = dc_msg_get_file(context, msg);
|
||||
if !(filename.is_null() || *filename.offset(0isize) as libc::c_int == 0i32) {
|
||||
if !(0
|
||||
== dc_read_file(
|
||||
msg.context,
|
||||
context,
|
||||
filename,
|
||||
&mut buf as *mut *mut libc::c_char as *mut *mut libc::c_void,
|
||||
&mut buf_bytes,
|
||||
@@ -977,6 +974,7 @@ pub fn dc_msg_set_duration(msg: &mut Message, duration: libc::c_int) {
|
||||
}
|
||||
|
||||
pub fn dc_msg_latefiling_mediasize(
|
||||
context: &Context,
|
||||
msg: &mut Message,
|
||||
width: libc::c_int,
|
||||
height: libc::c_int,
|
||||
@@ -989,20 +987,20 @@ pub fn dc_msg_latefiling_mediasize(
|
||||
if duration > 0 {
|
||||
msg.param.set_int(Param::Duration, duration);
|
||||
}
|
||||
dc_msg_save_param_to_disk(msg);
|
||||
dc_msg_save_param_to_disk(context, msg);
|
||||
}
|
||||
|
||||
pub fn dc_msg_save_param_to_disk(msg: &mut Message) -> bool {
|
||||
pub fn dc_msg_save_param_to_disk(context: &Context, msg: &mut Message) -> bool {
|
||||
sql::execute(
|
||||
msg.context,
|
||||
&msg.context.sql,
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET param=? WHERE id=?;",
|
||||
params![msg.param.to_string(), msg.id as i32],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn dc_msg_new_load<'a>(context: &'a Context, msg_id: u32) -> Result<Message<'a>, Error> {
|
||||
pub fn dc_msg_new_load(context: &Context, msg_id: u32) -> Result<Message, Error> {
|
||||
dc_msg_load_from_db(context, msg_id)
|
||||
}
|
||||
|
||||
@@ -1032,25 +1030,23 @@ The value is also used for CC:-summaries */
|
||||
|
||||
// Context functions to work with messages
|
||||
|
||||
pub unsafe fn dc_msg_exists(context: &Context, msg_id: u32) -> libc::c_int {
|
||||
if msg_id <= 9 {
|
||||
return 0;
|
||||
pub fn dc_msg_exists(context: &Context, msg_id: u32) -> bool {
|
||||
if msg_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return false;
|
||||
}
|
||||
|
||||
let chat_id: Option<i32> = context.sql.query_row_col(
|
||||
let chat_id: Option<u32> = context.sql.query_row_col(
|
||||
context,
|
||||
"SELECT chat_id FROM msgs WHERE id=?;",
|
||||
params![msg_id as i32],
|
||||
params![msg_id],
|
||||
0,
|
||||
);
|
||||
|
||||
if let Some(chat_id) = chat_id {
|
||||
if chat_id != 3 {
|
||||
return 1;
|
||||
}
|
||||
chat_id != DC_CHAT_ID_TRASH
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub fn dc_update_msg_move_state(
|
||||
@@ -1076,7 +1072,7 @@ pub fn dc_set_msg_failed(context: &Context, msg_id: u32, error: Option<impl AsRe
|
||||
}
|
||||
if let Some(error) = error {
|
||||
msg.param.set(Param::Error, error.as_ref());
|
||||
error!(context, 0, "{}", error.as_ref());
|
||||
error!(context, "{}", error.as_ref());
|
||||
}
|
||||
|
||||
if sql::execute(
|
||||
@@ -1205,7 +1201,7 @@ pub fn dc_get_real_msg_cnt(context: &Context) -> libc::c_int {
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!(context, 0, "dc_get_real_msg_cnt() failed. {}", err);
|
||||
error!(context, "dc_get_real_msg_cnt() failed. {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -1221,7 +1217,7 @@ pub fn dc_get_deaddrop_msg_cnt(context: &Context) -> size_t {
|
||||
) {
|
||||
Ok(res) => res as size_t,
|
||||
Err(err) => {
|
||||
error!(context, 0, "dc_get_deaddrop_msg_cnt() failed. {}", err);
|
||||
error!(context, "dc_get_deaddrop_msg_cnt() failed. {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -1236,7 +1232,7 @@ pub fn dc_rfc724_mid_cnt(context: &Context, rfc724_mid: *const libc::c_char) ->
|
||||
) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
error!(context, 0, "dc_get_rfc724_mid_cnt() failed. {}", err);
|
||||
error!(context, "dc_get_rfc724_mid_cnt() failed. {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -1290,7 +1286,7 @@ pub fn dc_update_server_uid(
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "msg: failed to update server_uid: {}", err);
|
||||
warn!(context, "msg: failed to update server_uid: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1323,7 +1319,7 @@ mod tests {
|
||||
|
||||
let chat = chat::create_by_contact_id(ctx, contact).unwrap();
|
||||
|
||||
let mut msg = dc_msg_new(ctx, Viewtype::Text);
|
||||
let mut msg = dc_msg_new(Viewtype::Text);
|
||||
|
||||
let msg_id = chat::prepare_msg(ctx, chat, &mut msg).unwrap();
|
||||
|
||||
|
||||
@@ -97,10 +97,7 @@ pub fn dc_get_oauth2_access_token(
|
||||
|
||||
let (redirect_uri, token_url, update_redirect_uri_on_success) =
|
||||
if refresh_token.is_none() || refresh_token_for != code.as_ref() {
|
||||
info!(
|
||||
context,
|
||||
0, "Generate OAuth2 refresh_token and access_token...",
|
||||
);
|
||||
info!(context, "Generate OAuth2 refresh_token and access_token...",);
|
||||
(
|
||||
context
|
||||
.sql
|
||||
@@ -112,7 +109,7 @@ pub fn dc_get_oauth2_access_token(
|
||||
} else {
|
||||
info!(
|
||||
context,
|
||||
0, "Regenerate OAuth2 access_token by refresh_token...",
|
||||
"Regenerate OAuth2 access_token by refresh_token...",
|
||||
);
|
||||
(
|
||||
context
|
||||
@@ -134,7 +131,7 @@ pub fn dc_get_oauth2_access_token(
|
||||
if response.is_err() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Error calling OAuth2 at {}: {:?}", token_url, response
|
||||
"Error calling OAuth2 at {}: {:?}", token_url, response
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@@ -142,7 +139,6 @@ pub fn dc_get_oauth2_access_token(
|
||||
if !response.status().is_success() {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Error calling OAuth2 at {}: {:?}",
|
||||
token_url,
|
||||
response.status()
|
||||
@@ -154,7 +150,7 @@ pub fn dc_get_oauth2_access_token(
|
||||
if parsed.is_err() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Failed to parse OAuth2 JSON response from {}: error: {:?}", token_url, parsed
|
||||
"Failed to parse OAuth2 JSON response from {}: error: {:?}", token_url, parsed
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@@ -195,12 +191,12 @@ pub fn dc_get_oauth2_access_token(
|
||||
.ok();
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "Failed to find OAuth2 access token");
|
||||
warn!(context, "Failed to find OAuth2 access token");
|
||||
}
|
||||
|
||||
response.access_token
|
||||
} else {
|
||||
warn!(context, 0, "Internal OAuth2 error: 2");
|
||||
warn!(context, "Internal OAuth2 error: 2");
|
||||
|
||||
None
|
||||
}
|
||||
@@ -268,17 +264,12 @@ impl Oauth2 {
|
||||
// }
|
||||
let response = reqwest::Client::new().get(&userinfo_url).send();
|
||||
if response.is_err() {
|
||||
warn!(context, 0, "Error getting userinfo: {:?}", response);
|
||||
warn!(context, "Error getting userinfo: {:?}", response);
|
||||
return None;
|
||||
}
|
||||
let mut response = response.unwrap();
|
||||
if !response.status().is_success() {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Error getting userinfo: {:?}",
|
||||
response.status()
|
||||
);
|
||||
warn!(context, "Error getting userinfo: {:?}", response.status());
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -286,19 +277,19 @@ impl Oauth2 {
|
||||
if parsed.is_err() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Failed to parse userinfo JSON response: {:?}", parsed
|
||||
"Failed to parse userinfo JSON response: {:?}", parsed
|
||||
);
|
||||
return None;
|
||||
}
|
||||
if let Ok(response) = parsed {
|
||||
let addr = response.get("email");
|
||||
if addr.is_none() {
|
||||
warn!(context, 0, "E-mail missing in userinfo.");
|
||||
warn!(context, "E-mail missing in userinfo.");
|
||||
}
|
||||
|
||||
addr.map(|addr| addr.to_string())
|
||||
} else {
|
||||
warn!(context, 0, "Failed to parse userinfo.");
|
||||
warn!(context, "Failed to parse userinfo.");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
18
src/pgp.rs
18
src/pgp.rs
@@ -1,6 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Cursor;
|
||||
use std::ptr;
|
||||
|
||||
@@ -274,32 +273,25 @@ pub fn dc_pgp_pk_decrypt(
|
||||
}
|
||||
|
||||
/// Symmetric encryption.
|
||||
pub fn dc_pgp_symm_encrypt(passphrase: *const libc::c_char, plain: &[u8]) -> Option<String> {
|
||||
assert!(!passphrase.is_null(), "invalid passphrase");
|
||||
|
||||
let pw = unsafe { CStr::from_ptr(passphrase).to_str().unwrap() };
|
||||
|
||||
pub fn dc_pgp_symm_encrypt(passphrase: &str, plain: &[u8]) -> Option<String> {
|
||||
let mut rng = thread_rng();
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
|
||||
let s2k = StringToKey::new_default(&mut rng);
|
||||
let msg = lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || pw.into());
|
||||
let msg =
|
||||
lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || passphrase.into());
|
||||
|
||||
msg.and_then(|msg| msg.to_armored_string(None)).ok()
|
||||
}
|
||||
|
||||
/// Symmetric decryption.
|
||||
pub fn dc_pgp_symm_decrypt(passphrase: *const libc::c_char, ctext: &[u8]) -> Option<Vec<u8>> {
|
||||
assert!(!passphrase.is_null(), "invalid passphrase");
|
||||
|
||||
let pw = unsafe { CStr::from_ptr(passphrase).to_str().unwrap() };
|
||||
|
||||
pub fn dc_pgp_symm_decrypt(passphrase: &str, ctext: &[u8]) -> Option<Vec<u8>> {
|
||||
let enc_msg = Message::from_bytes(Cursor::new(ctext));
|
||||
|
||||
enc_msg
|
||||
.and_then(|msg| {
|
||||
let mut decryptor = msg
|
||||
.decrypt_with_password(|| pw.into())
|
||||
.decrypt_with_password(|| passphrase.into())
|
||||
.expect("failed decryption");
|
||||
decryptor.next().expect("no message")
|
||||
})
|
||||
|
||||
@@ -37,7 +37,7 @@ impl Into<Lot> for Error {
|
||||
pub fn check_qr(context: &Context, qr: impl AsRef<str>) -> Lot {
|
||||
let qr = qr.as_ref();
|
||||
|
||||
info!(context, 0, "Scanned QR code: {}", qr);
|
||||
info!(context, "Scanned QR code: {}", qr);
|
||||
|
||||
if qr.starts_with(OPENPGP4FPR_SCHEME) {
|
||||
decode_openpgp(context, qr)
|
||||
|
||||
@@ -85,7 +85,7 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: uint32_t) -> Optio
|
||||
let self_addr = match context.sql.get_config(context, "configured_addr") {
|
||||
Some(addr) => addr,
|
||||
None => {
|
||||
error!(context, 0, "Not configured, cannot generate QR code.",);
|
||||
error!(context, "Not configured, cannot generate QR code.",);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
@@ -123,10 +123,7 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: uint32_t) -> Optio
|
||||
&auth,
|
||||
))
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot get QR-code for chat-id {}", group_chat_id,
|
||||
);
|
||||
error!(context, "Cannot get QR-code for chat-id {}", group_chat_id,);
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
@@ -136,7 +133,7 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: uint32_t) -> Optio
|
||||
))
|
||||
};
|
||||
|
||||
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
|
||||
info!(context, "Generated QR code: {}", qr.as_ref().unwrap());
|
||||
|
||||
qr
|
||||
}
|
||||
@@ -182,7 +179,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> uint32_t {
|
||||
let mut contact_chat_id: uint32_t = 0;
|
||||
let mut join_vg: bool = false;
|
||||
|
||||
info!(context, 0, "Requesting secure-join ...",);
|
||||
info!(context, "Requesting secure-join ...",);
|
||||
ensure_secret_key_exists(context).ok();
|
||||
if !dc_alloc_ongoing(context) {
|
||||
return cleanup(&context, contact_chat_id, false, join_vg);
|
||||
@@ -190,12 +187,12 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> uint32_t {
|
||||
let qr_scan = check_qr(context, &qr);
|
||||
if qr_scan.state != LotState::QrAskVerifyContact && qr_scan.state != LotState::QrAskVerifyGroup
|
||||
{
|
||||
error!(context, 0, "Unknown QR code.",);
|
||||
error!(context, "Unknown QR code.",);
|
||||
return cleanup(&context, contact_chat_id, true, join_vg);
|
||||
}
|
||||
contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default();
|
||||
if contact_chat_id == 0 {
|
||||
error!(context, 0, "Unknown contact.",);
|
||||
error!(context, "Unknown contact.",);
|
||||
return cleanup(&context, contact_chat_id, true, join_vg);
|
||||
}
|
||||
if check_exit(context) {
|
||||
@@ -221,7 +218,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> uint32_t {
|
||||
.unwrap(),
|
||||
contact_chat_id,
|
||||
) {
|
||||
info!(context, 0, "Taking protocol shortcut.");
|
||||
info!(context, "Taking protocol shortcut.");
|
||||
context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
|
||||
joiner_progress!(context, chat_id_2_contact_id(context, contact_chat_id), 400);
|
||||
let own_fingerprint = get_self_fingerprint(context).unwrap();
|
||||
@@ -277,7 +274,7 @@ fn send_handshake_msg(
|
||||
fingerprint: Option<String>,
|
||||
grpid: impl AsRef<str>,
|
||||
) {
|
||||
let mut msg = unsafe { dc_msg_new_untyped(context) };
|
||||
let mut msg = dc_msg_new_untyped();
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(format!("Secure-Join: {}", step));
|
||||
msg.hidden = true;
|
||||
@@ -359,7 +356,7 @@ pub fn handle_securejoin_handshake(
|
||||
};
|
||||
info!(
|
||||
context,
|
||||
0, ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step,
|
||||
">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step,
|
||||
);
|
||||
let (contact_chat_id, contact_chat_id_blocked) =
|
||||
chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default();
|
||||
@@ -383,15 +380,15 @@ pub fn handle_securejoin_handshake(
|
||||
let invitenumber = match lookup_field(mimeparser, "Secure-Join-Invitenumber") {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
warn!(context, 0, "Secure-join denied (invitenumber missing).",);
|
||||
warn!(context, "Secure-join denied (invitenumber missing).",);
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
if !token::exists(context, token::Namespace::InviteNumber, &invitenumber) {
|
||||
warn!(context, 0, "Secure-join denied (bad invitenumber).",);
|
||||
warn!(context, "Secure-join denied (bad invitenumber).",);
|
||||
return ret;
|
||||
}
|
||||
info!(context, 0, "Secure-join requested.",);
|
||||
info!(context, "Secure-join requested.",);
|
||||
|
||||
inviter_progress!(context, contact_id, 300);
|
||||
send_handshake_msg(
|
||||
@@ -413,7 +410,7 @@ pub fn handle_securejoin_handshake(
|
||||
};
|
||||
|
||||
if cond {
|
||||
warn!(context, 0, "auth-required message out of sync.",);
|
||||
warn!(context, "auth-required message out of sync.",);
|
||||
// no error, just aborted somehow or a mail from another handshake
|
||||
return ret;
|
||||
}
|
||||
@@ -442,7 +439,7 @@ pub fn handle_securejoin_handshake(
|
||||
end_bobs_joining(context, DC_BOB_ERROR);
|
||||
return ret;
|
||||
}
|
||||
info!(context, 0, "Fingerprint verified.",);
|
||||
info!(context, "Fingerprint verified.",);
|
||||
own_fingerprint = get_self_fingerprint(context).unwrap();
|
||||
joiner_progress!(context, contact_id, 400);
|
||||
context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
|
||||
@@ -494,7 +491,7 @@ pub fn handle_securejoin_handshake(
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
info!(context, 0, "Fingerprint verified.",);
|
||||
info!(context, "Fingerprint verified.",);
|
||||
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
|
||||
let auth_0 = match lookup_field(mimeparser, "Secure-Join-Auth") {
|
||||
Some(auth) => auth,
|
||||
@@ -520,7 +517,7 @@ pub fn handle_securejoin_handshake(
|
||||
return ret;
|
||||
}
|
||||
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited);
|
||||
info!(context, 0, "Auth verified.",);
|
||||
info!(context, "Auth verified.",);
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
emit_event!(context, Event::CONTACTS_CHANGED, contact_id, 0);
|
||||
inviter_progress!(context, contact_id, 600);
|
||||
@@ -528,7 +525,7 @@ pub fn handle_securejoin_handshake(
|
||||
let field_grpid = lookup_field(mimeparser, "Secure-Join-Group").unwrap_or_default();
|
||||
let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid);
|
||||
if group_chat_id == 0 {
|
||||
error!(context, 0, "Chat {} not found.", &field_grpid);
|
||||
error!(context, "Chat {} not found.", &field_grpid);
|
||||
return ret;
|
||||
} else {
|
||||
chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, 0x1i32);
|
||||
@@ -543,7 +540,7 @@ pub fn handle_securejoin_handshake(
|
||||
ret = DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING;
|
||||
}
|
||||
if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM {
|
||||
info!(context, 0, "Message belongs to a different handshake.",);
|
||||
info!(context, "Message belongs to a different handshake.",);
|
||||
return ret;
|
||||
}
|
||||
let cond = {
|
||||
@@ -554,7 +551,7 @@ pub fn handle_securejoin_handshake(
|
||||
if cond {
|
||||
warn!(
|
||||
context,
|
||||
0, "Message out of sync or belongs to a different handshake.",
|
||||
"Message out of sync or belongs to a different handshake.",
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
@@ -597,7 +594,7 @@ pub fn handle_securejoin_handshake(
|
||||
let cg_member_added =
|
||||
lookup_field(mimeparser, "Chat-Group-Member-Added").unwrap_or_default();
|
||||
if join_vg && !addr_equals_self(context, cg_member_added) {
|
||||
info!(context, 0, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group).");
|
||||
info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group).");
|
||||
return ret;
|
||||
}
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
@@ -620,19 +617,19 @@ pub fn handle_securejoin_handshake(
|
||||
==== Step 8 in "Out-of-band verified groups" protocol ====
|
||||
============================================================ */
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if contact.is_verified() == VerifiedStatus::Unverified {
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
if contact.is_verified(context) == VerifiedStatus::Unverified {
|
||||
warn!(context, "vg-member-added-received invalid.",);
|
||||
return ret;
|
||||
}
|
||||
inviter_progress!(context, contact_id, 800);
|
||||
inviter_progress!(context, contact_id, 1000);
|
||||
} else {
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
warn!(context, "vg-member-added-received invalid.",);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
warn!(context, 0, "invalid step: {}", step);
|
||||
warn!(context, "invalid step: {}", step);
|
||||
}
|
||||
}
|
||||
if ret == DC_HANDSHAKE_STOP_NORMAL_PROCESSING {
|
||||
@@ -694,7 +691,7 @@ fn could_not_establish_secure_connection(
|
||||
);
|
||||
|
||||
chat::add_device_msg(context, contact_chat_id, &msg);
|
||||
error!(context, 0, "{} ({})", &msg, details);
|
||||
error!(context, "{} ({})", &msg, details);
|
||||
}
|
||||
|
||||
fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> Result<(), Error> {
|
||||
@@ -723,13 +720,13 @@ fn encrypted_and_signed(
|
||||
expected_fingerprint: impl AsRef<str>,
|
||||
) -> bool {
|
||||
if !mimeparser.e2ee_helper.encrypted {
|
||||
warn!(mimeparser.context, 0, "Message not encrypted.",);
|
||||
warn!(mimeparser.context, "Message not encrypted.",);
|
||||
false
|
||||
} else if mimeparser.e2ee_helper.signatures.len() <= 0 {
|
||||
warn!(mimeparser.context, 0, "Message not signed.",);
|
||||
warn!(mimeparser.context, "Message not signed.",);
|
||||
false
|
||||
} else if expected_fingerprint.as_ref().is_empty() {
|
||||
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
|
||||
warn!(mimeparser.context, "Fingerprint for comparison missing.",);
|
||||
false
|
||||
} else if !mimeparser
|
||||
.e2ee_helper
|
||||
@@ -738,7 +735,6 @@ fn encrypted_and_signed(
|
||||
{
|
||||
warn!(
|
||||
mimeparser.context,
|
||||
0,
|
||||
"Message does not match expected fingerprint {}.",
|
||||
expected_fingerprint.as_ref(),
|
||||
);
|
||||
|
||||
@@ -45,7 +45,7 @@ impl Smtp {
|
||||
/// Connect using the provided login params
|
||||
pub fn connect(&mut self, context: &Context, lp: &LoginParam) -> bool {
|
||||
if self.is_connected() {
|
||||
warn!(context, 0, "SMTP already connected.");
|
||||
warn!(context, "SMTP already connected.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ impl Smtp {
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "SMTP: failed to establish connection {:?}", err);
|
||||
warn!(context, "SMTP: failed to establish connection {:?}", err);
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -151,7 +151,7 @@ impl Smtp {
|
||||
1
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "SMTP failed to send message: {}", err);
|
||||
warn!(context, "SMTP failed to send message: {}", err);
|
||||
self.error = Some(format!("{}", err));
|
||||
0
|
||||
}
|
||||
|
||||
60
src/sql.rs
60
src/sql.rs
@@ -35,7 +35,7 @@ impl Sql {
|
||||
self.in_use.remove();
|
||||
// drop closes the connection
|
||||
|
||||
info!(context, 0, "Database closed.");
|
||||
info!(context, "Database closed.");
|
||||
}
|
||||
|
||||
// return true on success, false on failure
|
||||
@@ -176,7 +176,7 @@ impl Sql {
|
||||
rusqlite::types::Type::Null,
|
||||
))) => None,
|
||||
Err(err) => {
|
||||
error!(context, 0, "sql: Failed query_row: {}", err);
|
||||
error!(context, "sql: Failed query_row: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ impl Sql {
|
||||
value: Option<&str>,
|
||||
) -> Result<()> {
|
||||
if !self.is_open() {
|
||||
error!(context, 0, "set_config(): Database not ready.");
|
||||
error!(context, "set_config(): Database not ready.");
|
||||
return Err(Error::SqlNoConnection);
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ impl Sql {
|
||||
match res {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
error!(context, 0, "set_config(): Cannot change value. {:?}", &err);
|
||||
error!(context, "set_config(): Cannot change value. {:?}", &err);
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
@@ -259,6 +259,20 @@ impl Sql {
|
||||
self.get_config(context, key).and_then(|s| s.parse().ok())
|
||||
}
|
||||
|
||||
pub fn get_config_bool(&self, context: &Context, key: impl AsRef<str>) -> bool {
|
||||
// Not the most obvious way to encode bool as string, but it is matter
|
||||
// of backward compatibility.
|
||||
self.get_config_int(context, key).unwrap_or_default() > 0
|
||||
}
|
||||
|
||||
pub fn set_config_bool<T>(&self, context: &Context, key: T, value: bool) -> Result<()>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
let value = if value { Some("1") } else { None };
|
||||
self.set_config(context, key, value)
|
||||
}
|
||||
|
||||
pub fn set_config_int64(
|
||||
&self,
|
||||
context: &Context,
|
||||
@@ -303,7 +317,6 @@ fn open(
|
||||
if sql.is_open() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot open, database \"{:?}\" already opened.",
|
||||
dbfile.as_ref(),
|
||||
);
|
||||
@@ -337,7 +350,6 @@ fn open(
|
||||
if !sql.table_exists("config") {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"First time init: creating tables in {:?}.",
|
||||
dbfile.as_ref(),
|
||||
);
|
||||
@@ -453,7 +465,6 @@ fn open(
|
||||
{
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create tables in new database \"{:?}\".",
|
||||
dbfile.as_ref(),
|
||||
);
|
||||
@@ -674,7 +685,7 @@ fn open(
|
||||
sql.set_config_int(context, "dbversion", 46)?;
|
||||
}
|
||||
if dbversion < 47 {
|
||||
info!(context, 0, "[migration] v47");
|
||||
info!(context, "[migration] v47");
|
||||
sql.execute(
|
||||
"ALTER TABLE jobs ADD COLUMN tries INTEGER DEFAULT 0;",
|
||||
params![],
|
||||
@@ -683,7 +694,7 @@ fn open(
|
||||
sql.set_config_int(context, "dbversion", 47)?;
|
||||
}
|
||||
if dbversion < 48 {
|
||||
info!(context, 0, "[migration] v48");
|
||||
info!(context, "[migration] v48");
|
||||
sql.execute(
|
||||
"ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;",
|
||||
params![],
|
||||
@@ -693,7 +704,7 @@ fn open(
|
||||
sql.set_config_int(context, "dbversion", 48)?;
|
||||
}
|
||||
if dbversion < 49 {
|
||||
info!(context, 0, "[migration] v49");
|
||||
info!(context, "[migration] v49");
|
||||
sql.execute(
|
||||
"ALTER TABLE chats ADD COLUMN gossiped_timestamp INTEGER DEFAULT 0;",
|
||||
params![],
|
||||
@@ -702,7 +713,7 @@ fn open(
|
||||
sql.set_config_int(context, "dbversion", 49)?;
|
||||
}
|
||||
if dbversion < 50 {
|
||||
info!(context, 0, "[migration] v50");
|
||||
info!(context, "[migration] v50");
|
||||
if 0 != exists_before_update {
|
||||
sql.set_config_int(context, "show_emails", 2)?;
|
||||
}
|
||||
@@ -710,7 +721,7 @@ fn open(
|
||||
sql.set_config_int(context, "dbversion", 50)?;
|
||||
}
|
||||
if dbversion < 53 {
|
||||
info!(context, 0, "[migration] v53");
|
||||
info!(context, "[migration] v53");
|
||||
sql.execute(
|
||||
"CREATE TABLE locations ( id INTEGER PRIMARY KEY AUTOINCREMENT, latitude REAL DEFAULT 0.0, longitude REAL DEFAULT 0.0, accuracy REAL DEFAULT 0.0, timestamp INTEGER DEFAULT 0, chat_id INTEGER DEFAULT 0, from_id INTEGER DEFAULT 0);",
|
||||
params![]
|
||||
@@ -743,7 +754,7 @@ fn open(
|
||||
sql.set_config_int(context, "dbversion", 53)?;
|
||||
}
|
||||
if dbversion < 54 {
|
||||
info!(context, 0, "[migration] v54");
|
||||
info!(context, "[migration] v54");
|
||||
sql.execute(
|
||||
"ALTER TABLE msgs ADD COLUMN location_id INTEGER DEFAULT 0;",
|
||||
params![],
|
||||
@@ -783,7 +794,7 @@ fn open(
|
||||
// for newer versions, we copy files always to the blob directory and store relative paths.
|
||||
// this snippet converts older databases and can be removed after some time.
|
||||
|
||||
info!(context, 0, "[open] update file paths");
|
||||
info!(context, "[open] update file paths");
|
||||
|
||||
let repl_from = sql
|
||||
.get_config(context, "backup_for")
|
||||
@@ -810,7 +821,7 @@ fn open(
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "Opened {:?}.", dbfile.as_ref(),);
|
||||
info!(context, "Opened {:?}.", dbfile.as_ref(),);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -825,7 +836,6 @@ where
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"execute failed: {:?} for {}",
|
||||
&err,
|
||||
querystr.as_ref()
|
||||
@@ -842,7 +852,6 @@ pub fn try_execute(context: &Context, sql: &Sql, querystr: impl AsRef<str>) -> R
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Try-execute for \"{}\" failed: {}",
|
||||
querystr.as_ref(),
|
||||
&err,
|
||||
@@ -886,7 +895,7 @@ pub fn get_rowid_with_conn(
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
0, "sql: Failed to retrieve rowid: {} in {}", err, query
|
||||
"sql: Failed to retrieve rowid: {} in {}", err, query
|
||||
);
|
||||
0
|
||||
}
|
||||
@@ -933,7 +942,7 @@ pub fn get_rowid2_with_conn(
|
||||
) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
error!(context, 0, "sql: Failed to retrieve rowid2: {}", err);
|
||||
error!(context, "sql: Failed to retrieve rowid2: {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -943,7 +952,7 @@ pub fn housekeeping(context: &Context) {
|
||||
let mut files_in_use = HashSet::new();
|
||||
let mut unreferenced_count = 0;
|
||||
|
||||
info!(context, 0, "Start housekeeping...");
|
||||
info!(context, "Start housekeeping...");
|
||||
maybe_add_from_param(
|
||||
context,
|
||||
&mut files_in_use,
|
||||
@@ -983,10 +992,10 @@ pub fn housekeeping(context: &Context) {
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
warn!(context, 0, "sql: failed query: {}", err);
|
||||
warn!(context, "sql: failed query: {}", err);
|
||||
});
|
||||
|
||||
info!(context, 0, "{} files in use.", files_in_use.len(),);
|
||||
info!(context, "{} files in use.", files_in_use.len(),);
|
||||
/* go through directory and delete unused files */
|
||||
let p = std::path::Path::new(as_str(context.get_blobdir()));
|
||||
match std::fs::read_dir(p) {
|
||||
@@ -1025,7 +1034,6 @@ pub fn housekeeping(context: &Context) {
|
||||
if recently_created || recently_modified || recently_accessed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Housekeeping: Keeping new unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name(),
|
||||
@@ -1037,7 +1045,6 @@ pub fn housekeeping(context: &Context) {
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Housekeeping: Deleting unreferenced file #{}: {:?}",
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
@@ -1049,7 +1056,6 @@ pub fn housekeeping(context: &Context) {
|
||||
Err(err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Housekeeping: Cannot open {}. ({})",
|
||||
as_str(context.get_blobdir()),
|
||||
err
|
||||
@@ -1057,7 +1063,7 @@ pub fn housekeeping(context: &Context) {
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "Housekeeping done.",);
|
||||
info!(context, "Housekeeping done.",);
|
||||
}
|
||||
|
||||
fn is_file_in_use(files_in_use: &HashSet<String>, namespc_opt: Option<&str>, name: &str) -> bool {
|
||||
@@ -1105,7 +1111,7 @@ fn maybe_add_from_param(
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
warn!(context, 0, "sql: failed to add_from_param: {}", err);
|
||||
warn!(context, "sql: failed to add_from_param: {}", err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,15 @@ import os
|
||||
import re
|
||||
|
||||
if __name__ == "__main__":
|
||||
if Path('src/top_evil_rs.py').exists():
|
||||
os.chdir('src')
|
||||
filestats = []
|
||||
for fn in Path(".").glob("**/*.rs"):
|
||||
s = fn.read_text()
|
||||
s = re.sub(r"(?m)///.*$", "", s) # remove comments
|
||||
unsafe = s.count("unsafe")
|
||||
free = s.count("free(")
|
||||
gotoblocks = s.count("ok_to_continue")
|
||||
gotoblocks = s.count("ok_to_continue") + s.count('OK_TO_CONTINUE')
|
||||
filestats.append((fn, unsafe, free, gotoblocks))
|
||||
|
||||
sum_unsafe, sum_free, sum_gotoblocks = 0, 0, 0
|
||||
|
||||
188
tests/stress.rs
188
tests/stress.rs
@@ -12,7 +12,6 @@ use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::key::*;
|
||||
use deltachat::keyring::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::pgp::*;
|
||||
@@ -91,7 +90,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
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"));
|
||||
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,
|
||||
@@ -129,7 +128,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
free(fn1 as *mut libc::c_void);
|
||||
}
|
||||
|
||||
let res = context.get_config(config::Config::SysConfigKeys).unwrap();
|
||||
let res = context.get_config(&config::Config::SysConfigKeys).unwrap();
|
||||
|
||||
assert!(!res.contains(" probably_never_a_key "));
|
||||
assert!(res.contains(" addr "));
|
||||
@@ -461,136 +460,111 @@ unsafe fn stress_functions(context: &Context) {
|
||||
#[test]
|
||||
#[ignore] // is too expensive
|
||||
fn test_encryption_decryption() {
|
||||
unsafe {
|
||||
let mut bad_data: [libc::c_uchar; 4096] = [0; 4096];
|
||||
let mut i_0: libc::c_int = 0i32;
|
||||
while i_0 < 4096i32 {
|
||||
bad_data[i_0 as usize] = (i_0 & 0xffi32) as libc::c_uchar;
|
||||
i_0 += 1
|
||||
}
|
||||
let mut j: libc::c_int = 0i32;
|
||||
let (public_key, private_key) = dc_pgp_create_keypair("foo@bar.de").unwrap();
|
||||
|
||||
while j < 4096 / 40 {
|
||||
let bad_key = Key::from_binary(
|
||||
&mut *bad_data.as_mut_ptr().offset(j as isize) as *const u8,
|
||||
4096 / 2 + j,
|
||||
if 0 != j & 1 {
|
||||
KeyType::Public
|
||||
} else {
|
||||
KeyType::Private
|
||||
},
|
||||
);
|
||||
private_key.split_key().unwrap();
|
||||
|
||||
assert!(bad_key.is_none());
|
||||
j += 1
|
||||
}
|
||||
let (public_key2, private_key2) = dc_pgp_create_keypair("two@zwo.de").unwrap();
|
||||
|
||||
let (public_key, private_key) = dc_pgp_create_keypair("foo@bar.de").unwrap();
|
||||
assert_ne!(public_key, public_key2);
|
||||
|
||||
private_key.split_key().unwrap();
|
||||
let original_text = b"This is a test";
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(public_key.clone());
|
||||
keyring.add_ref(&public_key2);
|
||||
|
||||
let (public_key2, private_key2) = dc_pgp_create_keypair("two@zwo.de").unwrap();
|
||||
let ctext_signed = dc_pgp_pk_encrypt(original_text, &keyring, Some(&private_key)).unwrap();
|
||||
assert!(!ctext_signed.is_empty());
|
||||
assert!(ctext_signed.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
assert_ne!(public_key, public_key2);
|
||||
let ctext_unsigned = dc_pgp_pk_encrypt(original_text, &keyring, None).unwrap();
|
||||
assert!(!ctext_unsigned.is_empty());
|
||||
assert!(ctext_unsigned.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let original_text = b"This is a test";
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(public_key.clone());
|
||||
keyring.add_ref(&public_key2);
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(private_key);
|
||||
|
||||
let ctext_signed = dc_pgp_pk_encrypt(original_text, &keyring, Some(&private_key)).unwrap();
|
||||
assert!(!ctext_signed.is_empty());
|
||||
assert!(ctext_signed.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
let mut public_keyring = Keyring::default();
|
||||
public_keyring.add_ref(&public_key);
|
||||
|
||||
let ctext_unsigned = dc_pgp_pk_encrypt(original_text, &keyring, None).unwrap();
|
||||
assert!(!ctext_unsigned.is_empty());
|
||||
assert!(ctext_unsigned.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
let mut public_keyring2 = Keyring::default();
|
||||
public_keyring2.add_owned(public_key2.clone());
|
||||
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(private_key);
|
||||
let mut valid_signatures: HashSet<String> = Default::default();
|
||||
|
||||
let mut public_keyring = Keyring::default();
|
||||
public_keyring.add_ref(&public_key);
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut public_keyring2 = Keyring::default();
|
||||
public_keyring2.add_owned(public_key2.clone());
|
||||
assert_eq!(plain, original_text,);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
|
||||
let mut valid_signatures: HashSet<String> = Default::default();
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
let empty_keyring = Keyring::default();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&empty_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
|
||||
assert_eq!(plain, original_text,);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
valid_signatures.clear();
|
||||
|
||||
valid_signatures.clear();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
|
||||
let empty_keyring = Keyring::default();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&empty_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
valid_signatures.clear();
|
||||
|
||||
valid_signatures.clear();
|
||||
public_keyring2.add_ref(&public_key);
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
|
||||
valid_signatures.clear();
|
||||
valid_signatures.clear();
|
||||
|
||||
public_keyring2.add_ref(&public_key);
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_unsigned.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
assert_eq!(plain, original_text);
|
||||
|
||||
valid_signatures.clear();
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_unsigned.as_bytes(),
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_ref(&private_key2);
|
||||
let mut public_keyring = Keyring::default();
|
||||
public_keyring.add_ref(&public_key);
|
||||
|
||||
assert_eq!(plain, original_text);
|
||||
let plain =
|
||||
dc_pgp_pk_decrypt(ctext_signed.as_bytes(), &keyring, &public_keyring, None).unwrap();
|
||||
|
||||
valid_signatures.clear();
|
||||
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_ref(&private_key2);
|
||||
let mut public_keyring = Keyring::default();
|
||||
public_keyring.add_ref(&public_key);
|
||||
|
||||
let plain =
|
||||
dc_pgp_pk_decrypt(ctext_signed.as_bytes(), &keyring, &public_keyring, None).unwrap();
|
||||
|
||||
assert_eq!(plain, original_text);
|
||||
}
|
||||
assert_eq!(plain, original_text);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn cb(
|
||||
|
||||
Reference in New Issue
Block a user