mirror of
https://github.com/chatmail/core.git
synced 2026-07-01 03:56:36 +03:00
Compare commits
2 Commits
encryption
...
safe-dc_ms
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a602ee313 | ||
|
|
e0c1b91a7a |
@@ -898,15 +898,9 @@ pub unsafe extern "C" fn dc_set_chat_profile_image(
|
||||
|
||||
let context = &*context;
|
||||
|
||||
chat::set_chat_profile_image(context, chat_id, {
|
||||
if image.is_null() {
|
||||
""
|
||||
} else {
|
||||
as_str(image)
|
||||
}
|
||||
})
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(context, "Failed to set profile image")
|
||||
chat::set_chat_profile_image(context, chat_id, as_str(image))
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(context, "Failed to set profile image")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1786,7 +1780,7 @@ pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut
|
||||
let chat = &*chat;
|
||||
|
||||
match chat.get_profile_image() {
|
||||
Some(p) => p.to_str().unwrap().to_string().strdup(),
|
||||
Some(i) => i.strdup(),
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
@@ -2403,7 +2397,7 @@ pub unsafe extern "C" fn dc_contact_get_profile_image(
|
||||
|
||||
contact
|
||||
.get_profile_image()
|
||||
.map(|p| p.to_str().unwrap().to_string().strdup())
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
|
||||
@@ -483,10 +483,6 @@ class EventLogger:
|
||||
def set_timeout(self, timeout):
|
||||
self._timeout = timeout
|
||||
|
||||
def consume_events(self, check_error=True):
|
||||
while not self._event_queue.empty():
|
||||
self.get()
|
||||
|
||||
def get(self, timeout=None, check_error=True):
|
||||
timeout = timeout or self._timeout
|
||||
ev = self._event_queue.get(timeout=timeout)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
""" chatting related objects: Contact, Chat, Message. """
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
from . import props
|
||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
||||
from .capi import lib, ffi
|
||||
@@ -316,46 +315,3 @@ class Chat(object):
|
||||
return list(iter_array(
|
||||
dc_array, lambda id: Contact(self._dc_context, id))
|
||||
)
|
||||
|
||||
def set_profile_image(self, img_path):
|
||||
"""Set group profile image.
|
||||
|
||||
If the group is already promoted (any message was sent to the group),
|
||||
all group members are informed by a special status message that is sent
|
||||
automatically by this function.
|
||||
:params img_path: path to image object
|
||||
:raises ValueError: if profile image could not be set
|
||||
:returns: None
|
||||
"""
|
||||
assert os.path.exists(img_path), img_path
|
||||
p = as_dc_charpointer(img_path)
|
||||
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, p)
|
||||
if res != 1:
|
||||
raise ValueError("Setting Profile Image {!r} failed".format(p))
|
||||
|
||||
def remove_profile_image(self):
|
||||
"""remove group profile image.
|
||||
|
||||
If the group is already promoted (any message was sent to the group),
|
||||
all group members are informed by a special status message that is sent
|
||||
automatically by this function.
|
||||
:raises ValueError: if profile image could not be reset
|
||||
:returns: None
|
||||
"""
|
||||
res = lib.dc_set_chat_profile_image(self._dc_context, self.id, ffi.NULL)
|
||||
if res != 1:
|
||||
raise ValueError("Removing Profile Image failed")
|
||||
|
||||
def get_profile_image(self):
|
||||
"""Get group profile image.
|
||||
|
||||
For groups, this is the image set by any group member using
|
||||
set_chat_profile_image(). For normal chats, this is the image
|
||||
set by each remote user on their own using dc_set_config(context,
|
||||
"selfavatar", image).
|
||||
:returns: path to profile image, None if no profile image exists.
|
||||
"""
|
||||
dc_res = lib.dc_chat_get_profile_image(self._dc_chat)
|
||||
if dc_res == ffi.NULL:
|
||||
return None
|
||||
return from_dc_charpointer(dc_res)
|
||||
|
||||
@@ -147,15 +147,6 @@ class TestOfflineChat:
|
||||
qr = chat.get_join_qr()
|
||||
assert ac2.check_qr(qr).is_ask_verifygroup
|
||||
|
||||
def test_get_set_profile_image_simple(self, ac1, data):
|
||||
chat = ac1.create_group_chat(name="title1")
|
||||
p = data.get_path("d.png")
|
||||
chat.set_profile_image(p)
|
||||
p2 = chat.get_profile_image()
|
||||
assert open(p, "rb").read() == open(p2, "rb").read()
|
||||
chat.remove_profile_image()
|
||||
assert chat.get_profile_image() is None
|
||||
|
||||
def test_delete_and_send_fails(self, ac1, chat1):
|
||||
chat1.delete()
|
||||
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
@@ -598,59 +589,3 @@ class TestOnlineAccount:
|
||||
ch = ac2.qr_join_chat(qr)
|
||||
assert ch.id >= 10
|
||||
wait_securejoin_inviter_progress(ac1, 1000)
|
||||
|
||||
def test_set_get_profile_image(self, acfactory, data, lp):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
|
||||
lp.sec("create unpromoted group chat")
|
||||
chat = ac1.create_group_chat("hello")
|
||||
p = data.get_path("d.png")
|
||||
|
||||
lp.sec("ac1: set profile image on unpromoted chat")
|
||||
chat.set_profile_image(p)
|
||||
ac1._evlogger.get_matching("DC_EVENT_CHAT_MODIFIED")
|
||||
assert not chat.is_promoted()
|
||||
|
||||
lp.sec("ac1: send text to promote chat (XXX without contact added)")
|
||||
# XXX first promote the chat before adding contact
|
||||
# because DC does not send out profile images for unpromoted chats
|
||||
# otherwise
|
||||
chat.send_text("ac1: initial message to promote chat (workaround)")
|
||||
assert chat.is_promoted()
|
||||
|
||||
lp.sec("ac2: add ac1 to a chat so the message does not land in DEADDROP")
|
||||
c1 = ac2.create_contact(email=ac1.get_config("addr"))
|
||||
ac2.create_chat_by_contact(c1)
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
|
||||
lp.sec("ac1: add ac2 to promoted group chat")
|
||||
c2 = ac1.create_contact(email=ac2.get_config("addr"))
|
||||
chat.add_contact(c2)
|
||||
|
||||
lp.sec("ac1: send a first message to ac2")
|
||||
chat.send_text("hi")
|
||||
assert chat.is_promoted()
|
||||
|
||||
lp.sec("ac2: wait for receiving message from ac1")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
msg_in = ac2.get_message_by_id(ev[2])
|
||||
assert not msg_in.chat.is_deaddrop()
|
||||
|
||||
lp.sec("ac2: create chat and read profile image")
|
||||
chat2 = ac2.create_chat_by_message(msg_in)
|
||||
p2 = chat2.get_profile_image()
|
||||
assert p2 is not None
|
||||
assert open(p2, "rb").read() == open(p, "rb").read()
|
||||
|
||||
ac2._evlogger.consume_events()
|
||||
ac1._evlogger.consume_events()
|
||||
lp.sec("ac2: delete profile image from chat")
|
||||
chat2.remove_profile_image()
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
assert ev[1] == chat.id
|
||||
chat1b = ac1.create_chat_by_message(ev[2])
|
||||
assert chat1b.get_profile_image() is None
|
||||
assert chat.get_profile_image() is None
|
||||
|
||||
122
src/chat.rs
122
src/chat.rs
@@ -1,5 +1,5 @@
|
||||
use std::ffi::CString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::chatlist::*;
|
||||
use crate::constants::*;
|
||||
@@ -72,15 +72,15 @@ impl<'a> Chat<'a> {
|
||||
},
|
||||
Ok(mut chat) => {
|
||||
match chat.id {
|
||||
DC_CHAT_ID_DEADDROP => {
|
||||
1 => {
|
||||
chat.name = chat.context.stock_str(StockMessage::DeadDrop).into();
|
||||
}
|
||||
DC_CHAT_ID_ARCHIVED_LINK => {
|
||||
6 => {
|
||||
let tempname = chat.context.stock_str(StockMessage::ArchivedChats);
|
||||
let cnt = dc_get_archived_cnt(chat.context);
|
||||
chat.name = format!("{} ({})", tempname, cnt);
|
||||
}
|
||||
DC_CHAT_ID_STARRED => {
|
||||
5 => {
|
||||
chat.name = chat.context.stock_str(StockMessage::StarredMsgs).into();
|
||||
}
|
||||
_ => {
|
||||
@@ -187,10 +187,10 @@ impl<'a> Chat<'a> {
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn get_profile_image(&self) -> Option<PathBuf> {
|
||||
pub unsafe fn get_profile_image(&self) -> Option<String> {
|
||||
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(to_string(dc_get_abs_path(self.context, image_rel)));
|
||||
}
|
||||
} else if self.typ == Chattype::Single {
|
||||
let contacts = get_chat_contacts(self.context, self.id);
|
||||
@@ -229,10 +229,6 @@ impl<'a> Chat<'a> {
|
||||
self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1
|
||||
}
|
||||
|
||||
pub fn is_promoted(&self) -> bool {
|
||||
!self.is_unpromoted()
|
||||
}
|
||||
|
||||
pub fn is_verified(&self) -> bool {
|
||||
(self.typ == Chattype::VerifiedGroup)
|
||||
}
|
||||
@@ -1486,10 +1482,7 @@ pub unsafe fn remove_contact_from_chat(
|
||||
"bad chat_id = {} <= 9",
|
||||
chat_id
|
||||
);
|
||||
ensure!(
|
||||
contact_id > DC_CONTACT_ID_LAST_SPECIAL || contact_id == DC_CONTACT_ID_SELF,
|
||||
"Cannot remove special contact"
|
||||
);
|
||||
ensure!(contact_id != DC_CONTACT_ID_SELF, "Cannot remove self");
|
||||
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
let mut success = false;
|
||||
@@ -1508,7 +1501,7 @@ pub unsafe fn remove_contact_from_chat(
|
||||
} else {
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if chat.is_promoted() {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
msg.type_0 = Viewtype::Text;
|
||||
if contact.id == DC_CONTACT_ID_SELF {
|
||||
set_group_explicitly_left(context, chat.grpid).unwrap();
|
||||
@@ -1616,7 +1609,7 @@ pub unsafe fn set_chat_name(
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
if chat.is_promoted() {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
StockMessage::MsgGrpName,
|
||||
@@ -1653,62 +1646,83 @@ pub unsafe fn set_chat_name(
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn set_chat_profile_image(
|
||||
pub unsafe fn set_chat_profile_image(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
new_image: impl AsRef<str>, // XXX use PathBuf
|
||||
new_image: impl AsRef<str>,
|
||||
) -> Result<(), Error> {
|
||||
ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat ID");
|
||||
|
||||
let mut OK_TO_CONTINUE = true;
|
||||
let mut success = false;
|
||||
|
||||
let mut chat = Chat::load_from_db(context, chat_id)?;
|
||||
let mut msg = dc_msg_new_untyped(context);
|
||||
let mut new_image_rel = None;
|
||||
|
||||
if real_group_exists(context, chat_id) {
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if !(is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF) == 1i32) {
|
||||
if !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) {
|
||||
log_event!(
|
||||
context,
|
||||
Event::ERROR_SELF_NOT_IN_GROUP,
|
||||
0,
|
||||
"Cannot set chat profile image; self not in group.",
|
||||
);
|
||||
bail!("Failed to set profile image");
|
||||
}
|
||||
let mut new_image_rel: String;
|
||||
if !new_image.as_ref().is_empty() {
|
||||
new_image_rel = new_image.as_ref().to_string();
|
||||
if !dc_make_rel_and_copy(context, &mut new_image_rel) {
|
||||
bail!("Failed to get relative path for profile image");
|
||||
}
|
||||
} else {
|
||||
new_image_rel = "".to_string();
|
||||
}
|
||||
|
||||
chat.param.set(Param::ProfileImage, &new_image_rel);
|
||||
if chat.update_param().is_ok() {
|
||||
if chat.is_promoted() {
|
||||
let mut msg = unsafe { dc_msg_new_untyped(context) };
|
||||
msg.param.set_int(Param::Cmd, DC_CMD_GROUPIMAGE_CHANGED);
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
if new_image_rel == "" {
|
||||
msg.param.remove(Param::Arg);
|
||||
StockMessage::MsgGrpImgDeleted
|
||||
} else {
|
||||
msg.param.set(Param::Arg, &new_image_rel);
|
||||
StockMessage::MsgGrpImgChanged
|
||||
},
|
||||
"",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF,
|
||||
));
|
||||
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
emit_event!(context, Event::MSGS_CHANGED, chat_id, msg.id);
|
||||
/* we should respect this - whatever we send to the group, it gets discarded anyway! */
|
||||
if !new_image.as_ref().is_empty() {
|
||||
let mut img = new_image.as_ref().to_string();
|
||||
if !dc_make_rel_and_copy(context, &mut img) {
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
new_image_rel = Some(img);
|
||||
} else {
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
}
|
||||
if OK_TO_CONTINUE {
|
||||
if let Some(ref new_image_rel) = new_image_rel {
|
||||
chat.param.set(Param::ProfileImage, new_image_rel);
|
||||
}
|
||||
if chat.update_param().is_ok() {
|
||||
if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 {
|
||||
msg.param.set_int(Param::Cmd, 3);
|
||||
if let Some(ref new_image_rel) = new_image_rel {
|
||||
msg.param.set(Param::Arg, new_image_rel);
|
||||
}
|
||||
msg.type_0 = Viewtype::Text;
|
||||
msg.text = Some(context.stock_system_msg(
|
||||
if new_image_rel.is_some() {
|
||||
StockMessage::MsgGrpImgChanged
|
||||
} else {
|
||||
StockMessage::MsgGrpImgDeleted
|
||||
},
|
||||
"",
|
||||
"",
|
||||
DC_CONTACT_ID_SELF,
|
||||
));
|
||||
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
|
||||
context.call_cb(
|
||||
Event::MSGS_CHANGED,
|
||||
chat_id as uintptr_t,
|
||||
msg.id as uintptr_t,
|
||||
);
|
||||
}
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
success = true;
|
||||
}
|
||||
emit_event!(context, Event::CHAT_MODIFIED, chat_id, 0);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
bail!("Failed to set profile image");
|
||||
|
||||
if !success {
|
||||
bail!("Failed to set profile image");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn forward_msgs(
|
||||
|
||||
@@ -77,7 +77,7 @@ pub const DC_CHAT_ID_TRASH: u32 = 3;
|
||||
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
|
||||
const DC_CHAT_ID_MSGS_IN_CREATION: u32 = 4;
|
||||
/// virtual chat showing all messages flagged with msgs.starred=2
|
||||
pub const DC_CHAT_ID_STARRED: u32 = 5;
|
||||
const DC_CHAT_ID_STARRED: u32 = 5;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: u32 = 6;
|
||||
/// only an indicator in a chatlist
|
||||
@@ -557,6 +557,6 @@ pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
|
||||
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
|
||||
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
|
||||
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
|
||||
pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
|
||||
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use deltachat_derive::*;
|
||||
use itertools::Itertools;
|
||||
use rusqlite;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::config::Config;
|
||||
@@ -767,11 +766,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) -> Option<String> {
|
||||
if self.id == DC_CONTACT_ID_SELF {
|
||||
if let Some(p) = self.context.get_config(Config::Selfavatar) {
|
||||
return Some(PathBuf::from(p));
|
||||
}
|
||||
return self.context.get_config(Config::Selfavatar);
|
||||
}
|
||||
// TODO: else get image_abs from contact param
|
||||
None
|
||||
|
||||
@@ -211,8 +211,12 @@ pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<Strin
|
||||
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."))?
|
||||
dc_pgp_symm_encrypt(
|
||||
passphrase_c.as_ptr(),
|
||||
private_key_asc_c.as_ptr() as *const libc::c_void,
|
||||
private_key_asc_c.as_bytes().len(),
|
||||
)
|
||||
.ok_or(format_err!("Failed to encrypt private key."))?
|
||||
};
|
||||
let replacement = format!(
|
||||
concat!(
|
||||
@@ -445,10 +449,9 @@ pub unsafe fn dc_decrypt_setup_file(
|
||||
|| binary_bytes == 0)
|
||||
{
|
||||
/* decrypt symmetrically */
|
||||
if let Some(plain) = dc_pgp_symm_decrypt(
|
||||
passphrase,
|
||||
std::slice::from_raw_parts(binary as *const u8, binary_bytes),
|
||||
) {
|
||||
if let Some(plain) =
|
||||
dc_pgp_symm_decrypt(passphrase, binary as *const libc::c_void, binary_bytes)
|
||||
{
|
||||
let payload_c = CString::new(plain).unwrap();
|
||||
payload = strdup(payload_c.as_ptr());
|
||||
}
|
||||
|
||||
@@ -548,7 +548,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
dc_encode_header_words(name.as_ptr()),
|
||||
),
|
||||
);
|
||||
if command == DC_CMD_MEMBER_REMOVED_FROM_GROUP {
|
||||
if command == 5 {
|
||||
let email_to_remove = factory
|
||||
.msg
|
||||
.param
|
||||
@@ -567,7 +567,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if command == DC_CMD_MEMBER_ADDED_TO_GROUP {
|
||||
} else if command == 4 {
|
||||
let msg = &factory.msg;
|
||||
do_gossip = 1;
|
||||
let email_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup();
|
||||
@@ -599,7 +599,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if command == DC_CMD_GROUPNAME_CHANGED {
|
||||
} else if command == 2 {
|
||||
let msg = &factory.msg;
|
||||
|
||||
let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup();
|
||||
@@ -612,7 +612,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
value_to_add,
|
||||
),
|
||||
);
|
||||
} else if command == DC_CMD_GROUPIMAGE_CHANGED {
|
||||
} else if command == 3 {
|
||||
let msg = &factory.msg;
|
||||
grpimage = msg.param.get(Param::Arg);
|
||||
if grpimage.is_none() {
|
||||
@@ -626,7 +626,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
}
|
||||
}
|
||||
}
|
||||
if command == DC_CMD_LOCATION_STREAMING_ENABLED {
|
||||
if command == 8 {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -637,7 +637,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
),
|
||||
);
|
||||
}
|
||||
if command == DC_CMD_AUTOCRYPT_SETUP_MESSAGE {
|
||||
if command == 6 {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -650,7 +650,7 @@ pub unsafe fn dc_mimefactory_render(factory: &mut dc_mimefactory_t) -> libc::c_i
|
||||
.stock_str(StockMessage::AcSetupMsgBody)
|
||||
.strdup();
|
||||
}
|
||||
if command == DC_CMD_SECUREJOIN_MESSAGE {
|
||||
if command == 7 {
|
||||
let msg = &factory.msg;
|
||||
let step = msg.param.get(Param::Arg).unwrap_or_default().strdup();
|
||||
if strlen(step) > 0 {
|
||||
@@ -726,7 +726,6 @@ 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);
|
||||
meta.type_0 = Viewtype::Image;
|
||||
meta.param.set(Param::File, grpimage);
|
||||
@@ -1084,7 +1083,7 @@ unsafe fn get_subject(
|
||||
} else {
|
||||
b"\x00" as *const u8 as *const libc::c_char
|
||||
};
|
||||
if msg.param.get_int(Param::Cmd).unwrap_or_default() == DC_CMD_AUTOCRYPT_SETUP_MESSAGE {
|
||||
if msg.param.get_int(Param::Cmd).unwrap_or_default() == 6 {
|
||||
ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup()
|
||||
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
ret = format!(
|
||||
|
||||
@@ -923,7 +923,7 @@ unsafe fn handle_reports(
|
||||
}
|
||||
}
|
||||
|
||||
fn save_locations(
|
||||
unsafe fn save_locations(
|
||||
context: &Context,
|
||||
mime_parser: &dc_mimeparser_t,
|
||||
chat_id: u32,
|
||||
@@ -1049,7 +1049,7 @@ unsafe fn create_or_lookup_group(
|
||||
// pointer somewhere into mime_parser, must not be freed
|
||||
let mut X_MrAddToGrp = std::ptr::null_mut();
|
||||
let mut X_MrGrpNameChanged = 0;
|
||||
let mut X_MrGrpImageChanged = "".to_string();
|
||||
let mut X_MrGrpImageChanged = std::ptr::null();
|
||||
let mut better_msg: String = From::from("");
|
||||
let mut failure_reason = std::ptr::null_mut();
|
||||
|
||||
@@ -1078,6 +1078,7 @@ unsafe fn create_or_lookup_group(
|
||||
}
|
||||
set_better_msg(mime_parser, &better_msg);
|
||||
|
||||
// search the grpid in the header
|
||||
let optional_field = dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-ID");
|
||||
if !optional_field.is_null() {
|
||||
grpid = to_string((*optional_field).fld_value)
|
||||
@@ -1172,8 +1173,7 @@ unsafe fn create_or_lookup_group(
|
||||
let optional_field =
|
||||
dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Image");
|
||||
if !optional_field.is_null() {
|
||||
// fld_value is a pointer somewhere into mime_parser, must not be freed
|
||||
X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string();
|
||||
X_MrGrpImageChanged = (*optional_field).fld_value
|
||||
}
|
||||
better_msg = context.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
@@ -1197,11 +1197,14 @@ unsafe fn create_or_lookup_group(
|
||||
let optional_field =
|
||||
dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Image");
|
||||
if !optional_field.is_null() {
|
||||
// fld_value is a pointer somewhere into mime_parser, must not be freed
|
||||
X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string();
|
||||
X_MrGrpImageChanged = (*optional_field).fld_value;
|
||||
mime_parser.is_system_message = DC_CMD_GROUPIMAGE_CHANGED;
|
||||
better_msg = context.stock_system_msg(
|
||||
if X_MrGrpImageChanged == "0" {
|
||||
if strcmp(
|
||||
X_MrGrpImageChanged,
|
||||
b"0\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
StockMessage::MsgGrpImgDeleted
|
||||
} else {
|
||||
StockMessage::MsgGrpImgChanged
|
||||
@@ -1316,7 +1319,6 @@ 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);
|
||||
if sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
@@ -1328,39 +1330,49 @@ unsafe fn create_or_lookup_group(
|
||||
context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0);
|
||||
}
|
||||
}
|
||||
if !X_MrGrpImageChanged.is_empty() {
|
||||
info!(
|
||||
context,
|
||||
0, "grp-image-change {} chat {}", X_MrGrpImageChanged, chat_id
|
||||
);
|
||||
let mut changed = false;
|
||||
let mut grpimage = "".to_string();
|
||||
if X_MrGrpImageChanged == "0" {
|
||||
changed = true;
|
||||
if !X_MrGrpImageChanged.is_null() {
|
||||
let mut ok = 0;
|
||||
let mut grpimage = ptr::null_mut();
|
||||
if strcmp(
|
||||
X_MrGrpImageChanged,
|
||||
b"0\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
ok = 1
|
||||
} else {
|
||||
for part in &mut mime_parser.parts {
|
||||
if part.type_0 == Viewtype::Image {
|
||||
grpimage = part
|
||||
.param
|
||||
.get(Param::File)
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|| "".to_string());
|
||||
info!(context, 0, "found image {:?}", grpimage);
|
||||
changed = true;
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
ok = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
info!(context, 0, "New group image set to '{}'.", grpimage);
|
||||
if 0 != ok {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"New group image set to {}.",
|
||||
if !grpimage.is_null() {
|
||||
"DELETED".to_string()
|
||||
} else {
|
||||
to_string(grpimage)
|
||||
},
|
||||
);
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
|
||||
if grpimage.is_empty() {
|
||||
if grpimage.is_null() {
|
||||
chat.param.remove(Param::ProfileImage);
|
||||
} else {
|
||||
chat.param.set(Param::ProfileImage, grpimage);
|
||||
chat.param.set(Param::ProfileImage, as_str(grpimage));
|
||||
}
|
||||
chat.update_param().unwrap();
|
||||
send_EVENT_CHAT_MODIFIED = 1;
|
||||
}
|
||||
|
||||
free(grpimage as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1669,7 +1681,10 @@ fn hex_hash(s: impl AsRef<str>) -> String {
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec<u32>) -> Vec<u32> {
|
||||
unsafe fn search_chat_ids_by_contact_ids(
|
||||
context: &Context,
|
||||
unsorted_contact_ids: &Vec<u32>,
|
||||
) -> Vec<u32> {
|
||||
/* searches chat_id's by the given contact IDs, may return zero, one or more chat_id's */
|
||||
let mut contact_ids = Vec::with_capacity(23);
|
||||
let mut chat_ids = Vec::with_capacity(23);
|
||||
@@ -1686,13 +1701,7 @@ fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec<
|
||||
let contact_ids_str = join(contact_ids.iter().map(|x| x.to_string()), ",");
|
||||
context.sql.query_map(
|
||||
format!(
|
||||
"SELECT DISTINCT cc.chat_id, cc.contact_id \
|
||||
FROM chats_contacts cc \
|
||||
LEFT JOIN chats c ON c.id=cc.chat_id \
|
||||
WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN({})) \
|
||||
AND c.type=120 \
|
||||
AND cc.contact_id!=1 \
|
||||
ORDER BY cc.chat_id, cc.contact_id;",
|
||||
"SELECT DISTINCT cc.chat_id, cc.contact_id FROM chats_contacts cc LEFT JOIN chats c ON c.id=cc.chat_id WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN({})) AND c.type=120 AND cc.contact_id!=1 ORDER BY cc.chat_id, cc.contact_id;",
|
||||
contact_ids_str
|
||||
),
|
||||
params![],
|
||||
@@ -1704,15 +1713,15 @@ fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec<
|
||||
|
||||
for row in rows {
|
||||
let (chat_id, contact_id) = row?;
|
||||
if chat_id != last_chat_id {
|
||||
if chat_id as u32 != last_chat_id {
|
||||
if matches == contact_ids.len() && mismatches == 0 {
|
||||
chat_ids.push(last_chat_id);
|
||||
}
|
||||
last_chat_id = chat_id;
|
||||
last_chat_id = chat_id as u32;
|
||||
matches = 0;
|
||||
mismatches = 0;
|
||||
}
|
||||
if matches < contact_ids.len() && contact_id == contact_ids[matches] {
|
||||
if contact_id == contact_ids[matches] {
|
||||
matches += 1;
|
||||
} else {
|
||||
mismatches += 1;
|
||||
|
||||
@@ -299,10 +299,8 @@ impl E2eeHelper {
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
if let Some(ctext_v) = dc_pgp_pk_encrypt(
|
||||
std::slice::from_raw_parts(
|
||||
(*plain).str_0 as *const u8,
|
||||
(*plain).len,
|
||||
),
|
||||
(*plain).str_0 as *const libc::c_void,
|
||||
(*plain).len,
|
||||
&keyring,
|
||||
sign_key.as_ref(),
|
||||
) {
|
||||
@@ -909,7 +907,8 @@ unsafe fn decrypt_part(
|
||||
|
||||
/*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */
|
||||
if let Some(plain) = dc_pgp_pk_decrypt(
|
||||
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes),
|
||||
decoded_data as *const libc::c_void,
|
||||
decoded_data_bytes,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
add_signatures,
|
||||
|
||||
35
src/pgp.rs
35
src/pgp.rs
@@ -188,11 +188,15 @@ pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
|
||||
}
|
||||
|
||||
pub fn dc_pgp_pk_encrypt(
|
||||
plain: &[u8],
|
||||
plain_text: *const libc::c_void,
|
||||
plain_bytes: size_t,
|
||||
public_keys_for_encryption: &Keyring,
|
||||
private_key_for_signing: Option<&Key>,
|
||||
) -> Option<String> {
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
assert!(!plain_text.is_null() && !plain_bytes > 0, "invalid input");
|
||||
|
||||
let bytes = unsafe { std::slice::from_raw_parts(plain_text as *const u8, plain_bytes) };
|
||||
let lit_msg = Message::new_literal_bytes("", bytes);
|
||||
let pkeys: Vec<&SignedPublicKey> = public_keys_for_encryption
|
||||
.keys()
|
||||
.into_iter()
|
||||
@@ -223,11 +227,16 @@ pub fn dc_pgp_pk_encrypt(
|
||||
}
|
||||
|
||||
pub fn dc_pgp_pk_decrypt(
|
||||
ctext: &[u8],
|
||||
ctext: *const libc::c_void,
|
||||
ctext_bytes: size_t,
|
||||
private_keys_for_decryption: &Keyring,
|
||||
public_keys_for_validation: &Keyring,
|
||||
ret_signature_fingerprints: Option<&mut HashSet<String>>,
|
||||
) -> Option<Vec<u8>> {
|
||||
assert!(!ctext.is_null() && ctext_bytes > 0, "invalid input");
|
||||
|
||||
let ctext = unsafe { std::slice::from_raw_parts(ctext as *const u8, ctext_bytes) };
|
||||
|
||||
// TODO: proper error handling
|
||||
if let Ok((msg, _)) = Message::from_armor_single(Cursor::new(ctext)) {
|
||||
let skeys: Vec<&SignedSecretKey> = private_keys_for_decryption
|
||||
@@ -274,13 +283,19 @@ pub fn dc_pgp_pk_decrypt(
|
||||
}
|
||||
|
||||
/// Symmetric encryption.
|
||||
pub fn dc_pgp_symm_encrypt(passphrase: *const libc::c_char, plain: &[u8]) -> Option<String> {
|
||||
pub fn dc_pgp_symm_encrypt(
|
||||
passphrase: *const libc::c_char,
|
||||
plain: *const libc::c_void,
|
||||
plain_bytes: size_t,
|
||||
) -> Option<String> {
|
||||
assert!(!passphrase.is_null(), "invalid passphrase");
|
||||
assert!(!plain.is_null() && !plain_bytes > 0, "invalid input");
|
||||
|
||||
let pw = unsafe { CStr::from_ptr(passphrase).to_str().unwrap() };
|
||||
let bytes = unsafe { std::slice::from_raw_parts(plain as *const u8, plain_bytes) };
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let lit_msg = Message::new_literal_bytes("", plain);
|
||||
let lit_msg = Message::new_literal_bytes("", bytes);
|
||||
|
||||
let s2k = StringToKey::new_default(&mut rng);
|
||||
let msg = lit_msg.encrypt_with_password(&mut rng, s2k, Default::default(), || pw.into());
|
||||
@@ -289,12 +304,18 @@ pub fn dc_pgp_symm_encrypt(passphrase: *const libc::c_char, plain: &[u8]) -> Opt
|
||||
}
|
||||
|
||||
/// Symmetric decryption.
|
||||
pub fn dc_pgp_symm_decrypt(passphrase: *const libc::c_char, ctext: &[u8]) -> Option<Vec<u8>> {
|
||||
pub fn dc_pgp_symm_decrypt(
|
||||
passphrase: *const libc::c_char,
|
||||
ctext: *const libc::c_void,
|
||||
ctext_bytes: size_t,
|
||||
) -> Option<Vec<u8>> {
|
||||
assert!(!passphrase.is_null(), "invalid passphrase");
|
||||
assert!(!ctext.is_null() && !ctext_bytes > 0, "invalid input");
|
||||
|
||||
let pw = unsafe { CStr::from_ptr(passphrase).to_str().unwrap() };
|
||||
let bytes = unsafe { std::slice::from_raw_parts(ctext as *const u8, ctext_bytes) };
|
||||
|
||||
let enc_msg = Message::from_bytes(Cursor::new(ctext));
|
||||
let enc_msg = Message::from_bytes(Cursor::new(bytes));
|
||||
|
||||
enc_msg
|
||||
.and_then(|msg| {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Stress some functions for testing; if used as a lib, this file is obsolete.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use tempfile::{tempdir, TempDir};
|
||||
@@ -493,18 +494,38 @@ fn test_encryption_decryption() {
|
||||
|
||||
assert_ne!(public_key, public_key2);
|
||||
|
||||
let original_text = b"This is a test";
|
||||
let original_text: *const libc::c_char =
|
||||
b"This is a test\x00" as *const u8 as *const libc::c_char;
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(public_key.clone());
|
||||
keyring.add_ref(&public_key2);
|
||||
|
||||
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 ctext = dc_pgp_pk_encrypt(
|
||||
original_text as *const libc::c_void,
|
||||
strlen(original_text),
|
||||
&keyring,
|
||||
Some(&private_key),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
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-----"));
|
||||
assert!(!ctext.is_empty());
|
||||
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let ctext_signed_bytes = ctext.len();
|
||||
let ctext_signed = CString::yolo(ctext);
|
||||
|
||||
let ctext = dc_pgp_pk_encrypt(
|
||||
original_text as *const libc::c_void,
|
||||
strlen(original_text),
|
||||
&keyring,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!ctext.is_empty());
|
||||
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let ctext_unsigned_bytes = ctext.len();
|
||||
let ctext_unsigned = CString::yolo(ctext);
|
||||
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(private_key);
|
||||
@@ -518,39 +539,42 @@ fn test_encryption_decryption() {
|
||||
let mut valid_signatures: HashSet<String> = Default::default();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plain, original_text,);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
|
||||
valid_signatures.clear();
|
||||
|
||||
let empty_keyring = Keyring::default();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&empty_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
assert_eq!(valid_signatures.len(), 0);
|
||||
|
||||
valid_signatures.clear();
|
||||
@@ -558,26 +582,28 @@ fn test_encryption_decryption() {
|
||||
public_keyring2.add_ref(&public_key);
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_bytes(),
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
assert_eq!(valid_signatures.len(), 1);
|
||||
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_unsigned.as_bytes(),
|
||||
ctext_unsigned.as_ptr() as *const _,
|
||||
ctext_unsigned_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
|
||||
valid_signatures.clear();
|
||||
|
||||
@@ -586,10 +612,16 @@ fn test_encryption_decryption() {
|
||||
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();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(plain, original_text);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user