mirror of
https://github.com/chatmail/core.git
synced 2026-04-03 05:52:10 +03:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ba25f9f77 | ||
|
|
e23704486a | ||
|
|
feee340f4d | ||
|
|
a5cde0d137 | ||
|
|
b08a2b4d2c | ||
|
|
3b6e1b0aae | ||
|
|
ca76cac314 | ||
|
|
3a16ad89bd | ||
|
|
fb9369f333 |
@@ -1528,9 +1528,17 @@ pub unsafe extern "C" fn dc_imex(
|
||||
eprintln!("ignoring careless call to dc_imex()");
|
||||
return;
|
||||
}
|
||||
let what = match imex::ImexMode::from_i32(what as i32) {
|
||||
Some(what) => what,
|
||||
None => {
|
||||
eprintln!("ignoring invalid argument {} to dc_imex", what);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| dc_imex::dc_imex(ctx, what, as_opt_str(param1)))
|
||||
.with_inner(|ctx| imex::imex(ctx, what, as_opt_str(param1)))
|
||||
.ok();
|
||||
}
|
||||
|
||||
@@ -1545,7 +1553,7 @@ pub unsafe extern "C" fn dc_imex_has_backup(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| match dc_imex::dc_imex_has_backup(ctx, as_str(dir)) {
|
||||
.with_inner(|ctx| match imex::has_backup(ctx, as_str(dir)) {
|
||||
Ok(res) => res.strdup(),
|
||||
Err(err) => {
|
||||
error!(ctx, "dc_imex_has_backup: {}", err);
|
||||
@@ -1563,7 +1571,7 @@ pub unsafe extern "C" fn dc_initiate_key_transfer(context: *mut dc_context_t) ->
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| match dc_imex::dc_initiate_key_transfer(ctx) {
|
||||
.with_inner(|ctx| match imex::initiate_key_transfer(ctx) {
|
||||
Ok(res) => res.strdup(),
|
||||
Err(err) => {
|
||||
error!(ctx, "dc_initiate_key_transfer(): {}", err);
|
||||
@@ -1588,15 +1596,15 @@ pub unsafe extern "C" fn dc_continue_key_transfer(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
match dc_imex::dc_continue_key_transfer(ctx, msg_id, as_str(setup_code)) {
|
||||
.with_inner(
|
||||
|ctx| match imex::continue_key_transfer(ctx, msg_id, as_str(setup_code)) {
|
||||
Ok(()) => 1,
|
||||
Err(err) => {
|
||||
error!(ctx, "dc_continue_key_transfer: {}", err);
|
||||
0
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ use deltachat::configure::*;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_receive_imf::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::error::Error;
|
||||
use deltachat::imex::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::location;
|
||||
use deltachat::lot::LotState;
|
||||
@@ -102,7 +102,7 @@ fn dc_poke_eml_file(context: &Context, filename: impl AsRef<Path>) -> Result<(),
|
||||
|
||||
/// Import a file to the database.
|
||||
/// For testing, import a folder with eml-files, a single eml-file, e-mail plus public key and so on.
|
||||
/// For normal importing, use dc_imex().
|
||||
/// For normal importing, use imex().
|
||||
///
|
||||
/// @private @memberof Context
|
||||
/// @param context The context as created by dc_context_new().
|
||||
@@ -405,7 +405,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
============================================="
|
||||
),
|
||||
},
|
||||
"initiate-key-transfer" => match dc_initiate_key_transfer(context) {
|
||||
"initiate-key-transfer" => match initiate_key_transfer(context) {
|
||||
Ok(setup_code) => println!(
|
||||
"Setup code for the transferred setup message: {}",
|
||||
setup_code,
|
||||
@@ -432,28 +432,28 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
!arg1.is_empty() && !arg2.is_empty(),
|
||||
"Arguments <msg-id> <setup-code> expected"
|
||||
);
|
||||
dc_continue_key_transfer(context, arg1.parse()?, &arg2)?;
|
||||
continue_key_transfer(context, arg1.parse()?, &arg2)?;
|
||||
}
|
||||
"has-backup" => {
|
||||
dc_imex_has_backup(context, blobdir)?;
|
||||
has_backup(context, blobdir)?;
|
||||
}
|
||||
"export-backup" => {
|
||||
dc_imex(context, 11, Some(blobdir));
|
||||
imex(context, ImexMode::ExportBackup, Some(blobdir));
|
||||
}
|
||||
"import-backup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
|
||||
dc_imex(context, 12, Some(arg1));
|
||||
imex(context, ImexMode::ImportBackup, Some(arg1));
|
||||
}
|
||||
"export-keys" => {
|
||||
dc_imex(context, 1, Some(blobdir));
|
||||
imex(context, ImexMode::ExportSelfKeys, Some(blobdir));
|
||||
}
|
||||
"import-keys" => {
|
||||
dc_imex(context, 2, Some(blobdir));
|
||||
imex(context, ImexMode::ImportSelfKeys, Some(blobdir));
|
||||
}
|
||||
"export-setup" => {
|
||||
let setup_code = dc_create_setup_code(context);
|
||||
let setup_code = create_setup_code(context);
|
||||
let file_name = blobdir.join("autocrypt-setup-message.html");
|
||||
let file_content = dc_render_setup_file(context, &setup_code)?;
|
||||
let file_content = render_setup_file(context, &setup_code)?;
|
||||
std::fs::write(&file_name, file_content)?;
|
||||
println!(
|
||||
"Setup message written to: {}\nSetup code: {}",
|
||||
|
||||
@@ -888,6 +888,7 @@ pub unsafe fn mailimf_fields_new(mut fld_list: *mut clist) -> *mut mailimf_field
|
||||
(*fields).fld_list = fld_list;
|
||||
return fields;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn mailimf_field_new(
|
||||
mut fld_type: libc::c_int,
|
||||
@@ -947,6 +948,20 @@ pub unsafe fn mailimf_field_new(
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn mailimf_field_new_subject(fld_subject: *mut mailimf_subject) -> *mut mailimf_field {
|
||||
let mut field: *mut mailimf_field = 0 as *mut mailimf_field;
|
||||
field = malloc(::std::mem::size_of::<mailimf_field>() as libc::size_t) as *mut mailimf_field;
|
||||
if field.is_null() {
|
||||
return 0 as *mut mailimf_field;
|
||||
}
|
||||
(*field).fld_type = MAILIMF_FIELD_SUBJECT as libc::c_int;
|
||||
(*field).fld_data.fld_subject = fld_subject;
|
||||
|
||||
field
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn mailimf_orig_date_new(
|
||||
mut dt_date_time: *mut mailimf_date_time,
|
||||
|
||||
@@ -708,6 +708,28 @@ pub unsafe fn mailmime_new(
|
||||
return mime;
|
||||
}
|
||||
|
||||
pub unsafe fn mailmime_new_simple(
|
||||
mut mm_type: libc::c_int,
|
||||
mut mm_mime_fields: *mut mailmime_fields,
|
||||
mut mm_content_type: *mut mailmime_content,
|
||||
mut mm_fields: *mut mailimf_fields,
|
||||
mut mm_msg_mime: *mut Mailmime,
|
||||
) -> *mut Mailmime {
|
||||
mailmime_new(
|
||||
mm_type,
|
||||
std::ptr::null(),
|
||||
0,
|
||||
mm_mime_fields,
|
||||
mm_content_type,
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
mm_fields,
|
||||
mm_msg_mime,
|
||||
)
|
||||
}
|
||||
|
||||
pub unsafe fn mailmime_free(mut mime: *mut Mailmime) {
|
||||
match (*mime).mm_type {
|
||||
1 => {
|
||||
|
||||
@@ -64,15 +64,6 @@ pub const DC_GCL_ADD_SELF: usize = 0x02;
|
||||
pub(crate) const DC_FP_NO_AUTOCRYPT_HEADER: i32 = 2;
|
||||
pub(crate) const DC_FP_ADD_AUTOCRYPT_HEADER: i32 = 1;
|
||||
|
||||
/// param1 is a directory where the keys are written to
|
||||
pub const DC_IMEX_EXPORT_SELF_KEYS: i32 = 1;
|
||||
/// param1 is a directory where the keys are searched in and read from
|
||||
pub const DC_IMEX_IMPORT_SELF_KEYS: i32 = 2;
|
||||
/// param1 is a directory where the backup is written to
|
||||
pub const DC_IMEX_EXPORT_BACKUP: i32 = 11;
|
||||
/// param1 is the file with the backup to import
|
||||
pub const DC_IMEX_IMPORT_BACKUP: i32 = 12;
|
||||
|
||||
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
|
||||
pub(crate) const DC_CHAT_ID_DEADDROP: u32 = 1;
|
||||
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
|
||||
|
||||
@@ -5,7 +5,6 @@ use std::ptr;
|
||||
use charset::Charset;
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use libc::{strcmp, strlen, strncmp};
|
||||
use mmime::clist::*;
|
||||
use mmime::mailimf::types::*;
|
||||
use mmime::mailimf::*;
|
||||
use mmime::mailmime::content::*;
|
||||
@@ -21,7 +20,7 @@ use crate::context::Context;
|
||||
use crate::dc_simplify::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee::*;
|
||||
use crate::e2ee;
|
||||
use crate::error::Error;
|
||||
use crate::location;
|
||||
use crate::param::*;
|
||||
@@ -38,7 +37,9 @@ pub struct MimeParser<'a> {
|
||||
pub subject: Option<String>,
|
||||
pub is_send_by_messenger: bool,
|
||||
pub decrypting_failed: bool,
|
||||
pub e2ee_helper: E2eeHelper,
|
||||
pub encrypted: bool,
|
||||
pub signatures: HashSet<String>,
|
||||
pub gossipped_addr: HashSet<String>,
|
||||
pub is_forwarded: bool,
|
||||
pub reports: Vec<*mut Mailmime>,
|
||||
pub is_system_message: SystemMessage,
|
||||
@@ -92,7 +93,9 @@ impl<'a> MimeParser<'a> {
|
||||
subject: None,
|
||||
is_send_by_messenger: false,
|
||||
decrypting_failed: false,
|
||||
e2ee_helper: Default::default(),
|
||||
encrypted: false,
|
||||
signatures: Default::default(),
|
||||
gossipped_addr: Default::default(),
|
||||
is_forwarded: false,
|
||||
context,
|
||||
reports: Vec::new(),
|
||||
@@ -113,7 +116,11 @@ impl<'a> MimeParser<'a> {
|
||||
);
|
||||
|
||||
if r == MAILIMF_NO_ERROR as libc::c_int && !self.mimeroot.is_null() {
|
||||
self.e2ee_helper.try_decrypt(self.context, self.mimeroot)?;
|
||||
let (encrypted, signatures, gossipped_addr) =
|
||||
e2ee::try_decrypt(self.context, self.mimeroot)?;
|
||||
self.encrypted = encrypted;
|
||||
self.signatures = signatures;
|
||||
self.gossipped_addr = gossipped_addr;
|
||||
self.parse_mime_recursive(self.mimeroot);
|
||||
|
||||
if let Some(field) = self.lookup_field("Subject") {
|
||||
@@ -794,9 +801,9 @@ impl<'a> MimeParser<'a> {
|
||||
}
|
||||
|
||||
fn do_add_single_part(&mut self, mut part: Part) {
|
||||
if self.e2ee_helper.encrypted && self.e2ee_helper.signatures.len() > 0 {
|
||||
if self.encrypted && self.signatures.len() > 0 {
|
||||
part.param.set_int(Param::GuranteeE2ee, 1);
|
||||
} else if self.e2ee_helper.encrypted {
|
||||
} else if self.encrypted {
|
||||
part.param.set_int(Param::ErroneousE2ee, 0x2);
|
||||
}
|
||||
self.parts.push(part);
|
||||
@@ -1204,50 +1211,61 @@ pub unsafe fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result<Vec<u8>, E
|
||||
Err(format_err!("Failed to to decode"))
|
||||
}
|
||||
|
||||
pub unsafe fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> {
|
||||
pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> {
|
||||
/* returned addresses are normalized. */
|
||||
let mut recipients: HashSet<String> = Default::default();
|
||||
|
||||
for cur in (*(*imffields).fld_list).into_iter() {
|
||||
for cur in unsafe { (*(*imffields).fld_list).into_iter() } {
|
||||
let fld = cur as *mut mailimf_field;
|
||||
|
||||
let fld_to: *mut mailimf_to;
|
||||
let fld_cc: *mut mailimf_cc;
|
||||
|
||||
let mut addr_list: *mut mailimf_address_list = ptr::null_mut();
|
||||
if fld.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let fld = unsafe { *fld };
|
||||
|
||||
// TODO match on enums /rtn
|
||||
match (*fld).fld_type {
|
||||
match fld.fld_type {
|
||||
13 => {
|
||||
fld_to = (*fld).fld_data.fld_to;
|
||||
fld_to = unsafe { fld.fld_data.fld_to };
|
||||
if !fld_to.is_null() {
|
||||
addr_list = (*fld_to).to_addr_list
|
||||
addr_list = unsafe { (*fld_to).to_addr_list };
|
||||
}
|
||||
}
|
||||
14 => {
|
||||
fld_cc = (*fld).fld_data.fld_cc;
|
||||
fld_cc = unsafe { fld.fld_data.fld_cc };
|
||||
if !fld_cc.is_null() {
|
||||
addr_list = (*fld_cc).cc_addr_list
|
||||
addr_list = unsafe { (*fld_cc).cc_addr_list };
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if !addr_list.is_null() {
|
||||
for cur2 in (*(*addr_list).ad_list).into_iter() {
|
||||
for cur2 in unsafe { &(*(*addr_list).ad_list) } {
|
||||
let adr = cur2 as *mut mailimf_address;
|
||||
|
||||
if !adr.is_null() {
|
||||
if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
||||
mailimf_get_recipients_add_addr(&mut recipients, (*adr).ad_data.ad_mailbox);
|
||||
} else if (*adr).ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int {
|
||||
let group: *mut mailimf_group = (*adr).ad_data.ad_group;
|
||||
if !group.is_null() && !(*group).grp_mb_list.is_null() {
|
||||
for cur3 in (*(*(*group).grp_mb_list).mb_list).into_iter() {
|
||||
mailimf_get_recipients_add_addr(
|
||||
&mut recipients,
|
||||
cur3 as *mut mailimf_mailbox,
|
||||
);
|
||||
}
|
||||
if adr.is_null() {
|
||||
continue;
|
||||
}
|
||||
let adr = unsafe { *adr };
|
||||
|
||||
if adr.ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
|
||||
mailimf_get_recipients_add_addr(&mut recipients, unsafe {
|
||||
adr.ad_data.ad_mailbox
|
||||
});
|
||||
} else if adr.ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int {
|
||||
let group = unsafe { adr.ad_data.ad_group };
|
||||
if !group.is_null() && unsafe { !(*group).grp_mb_list.is_null() } {
|
||||
for cur3 in unsafe { &(*(*(*group).grp_mb_list).mb_list) } {
|
||||
mailimf_get_recipients_add_addr(
|
||||
&mut recipients,
|
||||
cur3 as *mut mailimf_mailbox,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1266,30 +1284,26 @@ fn mailimf_get_recipients_add_addr(recipients: &mut HashSet<String>, mb: *mut ma
|
||||
}
|
||||
|
||||
/*the result is a pointer to mime, must not be freed*/
|
||||
pub unsafe fn mailimf_find_field(
|
||||
pub fn mailimf_find_field(
|
||||
header: *mut mailimf_fields,
|
||||
wanted_fld_type: libc::c_int,
|
||||
) -> *mut mailimf_field {
|
||||
if header.is_null() || (*header).fld_list.is_null() {
|
||||
if header.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let mut cur1: *mut clistiter = (*(*header).fld_list).first;
|
||||
while !cur1.is_null() {
|
||||
let field: *mut mailimf_field = (if !cur1.is_null() {
|
||||
(*cur1).data
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}) as *mut mailimf_field;
|
||||
|
||||
let header = unsafe { (*header) };
|
||||
if header.fld_list.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
for cur in unsafe { &(*header.fld_list) } {
|
||||
let field = cur as *mut mailimf_field;
|
||||
if !field.is_null() {
|
||||
if (*field).fld_type == wanted_fld_type {
|
||||
if unsafe { (*field).fld_type } == wanted_fld_type {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
cur1 = if !cur1.is_null() {
|
||||
(*cur1).next
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
ptr::null_mut()
|
||||
|
||||
@@ -610,7 +610,7 @@ unsafe fn add_parts(
|
||||
let icnt = mime_parser.parts.len();
|
||||
let mut txt_raw = None;
|
||||
|
||||
let is_ok = context
|
||||
context
|
||||
.sql
|
||||
.prepare(
|
||||
"INSERT INTO msgs \
|
||||
@@ -697,13 +697,10 @@ unsafe fn add_parts(
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.is_ok();
|
||||
|
||||
if !is_ok {
|
||||
// i/o error - there is nothing more we can do - in other cases, we try to write at least an empty record
|
||||
cleanup(mime_in_reply_to, mime_references);
|
||||
bail!("Cannot write DB.");
|
||||
}
|
||||
.map_err(|err| {
|
||||
cleanup(mime_in_reply_to, mime_references);
|
||||
err
|
||||
})?;
|
||||
|
||||
info!(
|
||||
context,
|
||||
@@ -1609,10 +1606,7 @@ fn check_verified_properties(
|
||||
) -> Result<()> {
|
||||
let contact = Contact::load_from_db(context, from_id)?;
|
||||
|
||||
ensure!(
|
||||
mimeparser.e2ee_helper.encrypted,
|
||||
"This message is not encrypted."
|
||||
);
|
||||
ensure!(mimeparser.encrypted, "This message is not encrypted.");
|
||||
|
||||
// ensure, the contact is verified
|
||||
// and the message is signed with a verified key of the sender.
|
||||
@@ -1633,7 +1627,7 @@ fn check_verified_properties(
|
||||
|
||||
if let Some(peerstate) = peerstate {
|
||||
ensure!(
|
||||
peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures),
|
||||
peerstate.has_verified_key(&mimeparser.signatures),
|
||||
"The message was sent with non-verified encryption."
|
||||
);
|
||||
}
|
||||
@@ -1660,7 +1654,7 @@ fn check_verified_properties(
|
||||
let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr);
|
||||
|
||||
// mark gossiped keys (if any) as verified
|
||||
if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() {
|
||||
if mimeparser.gossipped_addr.contains(&to_addr) && peerstate.is_some() {
|
||||
let peerstate = peerstate.as_mut().unwrap();
|
||||
|
||||
// if we're here, we know the gossip key is verified:
|
||||
|
||||
@@ -217,26 +217,26 @@ pub(crate) fn dc_str_to_color(s: impl AsRef<str>) -> u32 {
|
||||
|
||||
/* date/time tools */
|
||||
/* the result is UTC or DC_INVALID_TIMESTAMP */
|
||||
pub(crate) unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||
let sec = (*date_time).dt_sec;
|
||||
let min = (*date_time).dt_min;
|
||||
let hour = (*date_time).dt_hour;
|
||||
let day = (*date_time).dt_day;
|
||||
let month = (*date_time).dt_month;
|
||||
let year = (*date_time).dt_year;
|
||||
pub(crate) fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||
assert!(!date_time.is_null());
|
||||
let dt = unsafe { *date_time };
|
||||
|
||||
let sec = dt.dt_sec;
|
||||
let min = dt.dt_min;
|
||||
let hour = dt.dt_hour;
|
||||
let day = dt.dt_day;
|
||||
let month = dt.dt_month;
|
||||
let year = dt.dt_year;
|
||||
|
||||
let ts = chrono::NaiveDateTime::new(
|
||||
chrono::NaiveDate::from_ymd(year, month as u32, day as u32),
|
||||
chrono::NaiveTime::from_hms(hour as u32, min as u32, sec as u32),
|
||||
);
|
||||
|
||||
let (zone_hour, zone_min) = if (*date_time).dt_zone >= 0 {
|
||||
((*date_time).dt_zone / 100, (*date_time).dt_zone % 100)
|
||||
let (zone_hour, zone_min) = if dt.dt_zone >= 0 {
|
||||
(dt.dt_zone / 100, dt.dt_zone % 100)
|
||||
} else {
|
||||
(
|
||||
-(-(*date_time).dt_zone / 100),
|
||||
-(-(*date_time).dt_zone % 100),
|
||||
)
|
||||
(-(-dt.dt_zone / 100), -(-dt.dt_zone % 100))
|
||||
};
|
||||
|
||||
ts.timestamp() - (zone_hour * 3600 + zone_min * 60) as i64
|
||||
|
||||
639
src/e2ee.rs
639
src/e2ee.rs
@@ -1,11 +1,10 @@
|
||||
//! End-to-end encryption support.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use libc::{strcmp, strlen, strncmp};
|
||||
use libc::strlen;
|
||||
use mmime::clist::*;
|
||||
use mmime::mailimf::types::*;
|
||||
use mmime::mailimf::types_helper::*;
|
||||
@@ -18,6 +17,7 @@ use mmime::mailmime::*;
|
||||
use mmime::mailprivacy_prepare_mime;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::config::Config;
|
||||
@@ -47,12 +47,12 @@ pub struct EncryptHelper {
|
||||
|
||||
impl EncryptHelper {
|
||||
pub fn new(context: &Context) -> Result<EncryptHelper> {
|
||||
let e2ee = context.sql.get_config_int(&context, "e2ee_enabled");
|
||||
let prefer_encrypt = if 0 != e2ee.unwrap_or_default() {
|
||||
EncryptPreference::Mutual
|
||||
} else {
|
||||
EncryptPreference::NoPreference
|
||||
};
|
||||
let prefer_encrypt = context
|
||||
.sql
|
||||
.get_config_int(&context, "e2ee_enabled")
|
||||
.and_then(EncryptPreference::from_i32)
|
||||
.unwrap_or_default();
|
||||
|
||||
let addr = match context.get_config(Config::ConfiguredAddr) {
|
||||
None => {
|
||||
bail!("addr not configured!");
|
||||
@@ -61,6 +61,7 @@ impl EncryptHelper {
|
||||
};
|
||||
|
||||
let public_key = load_or_generate_self_public_key(context, &addr)?;
|
||||
|
||||
Ok(EncryptHelper {
|
||||
prefer_encrypt,
|
||||
addr,
|
||||
@@ -83,11 +84,13 @@ impl EncryptHelper {
|
||||
mut in_out_message: *mut Mailmime,
|
||||
imffields_unprotected: *mut mailimf_fields,
|
||||
) -> Result<bool> {
|
||||
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
||||
We just expect the root as being given to this function. */
|
||||
if in_out_message.is_null() || unsafe { !(*in_out_message).mm_parent.is_null() } {
|
||||
bail!("corrupted inputs");
|
||||
}
|
||||
// libEtPan's pgp_encrypt_mime() takes the parent as the new root.
|
||||
// We just expect the root as being given to this function.
|
||||
ensure!(
|
||||
!in_out_message.is_null() && unsafe { (*in_out_message).mm_parent.is_null() },
|
||||
"corrupted inputs"
|
||||
);
|
||||
|
||||
if !(self.prefer_encrypt == EncryptPreference::Mutual || e2ee_guaranteed) {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -106,13 +109,14 @@ impl EncryptHelper {
|
||||
None => {
|
||||
let msg = format!("peerstate for {} missing, cannot encrypt", recipient_addr);
|
||||
if e2ee_guaranteed {
|
||||
bail!("{}", msg);
|
||||
return Err(format_err!("{}", msg));
|
||||
} else {
|
||||
info!(context, "{}", msg);
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if peerstate.prefer_encrypt != EncryptPreference::Mutual && !e2ee_guaranteed {
|
||||
info!(context, "peerstate for {} is no-encrypt", recipient_addr);
|
||||
return Ok(false);
|
||||
@@ -136,30 +140,24 @@ impl EncryptHelper {
|
||||
let sign_key = {
|
||||
keyring.add_ref(&self.public_key);
|
||||
let key = Key::from_self_private(context, self.addr.clone(), &context.sql);
|
||||
if key.is_none() {
|
||||
bail!("no own private key found")
|
||||
}
|
||||
ensure!(key.is_some(), "no own private key found");
|
||||
|
||||
key
|
||||
};
|
||||
|
||||
/* encrypt message */
|
||||
// encrypt message
|
||||
unsafe {
|
||||
mailprivacy_prepare_mime(in_out_message);
|
||||
let mut part_to_encrypt: *mut Mailmime =
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||
let mut part_to_encrypt = (*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||
(*part_to_encrypt).mm_parent = ptr::null_mut();
|
||||
let imffields_encrypted: *mut mailimf_fields = mailimf_fields_new_empty();
|
||||
/* mailmime_new_message_data() calls mailmime_fields_new_with_version() which would add the unwanted MIME-Version:-header */
|
||||
let message_to_encrypt: *mut Mailmime = mailmime_new(
|
||||
let imffields_encrypted = mailimf_fields_new_empty();
|
||||
|
||||
// mailmime_new_message_data() calls mailmime_fields_new_with_version()
|
||||
// which would add the unwanted MIME-Version:-header
|
||||
let message_to_encrypt = mailmime_new_simple(
|
||||
MAILMIME_MESSAGE as libc::c_int,
|
||||
ptr::null(),
|
||||
0 as libc::size_t,
|
||||
mailmime_fields_new_empty(),
|
||||
mailmime_get_content_message(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
imffields_encrypted,
|
||||
part_to_encrypt,
|
||||
);
|
||||
@@ -168,12 +166,13 @@ impl EncryptHelper {
|
||||
wrapmime::new_custom_field(imffields_encrypted, "Autocrypt-Gossip", &header)
|
||||
}
|
||||
|
||||
/* memoryhole headers: move some headers into encrypted part */
|
||||
// memoryhole headers: move some headers into encrypted part
|
||||
// XXX note we can't use clist's into_iter() because the loop body also removes items
|
||||
let mut cur: *mut clistiter = (*(*imffields_unprotected).fld_list).first;
|
||||
let mut cur = (*(*imffields_unprotected).fld_list).first;
|
||||
while !cur.is_null() {
|
||||
let field: *mut mailimf_field = (*cur).data as *mut mailimf_field;
|
||||
let field = (*cur).data as *mut mailimf_field;
|
||||
let mut move_to_encrypted = false;
|
||||
|
||||
if !field.is_null() {
|
||||
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||
move_to_encrypted = true;
|
||||
@@ -189,6 +188,7 @@ impl EncryptHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if move_to_encrypted {
|
||||
mailimf_fields_add(imffields_encrypted, field);
|
||||
cur = clist_delete((*imffields_unprotected).fld_list, cur);
|
||||
@@ -196,48 +196,24 @@ impl EncryptHelper {
|
||||
cur = (*cur).next;
|
||||
}
|
||||
}
|
||||
let subject: *mut mailimf_subject = mailimf_subject_new("...".strdup());
|
||||
mailimf_fields_add(
|
||||
imffields_unprotected,
|
||||
mailimf_field_new(
|
||||
MAILIMF_FIELD_SUBJECT as libc::c_int,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
subject,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
),
|
||||
);
|
||||
|
||||
let subject = mailimf_subject_new("...".strdup());
|
||||
mailimf_fields_add(imffields_unprotected, mailimf_field_new_subject(subject));
|
||||
|
||||
wrapmime::append_ct_param(
|
||||
(*part_to_encrypt).mm_content_type,
|
||||
"protected-headers",
|
||||
"v1",
|
||||
)?;
|
||||
let plain: *mut MMAPString =
|
||||
mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
let mut col: libc::c_int = 0i32;
|
||||
let plain = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
let mut col = 0;
|
||||
mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
||||
mailmime_free(message_to_encrypt);
|
||||
if (*plain).str_0.is_null() || (*plain).len <= 0 {
|
||||
bail!("could not write/allocate");
|
||||
}
|
||||
|
||||
ensure!(
|
||||
!(*plain).str_0.is_null() && (*plain).len > 0,
|
||||
"could not write/allocate"
|
||||
);
|
||||
|
||||
let ctext = dc_pgp_pk_encrypt(
|
||||
std::slice::from_raw_parts((*plain).str_0 as *const u8, (*plain).len),
|
||||
@@ -246,165 +222,148 @@ impl EncryptHelper {
|
||||
);
|
||||
mmap_string_free(plain);
|
||||
|
||||
if let Ok(ctext_v) = ctext {
|
||||
/* create MIME-structure that will contain the encrypted text */
|
||||
let mut encrypted_part: *mut Mailmime = new_data_part(
|
||||
ptr::null_mut(),
|
||||
0 as libc::size_t,
|
||||
"multipart/encrypted",
|
||||
MAILMIME_MECHANISM_BASE64,
|
||||
)?;
|
||||
let content: *mut mailmime_content = (*encrypted_part).mm_content_type;
|
||||
wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
||||
let version_mime: *mut Mailmime = new_data_part(
|
||||
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
||||
strlen(VERSION_CONTENT.as_mut_ptr()),
|
||||
"application/pgp-encrypted",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
mailmime_smart_add_part(encrypted_part, version_mime);
|
||||
let ctext_v = ctext?;
|
||||
|
||||
// we assume that ctext_v is not dropped until the end
|
||||
// of this if-scope
|
||||
let ctext_part: *mut Mailmime = new_data_part(
|
||||
ctext_v.as_ptr() as *mut libc::c_void,
|
||||
ctext_v.len(),
|
||||
"application/octet-stream",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
mailmime_smart_add_part(encrypted_part, ctext_part);
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||
(*encrypted_part).mm_parent = in_out_message;
|
||||
let gossiped = !&gossip_headers.is_empty();
|
||||
factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
||||
Ok(true)
|
||||
} else {
|
||||
bail!("encryption failed")
|
||||
}
|
||||
// create MIME-structure that will contain the encrypted text
|
||||
let mut encrypted_part = new_data_part(
|
||||
ptr::null_mut(),
|
||||
0 as libc::size_t,
|
||||
"multipart/encrypted",
|
||||
MAILMIME_MECHANISM_BASE64,
|
||||
)?;
|
||||
let content = (*encrypted_part).mm_content_type;
|
||||
wrapmime::append_ct_param(content, "protocol", "application/pgp-encrypted")?;
|
||||
|
||||
let version_mime = new_data_part(
|
||||
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
|
||||
strlen(VERSION_CONTENT.as_mut_ptr()),
|
||||
"application/pgp-encrypted",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
mailmime_smart_add_part(encrypted_part, version_mime);
|
||||
|
||||
// we assume that ctext_v is not dropped until the end
|
||||
// of this if-scope
|
||||
let ctext_part = new_data_part(
|
||||
ctext_v.as_ptr() as *mut libc::c_void,
|
||||
ctext_v.len(),
|
||||
"application/octet-stream",
|
||||
MAILMIME_MECHANISM_7BIT,
|
||||
)?;
|
||||
|
||||
mailmime_smart_add_part(encrypted_part, ctext_part);
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||
(*encrypted_part).mm_parent = in_out_message;
|
||||
let gossiped = !&gossip_headers.is_empty();
|
||||
factory.finalize_mime_message(in_out_message, true, gossiped)?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct E2eeHelper {
|
||||
// for decrypting only
|
||||
pub encrypted: bool,
|
||||
pub signatures: HashSet<String>,
|
||||
pub gossipped_addr: HashSet<String>,
|
||||
}
|
||||
pub fn try_decrypt(
|
||||
context: &Context,
|
||||
in_out_message: *mut Mailmime,
|
||||
) -> Result<(bool, HashSet<String>, HashSet<String>)> {
|
||||
let mut encrypted = false;
|
||||
let mut signatures = HashSet::default();
|
||||
let mut gossipped_addr = HashSet::default();
|
||||
|
||||
impl E2eeHelper {
|
||||
pub unsafe fn try_decrypt(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
in_out_message: *mut Mailmime,
|
||||
) -> Result<()> {
|
||||
/*just a pointer into mailmime structure, must not be freed*/
|
||||
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
|
||||
let mut message_time = 0;
|
||||
let mut from = None;
|
||||
let mut private_keyring = Keyring::default();
|
||||
let mut public_keyring_for_validate = Keyring::default();
|
||||
let mut gossip_headers: *mut mailimf_fields = ptr::null_mut();
|
||||
// just a pointer into mailmime structure, must not be freed
|
||||
let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) };
|
||||
let mut message_time = 0;
|
||||
let mut from = None;
|
||||
let mut private_keyring = Keyring::default();
|
||||
let mut public_keyring_for_validate = Keyring::default();
|
||||
let mut gossip_headers = ptr::null_mut();
|
||||
|
||||
// XXX do wrapmime:: helper for the next block
|
||||
if !(in_out_message.is_null() || imffields.is_null()) {
|
||||
let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||
// XXX do wrapmime:: helper for the next block
|
||||
if !(in_out_message.is_null() || imffields.is_null()) {
|
||||
let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||
|
||||
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
|
||||
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
|
||||
}
|
||||
if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } {
|
||||
let mb_list = unsafe { (*(*field).fld_data.fld_from).frm_mb_list };
|
||||
from = mailimf_find_first_addr(mb_list);
|
||||
}
|
||||
|
||||
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
|
||||
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
|
||||
if !orig_date.is_null() {
|
||||
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
|
||||
if message_time != 0 && message_time > time() {
|
||||
message_time = time()
|
||||
}
|
||||
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||
if !field.is_null() && unsafe { !(*field).fld_data.fld_orig_date.is_null() } {
|
||||
let orig_date = unsafe { (*field).fld_data.fld_orig_date };
|
||||
|
||||
if !orig_date.is_null() {
|
||||
let dt = unsafe { (*orig_date).dt_date_time };
|
||||
message_time = dc_timestamp_from_date(dt);
|
||||
if message_time != 0 && message_time > time() {
|
||||
message_time = time()
|
||||
}
|
||||
}
|
||||
let mut peerstate = None;
|
||||
let autocryptheader = from
|
||||
.as_ref()
|
||||
.and_then(|from| Aheader::from_imffields(from, imffields));
|
||||
if message_time > 0 {
|
||||
if let Some(ref from) = from {
|
||||
peerstate = Peerstate::from_addr(context, &context.sql, from);
|
||||
}
|
||||
let mut peerstate = None;
|
||||
let autocryptheader = from
|
||||
.as_ref()
|
||||
.and_then(|from| Aheader::from_imffields(from, imffields));
|
||||
if message_time > 0 {
|
||||
if let Some(ref from) = from {
|
||||
peerstate = Peerstate::from_addr(context, &context.sql, from);
|
||||
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
if let Some(ref header) = autocryptheader {
|
||||
peerstate.apply_header(&header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||
} else if message_time > peerstate.last_seen_autocrypt
|
||||
&& !contains_report(in_out_message)
|
||||
{
|
||||
peerstate.degrade_encryption(message_time);
|
||||
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||
}
|
||||
} else if let Some(ref header) = autocryptheader {
|
||||
let p = Peerstate::from_header(context, header, message_time);
|
||||
p.save_to_db(&context.sql, true).unwrap();
|
||||
peerstate = Some(p);
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
if let Some(ref header) = autocryptheader {
|
||||
peerstate.apply_header(&header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||
} else if message_time > peerstate.last_seen_autocrypt
|
||||
&& !contains_report(in_out_message)
|
||||
{
|
||||
peerstate.degrade_encryption(message_time);
|
||||
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||
}
|
||||
} else if let Some(ref header) = autocryptheader {
|
||||
let p = Peerstate::from_header(context, header, message_time);
|
||||
p.save_to_db(&context.sql, true).unwrap();
|
||||
peerstate = Some(p);
|
||||
}
|
||||
}
|
||||
/* load private key for decryption */
|
||||
let self_addr = context.get_config(Config::ConfiguredAddr);
|
||||
if let Some(self_addr) = self_addr {
|
||||
if private_keyring.load_self_private_for_decrypting(
|
||||
}
|
||||
/* load private key for decryption */
|
||||
let self_addr = context.get_config(Config::ConfiguredAddr);
|
||||
if let Some(self_addr) = self_addr {
|
||||
if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) {
|
||||
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
|
||||
peerstate =
|
||||
Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default());
|
||||
}
|
||||
if let Some(ref peerstate) = peerstate {
|
||||
if peerstate.degrade_event.is_some() {
|
||||
handle_degrade_event(context, &peerstate)?;
|
||||
}
|
||||
if let Some(ref key) = peerstate.gossip_key {
|
||||
public_keyring_for_validate.add_ref(key);
|
||||
}
|
||||
if let Some(ref key) = peerstate.public_key {
|
||||
public_keyring_for_validate.add_ref(key);
|
||||
}
|
||||
}
|
||||
|
||||
encrypted = decrypt_if_autocrypt_message(
|
||||
context,
|
||||
self_addr,
|
||||
&context.sql,
|
||||
) {
|
||||
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
|
||||
peerstate =
|
||||
Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default());
|
||||
}
|
||||
if let Some(ref peerstate) = peerstate {
|
||||
if peerstate.degrade_event.is_some() {
|
||||
handle_degrade_event(context, &peerstate);
|
||||
}
|
||||
if let Some(ref key) = peerstate.gossip_key {
|
||||
public_keyring_for_validate.add_ref(key);
|
||||
}
|
||||
if let Some(ref key) = peerstate.public_key {
|
||||
public_keyring_for_validate.add_ref(key);
|
||||
}
|
||||
}
|
||||
|
||||
match decrypt_if_autocrypt_message(
|
||||
context,
|
||||
in_out_message,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
&mut self.signatures,
|
||||
&mut gossip_headers,
|
||||
) {
|
||||
Ok(res) => {
|
||||
self.encrypted = res;
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("failed to decrypt: {}", err);
|
||||
}
|
||||
}
|
||||
if !gossip_headers.is_null() {
|
||||
self.gossipped_addr = update_gossip_peerstates(
|
||||
context,
|
||||
message_time,
|
||||
imffields,
|
||||
gossip_headers,
|
||||
)
|
||||
}
|
||||
in_out_message,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
&mut signatures,
|
||||
&mut gossip_headers,
|
||||
)?;
|
||||
if !gossip_headers.is_null() {
|
||||
gossipped_addr =
|
||||
update_gossip_peerstates(context, message_time, imffields, gossip_headers)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !gossip_headers.is_null() {
|
||||
mailimf_fields_free(gossip_headers);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
if !gossip_headers.is_null() {
|
||||
unsafe { mailimf_fields_free(gossip_headers) };
|
||||
}
|
||||
|
||||
Ok((encrypted, signatures, gossipped_addr))
|
||||
}
|
||||
|
||||
fn new_data_part(
|
||||
@@ -414,31 +373,34 @@ fn new_data_part(
|
||||
default_encoding: u32,
|
||||
) -> Result<*mut Mailmime> {
|
||||
let content = new_content_type(&content_type)?;
|
||||
unsafe {
|
||||
let mut encoding: *mut mailmime_mechanism = ptr::null_mut();
|
||||
if wrapmime::content_type_needs_encoding(content) {
|
||||
encoding = mailmime_mechanism_new(default_encoding as i32, ptr::null_mut());
|
||||
ensure!(!encoding.is_null(), "failed to create encoding");
|
||||
}
|
||||
let mime_fields = mailmime_fields_new_with_data(
|
||||
encoding,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
ensure!(!mime_fields.is_null(), "internal mime error");
|
||||
|
||||
let mime = mailmime_new_empty(content, mime_fields);
|
||||
ensure!(!mime.is_null(), "internal mime error");
|
||||
|
||||
if (*mime).mm_type == MAILMIME_SINGLE as libc::c_int {
|
||||
if !data.is_null() && data_bytes > 0 {
|
||||
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
|
||||
}
|
||||
}
|
||||
return Ok(mime);
|
||||
let mut encoding = ptr::null_mut();
|
||||
if wrapmime::content_type_needs_encoding(content) {
|
||||
encoding = unsafe { mailmime_mechanism_new(default_encoding as i32, ptr::null_mut()) };
|
||||
ensure!(!encoding.is_null(), "failed to create encoding");
|
||||
}
|
||||
let mime_fields = {
|
||||
unsafe {
|
||||
mailmime_fields_new_with_data(
|
||||
encoding,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
};
|
||||
ensure!(!mime_fields.is_null(), "internal mime error");
|
||||
|
||||
let mime = unsafe { mailmime_new_empty(content, mime_fields) };
|
||||
ensure!(!mime.is_null(), "internal mime error");
|
||||
|
||||
if unsafe { (*mime).mm_type } == MAILMIME_SINGLE as libc::c_int {
|
||||
if !data.is_null() && data_bytes > 0 {
|
||||
unsafe { mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes) };
|
||||
}
|
||||
}
|
||||
|
||||
Ok(mime)
|
||||
}
|
||||
|
||||
/// Load public key from database or generate a new one.
|
||||
@@ -470,7 +432,7 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
||||
&public_key,
|
||||
&private_key,
|
||||
&self_addr,
|
||||
1,
|
||||
true,
|
||||
&context.sql,
|
||||
) {
|
||||
true => {
|
||||
@@ -488,31 +450,37 @@ fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn update_gossip_peerstates(
|
||||
fn update_gossip_peerstates(
|
||||
context: &Context,
|
||||
message_time: i64,
|
||||
imffields: *mut mailimf_fields,
|
||||
gossip_headers: *const mailimf_fields,
|
||||
) -> HashSet<String> {
|
||||
) -> Result<HashSet<String>> {
|
||||
// XXX split the parsing from the modification part
|
||||
let mut recipients: Option<HashSet<String>> = None;
|
||||
let mut gossipped_addr: HashSet<String> = Default::default();
|
||||
|
||||
for cur_data in (*(*gossip_headers).fld_list).into_iter() {
|
||||
let field: *mut mailimf_field = cur_data as *mut _;
|
||||
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let optional_field = (*field).fld_data.fld_optional_field;
|
||||
if !optional_field.is_null()
|
||||
&& !(*optional_field).fld_name.is_null()
|
||||
&& strcasecmp(
|
||||
(*optional_field).fld_name,
|
||||
b"Autocrypt-Gossip\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
for cur_data in unsafe { (*(*gossip_headers).fld_list).into_iter() } {
|
||||
let field = cur_data as *mut mailimf_field;
|
||||
if field.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let field = unsafe { *field };
|
||||
|
||||
if field.fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let optional_field = unsafe { field.fld_data.fld_optional_field };
|
||||
if optional_field.is_null() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let optional_field = unsafe { *optional_field };
|
||||
if !optional_field.fld_name.is_null()
|
||||
&& as_str(optional_field.fld_name) == "Autocrypt-Gossip"
|
||||
{
|
||||
let value = CStr::from_ptr((*optional_field).fld_value)
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let gossip_header = Aheader::from_str(value);
|
||||
let value = to_string_lossy(optional_field.fld_value);
|
||||
let gossip_header = Aheader::from_str(&value);
|
||||
|
||||
if let Ok(ref header) = gossip_header {
|
||||
if recipients.is_none() {
|
||||
recipients = Some(mailimf_get_recipients(imffields));
|
||||
@@ -522,15 +490,15 @@ unsafe fn update_gossip_peerstates(
|
||||
Peerstate::from_addr(context, &context.sql, &header.addr);
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
peerstate.apply_gossip(header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false).unwrap();
|
||||
peerstate.save_to_db(&context.sql, false)?;
|
||||
} else {
|
||||
let p = Peerstate::from_gossip(context, header, message_time);
|
||||
p.save_to_db(&context.sql, true).unwrap();
|
||||
p.save_to_db(&context.sql, true)?;
|
||||
peerstate = Some(p);
|
||||
}
|
||||
if let Some(peerstate) = peerstate {
|
||||
if peerstate.degrade_event.is_some() {
|
||||
handle_degrade_event(context, &peerstate);
|
||||
handle_degrade_event(context, &peerstate)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,7 +515,7 @@ unsafe fn update_gossip_peerstates(
|
||||
}
|
||||
}
|
||||
|
||||
gossipped_addr
|
||||
Ok(gossipped_addr)
|
||||
}
|
||||
|
||||
fn decrypt_if_autocrypt_message(
|
||||
@@ -582,16 +550,15 @@ fn decrypt_if_autocrypt_message(
|
||||
public_keyring_for_validate,
|
||||
ret_valid_signatures,
|
||||
)?;
|
||||
/* decrypted_mime is a dangling pointer which we now put into
|
||||
mailmime's Ownership */
|
||||
// decrypted_mime is a dangling pointer which we now put into mailmime's Ownership
|
||||
unsafe {
|
||||
mailmime_substitute(mime, decrypted_mime);
|
||||
mailmime_free(mime);
|
||||
}
|
||||
|
||||
/* finally, let's also return gossip headers
|
||||
XXX better return parsed headers so that upstream
|
||||
does not need to dive into mmime-stuff again. */
|
||||
// finally, let's also return gossip headers
|
||||
// XXX better return parsed headers so that upstream
|
||||
// does not need to dive into mmime-stuff again.
|
||||
unsafe {
|
||||
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
|
||||
let mut dummy: libc::size_t = 0;
|
||||
@@ -604,11 +571,12 @@ fn decrypt_if_autocrypt_message(
|
||||
) == MAILIMF_NO_ERROR as libc::c_int
|
||||
&& !test.is_null()
|
||||
{
|
||||
*ret_gossip_headers = test
|
||||
*ret_gossip_headers = test;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(true);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn decrypt_part(
|
||||
@@ -634,74 +602,75 @@ fn decrypt_part(
|
||||
|
||||
let (decoded_data, decoded_data_bytes) =
|
||||
wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?;
|
||||
/* encrypted, non-NULL decoded data in decoded_data now ...
|
||||
Note that we need to take care of freeing decoded_data ourself,
|
||||
after encryption has been attempted.
|
||||
*/
|
||||
|
||||
// encrypted, non-NULL decoded data in decoded_data now ...
|
||||
// Note that we need to take care of freeing decoded_data ourself,
|
||||
// after encryption has been attempted.
|
||||
let mut ret_decrypted_mime = ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
if has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int) {
|
||||
/* we should only have one decryption happening */
|
||||
ensure!(ret_valid_signatures.is_empty(), "corrupt signatures");
|
||||
ensure!(!decoded_data.is_null(), "Missing data");
|
||||
let data = unsafe { std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes) };
|
||||
if has_decrypted_pgp_armor(data) {
|
||||
// we should only have one decryption happening
|
||||
ensure!(ret_valid_signatures.is_empty(), "corrupt signatures");
|
||||
|
||||
let plain = match dc_pgp_pk_decrypt(
|
||||
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes),
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
Some(ret_valid_signatures),
|
||||
) {
|
||||
Ok(plain) => {
|
||||
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
|
||||
plain
|
||||
}
|
||||
Err(err) => {
|
||||
mmap_string_unref(decoded_data);
|
||||
bail!("could not decrypt: {}", err)
|
||||
}
|
||||
};
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
let plain = match dc_pgp_pk_decrypt(
|
||||
data,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
Some(ret_valid_signatures),
|
||||
) {
|
||||
Ok(plain) => {
|
||||
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
|
||||
plain
|
||||
}
|
||||
Err(err) => {
|
||||
unsafe { mmap_string_unref(decoded_data) };
|
||||
bail!("could not decrypt: {}", err)
|
||||
}
|
||||
};
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index: libc::size_t = 0;
|
||||
let mut decrypted_mime: *mut Mailmime = ptr::null_mut();
|
||||
if mailmime_parse(
|
||||
let mut index = 0;
|
||||
let mut decrypted_mime = ptr::null_mut();
|
||||
if unsafe {
|
||||
mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
) != MAIL_NO_ERROR as libc::c_int
|
||||
|| decrypted_mime.is_null()
|
||||
{
|
||||
if !decrypted_mime.is_null() {
|
||||
mailmime_free(decrypted_mime);
|
||||
}
|
||||
} else {
|
||||
ret_decrypted_mime = decrypted_mime;
|
||||
)
|
||||
} != MAIL_NO_ERROR as libc::c_int
|
||||
|| decrypted_mime.is_null()
|
||||
{
|
||||
if !decrypted_mime.is_null() {
|
||||
unsafe { mailmime_free(decrypted_mime) };
|
||||
}
|
||||
} else {
|
||||
// decrypted_mime points into `plain`.
|
||||
// FIXME(@dignifiedquire): this still leaks memory I believe, as mailmime_free
|
||||
// does not free the underlying buffer. But for now we have to live with it
|
||||
std::mem::forget(plain);
|
||||
ret_decrypted_mime = decrypted_mime;
|
||||
}
|
||||
mmap_string_unref(decoded_data);
|
||||
}
|
||||
unsafe { mmap_string_unref(decoded_data) };
|
||||
|
||||
Ok(ret_decrypted_mime)
|
||||
}
|
||||
|
||||
unsafe fn has_decrypted_pgp_armor(str__: *const libc::c_char, mut str_bytes: libc::c_int) -> bool {
|
||||
let str_end: *const libc::c_uchar = (str__ as *const libc::c_uchar).offset(str_bytes as isize);
|
||||
let mut p: *const libc::c_uchar = str__ as *const libc::c_uchar;
|
||||
while p < str_end {
|
||||
if *p as libc::c_int > ' ' as i32 {
|
||||
break;
|
||||
fn has_decrypted_pgp_armor(input: &[u8]) -> bool {
|
||||
if let Some(index) = input.iter().position(|b| *b > b' ') {
|
||||
if input.len() - index > 26 {
|
||||
let start = index;
|
||||
let end = start + 27;
|
||||
|
||||
return &input[start..end] == b"-----BEGIN PGP MESSAGE-----";
|
||||
}
|
||||
p = p.offset(1isize);
|
||||
str_bytes -= 1
|
||||
}
|
||||
str_bytes > 27i32
|
||||
&& strncmp(
|
||||
p as *const libc::c_char,
|
||||
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
|
||||
27,
|
||||
) == 0
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Check if a MIME structure contains a multipart/report part.
|
||||
@@ -712,29 +681,31 @@ unsafe fn has_decrypted_pgp_armor(str__: *const libc::c_char, mut str_bytes: lib
|
||||
/// However, Delta Chat itself has no problem with encrypted multipart/report
|
||||
/// parts and MUAs should be encouraged to encrpyt multipart/reports as well so
|
||||
/// that we could use the normal Autocrypt processing.
|
||||
unsafe fn contains_report(mime: *mut Mailmime) -> bool {
|
||||
if (*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
||||
if (*(*(*mime).mm_content_type).ct_type).tp_type
|
||||
== MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
||||
&& (*(*(*(*mime).mm_content_type).ct_type)
|
||||
.tp_data
|
||||
.tp_composite_type)
|
||||
.ct_type
|
||||
== MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
|
||||
&& strcmp(
|
||||
(*(*mime).mm_content_type).ct_subtype,
|
||||
b"report\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
fn contains_report(mime: *mut Mailmime) -> bool {
|
||||
assert!(!mime.is_null());
|
||||
let mime = unsafe { *mime };
|
||||
|
||||
if mime.mm_type == MAILMIME_MULTIPLE as libc::c_int {
|
||||
let tp_type = unsafe { (*(*mime.mm_content_type).ct_type).tp_type };
|
||||
let ct_type =
|
||||
unsafe { (*(*(*mime.mm_content_type).ct_type).tp_data.tp_composite_type).ct_type };
|
||||
|
||||
if tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int
|
||||
&& ct_type == MAILMIME_COMPOSITE_TYPE_MULTIPART as libc::c_int
|
||||
&& as_str(unsafe { (*mime.mm_content_type).ct_subtype }) == "report"
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for cur_data in (*(*(*mime).mm_mime_fields).fld_list).into_iter() {
|
||||
|
||||
for cur_data in unsafe { (*(*mime.mm_mime_fields).fld_list).into_iter() } {
|
||||
if contains_report(cur_data as *mut Mailmime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (*mime).mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||
if contains_report((*mime).mm_data.mm_message.mm_msg_mime) {
|
||||
} else if mime.mm_type == MAILMIME_MESSAGE as libc::c_int {
|
||||
let m = unsafe { mime.mm_data.mm_message.mm_msg_mime };
|
||||
|
||||
if contains_report(m) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -869,4 +840,22 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
assert_eq!(res0.unwrap(), res1.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_decrypted_pgp_armor() {
|
||||
let data = b" -----BEGIN PGP MESSAGE-----";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||
|
||||
let data = b" \n-----BEGIN PGP MESSAGE-----";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||
|
||||
let data = b" -----BEGIN PGP MESSAGE---";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||
|
||||
let data = b" -----BEGIN PGP MESSAGE-----";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), true);
|
||||
|
||||
let data = b"blas";
|
||||
assert_eq!(has_decrypted_pgp_armor(data), false);
|
||||
}
|
||||
}
|
||||
|
||||
16
src/error.rs
16
src/error.rs
@@ -26,6 +26,10 @@ pub enum Error {
|
||||
CStringError(crate::dc_tools::CStringError),
|
||||
#[fail(display = "PGP: {:?}", _0)]
|
||||
Pgp(pgp::errors::Error),
|
||||
#[fail(display = "Base64Decode: {:?}", _0)]
|
||||
Base64Decode(base64::DecodeError),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
FromUtf8(std::string::FromUtf8Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -36,6 +40,12 @@ impl From<rusqlite::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<base64::DecodeError> for Error {
|
||||
fn from(err: base64::DecodeError) -> Error {
|
||||
Error::Base64Decode(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<failure::Error> for Error {
|
||||
fn from(err: failure::Error) -> Error {
|
||||
Error::Failure(err)
|
||||
@@ -60,6 +70,12 @@ impl From<std::str::Utf8Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::string::FromUtf8Error> for Error {
|
||||
fn from(err: std::string::FromUtf8Error) -> Error {
|
||||
Error::FromUtf8(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<image_meta::ImageError> for Error {
|
||||
fn from(err: image_meta::ImageError) -> Error {
|
||||
Error::Image(err)
|
||||
|
||||
@@ -167,7 +167,7 @@ pub enum Event {
|
||||
#[strum(props(id = "2041"))]
|
||||
ConfigureProgress(usize),
|
||||
|
||||
/// Inform about the import/export progress started by dc_imex().
|
||||
/// Inform about the import/export progress started by imex().
|
||||
///
|
||||
/// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done
|
||||
/// @param data2 0
|
||||
@@ -175,8 +175,8 @@ pub enum Event {
|
||||
#[strum(props(id = "2051"))]
|
||||
ImexProgress(usize),
|
||||
|
||||
/// A file has been exported. A file has been written by dc_imex().
|
||||
/// This event may be sent multiple times by a single call to dc_imex().
|
||||
/// A file has been exported. A file has been written by imex().
|
||||
/// This event may be sent multiple times by a single call to imex().
|
||||
///
|
||||
/// A typical purpose for a handler of this event may be to make the file public to some system
|
||||
/// services.
|
||||
|
||||
@@ -2,10 +2,7 @@ use std::ffi::CString;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
|
||||
use libc::{free, strlen};
|
||||
use mmime::mailmime::content::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
use num_traits::FromPrimitive;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::chat;
|
||||
@@ -25,12 +22,48 @@ use crate::pgp::*;
|
||||
use crate::sql::{self, Sql};
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
// import/export and tools
|
||||
// param1 is a directory where the keys are written to
|
||||
// param1 is a directory where the keys are searched in and read from
|
||||
// param1 is a directory where the backup is written to
|
||||
// param1 is the file with the backup to import
|
||||
pub fn dc_imex(context: &Context, what: libc::c_int, param1: Option<impl AsRef<Path>>) {
|
||||
#[derive(Debug, Display, Copy, Clone, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
#[repr(i32)]
|
||||
pub enum ImexMode {
|
||||
/// Export all private keys and all public keys of the user to the
|
||||
/// directory given as `param1`. The default key is written to the files `public-key-default.asc`
|
||||
/// and `private-key-default.asc`, if there are more keys, they are written to files as
|
||||
/// `public-key-<id>.asc` and `private-key-<id>.asc`
|
||||
ExportSelfKeys = 1,
|
||||
/// Import private keys found in the directory given as `param1`.
|
||||
/// The last imported key is made the default keys unless its name contains the string `legacy`.
|
||||
/// Public keys are not imported.
|
||||
ImportSelfKeys = 2,
|
||||
/// Export a backup to the directory given as `param1`.
|
||||
/// The backup contains all contacts, chats, images and other data and device independent settings.
|
||||
/// The backup does not contain device dependent settings as ringtones or LED notification settings.
|
||||
/// The name of the backup is typically `delta-chat.<day>.bak`, if more than one backup is create on a day,
|
||||
/// the format is `delta-chat.<day>-<number>.bak`
|
||||
ExportBackup = 11,
|
||||
/// `param1` is the file (not: directory) to import. The file is normally
|
||||
/// created by DC_IMEX_EXPORT_BACKUP and detected by dc_imex_has_backup(). Importing a backup
|
||||
/// is only possible as long as the context is not configured or used in another way.
|
||||
ImportBackup = 12,
|
||||
}
|
||||
|
||||
/// Import/export things.
|
||||
/// For this purpose, the function creates a job that is executed in the IMAP-thread then;
|
||||
/// this requires to call dc_perform_imap_jobs() regularly.
|
||||
///
|
||||
/// What to do is defined by the _what_ parameter.
|
||||
///
|
||||
/// While dc_imex() returns immediately, the started job may take a while,
|
||||
/// you can stop it using dc_stop_ongoing_process(). During execution of the job,
|
||||
/// some events are sent out:
|
||||
///
|
||||
/// - A number of #DC_EVENT_IMEX_PROGRESS events are sent and may be used to create
|
||||
/// a progress bar or stuff like that. Moreover, you'll be informed when the imex-job is done.
|
||||
///
|
||||
/// - For each file written on export, the function sends #DC_EVENT_IMEX_FILE_WRITTEN
|
||||
///
|
||||
/// Only one import-/export-progress can run at the same time.
|
||||
/// To cancel an import-/export-progress, use dc_stop_ongoing_process().
|
||||
pub fn imex(context: &Context, what: ImexMode, param1: Option<impl AsRef<Path>>) {
|
||||
let mut param = Params::new();
|
||||
param.set_int(Param::Cmd, what as i32);
|
||||
if let Some(param1) = param1 {
|
||||
@@ -42,7 +75,7 @@ pub fn dc_imex(context: &Context, what: libc::c_int, param1: Option<impl AsRef<P
|
||||
}
|
||||
|
||||
/// Returns the filename of the backup if found, nullptr otherwise.
|
||||
pub fn dc_imex_has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<String> {
|
||||
pub fn has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<String> {
|
||||
let dir_name = dir_name.as_ref();
|
||||
let dir_iter = std::fs::read_dir(dir_name)?;
|
||||
let mut newest_backup_time = 0;
|
||||
@@ -75,10 +108,10 @@ pub fn dc_imex_has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Resu
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
pub fn initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
let mut msg: Message;
|
||||
ensure!(dc_alloc_ongoing(context), "could not allocate ongoing");
|
||||
let setup_code = dc_create_setup_code(context);
|
||||
let setup_code = create_setup_code(context);
|
||||
/* this may require a keypair to be created. this may take a second ... */
|
||||
if !context
|
||||
.running_state
|
||||
@@ -87,7 +120,7 @@ pub fn dc_initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
.unwrap()
|
||||
.shall_stop_ongoing
|
||||
{
|
||||
if let Ok(ref setup_file_content) = dc_render_setup_file(context, &setup_code) {
|
||||
if let Ok(ref setup_file_content) = render_setup_file(context, &setup_code) {
|
||||
/* encrypting may also take a while ... */
|
||||
if !context
|
||||
.running_state
|
||||
@@ -153,7 +186,7 @@ pub fn dc_initiate_key_transfer(context: &Context) -> Result<String> {
|
||||
/// Renders HTML body of a setup file message.
|
||||
///
|
||||
/// The `passphrase` must be at least 2 characters long.
|
||||
pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<String> {
|
||||
pub fn render_setup_file(context: &Context, passphrase: &str) -> Result<String> {
|
||||
ensure!(
|
||||
passphrase.len() >= 2,
|
||||
"Passphrase must be at least 2 chars long."
|
||||
@@ -203,7 +236,7 @@ pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<Strin
|
||||
))
|
||||
}
|
||||
|
||||
pub fn dc_create_setup_code(_context: &Context) -> String {
|
||||
pub fn create_setup_code(_context: &Context) -> String {
|
||||
let mut random_val: u16;
|
||||
let mut rng = thread_rng();
|
||||
let mut ret = String::new();
|
||||
@@ -211,22 +244,22 @@ pub fn dc_create_setup_code(_context: &Context) -> String {
|
||||
for i in 0..9 {
|
||||
loop {
|
||||
random_val = rng.gen();
|
||||
if !(random_val as libc::c_int > 60000) {
|
||||
if !(random_val as usize > 60000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
random_val = (random_val as libc::c_int % 10000) as u16;
|
||||
random_val = (random_val as usize % 10000) as u16;
|
||||
ret += &format!(
|
||||
"{}{:04}",
|
||||
if 0 != i { "-" } else { "" },
|
||||
random_val as libc::c_int,
|
||||
random_val as usize
|
||||
);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn dc_continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str) -> Result<()> {
|
||||
pub fn continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str) -> Result<()> {
|
||||
ensure!(msg_id > DC_MSG_ID_LAST_SPECIAL, "wrong id");
|
||||
|
||||
let msg = Message::load_from_db(context, msg_id);
|
||||
@@ -240,17 +273,14 @@ pub fn dc_continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str
|
||||
);
|
||||
|
||||
if let Some(filename) = msg.get_file(context) {
|
||||
if let Ok(buf) = dc_read_file(context, filename) {
|
||||
let norm_sc = CString::yolo(dc_normalize_setup_code(setup_code));
|
||||
unsafe {
|
||||
if let Ok(armored_key) =
|
||||
dc_decrypt_setup_file(context, norm_sc.as_ptr(), buf.as_ptr().cast())
|
||||
{
|
||||
set_self_key(context, &armored_key, true, true)?;
|
||||
} else {
|
||||
bail!("Bad setup code.")
|
||||
}
|
||||
if let Ok(ref mut buf) = dc_read_file(context, filename) {
|
||||
let sc = normalize_setup_code(setup_code);
|
||||
if let Ok(armored_key) = decrypt_setup_file(context, sc, buf) {
|
||||
set_self_key(context, &armored_key, true, true)?;
|
||||
} else {
|
||||
bail!("Bad setup code.")
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
bail!("Cannot read Autocrypt Setup Message file.");
|
||||
@@ -320,7 +350,7 @@ fn set_self_key(
|
||||
&public_key,
|
||||
&private_key,
|
||||
self_addr.unwrap(),
|
||||
set_default as libc::c_int,
|
||||
set_default,
|
||||
&context.sql,
|
||||
) {
|
||||
bail!("Cannot save keypair, internal key-state possibly corrupted now!");
|
||||
@@ -328,65 +358,56 @@ fn set_self_key(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub unsafe fn dc_decrypt_setup_file(
|
||||
context: &Context,
|
||||
passphrase: *const libc::c_char,
|
||||
filecontent: *const libc::c_char,
|
||||
fn decrypt_setup_file(
|
||||
_context: &Context,
|
||||
passphrase: impl AsRef<str>,
|
||||
filecontent: &mut [u8],
|
||||
) -> Result<String> {
|
||||
let fc_buf: *mut libc::c_char;
|
||||
let mut fc_headerline = String::default();
|
||||
let mut fc_base64: *const libc::c_char = ptr::null();
|
||||
let mut binary: *mut libc::c_char = ptr::null_mut();
|
||||
let mut binary_bytes: libc::size_t = 0;
|
||||
let mut indx: libc::size_t = 0;
|
||||
|
||||
let mut payload: Result<String> = Err(format_err!("Failed to decrypt"));
|
||||
let split_result = unsafe {
|
||||
dc_split_armored_data(
|
||||
filecontent.as_mut_ptr().cast(),
|
||||
&mut fc_headerline,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut fc_base64,
|
||||
)
|
||||
};
|
||||
|
||||
fc_buf = dc_strdup(filecontent);
|
||||
if dc_split_armored_data(
|
||||
fc_buf,
|
||||
&mut fc_headerline,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut fc_base64,
|
||||
) && fc_headerline == "-----BEGIN PGP MESSAGE-----"
|
||||
&& !fc_base64.is_null()
|
||||
{
|
||||
/* convert base64 to binary */
|
||||
/*must be freed using mmap_string_unref()*/
|
||||
if !(mailmime_base64_body_parse(
|
||||
fc_base64,
|
||||
strlen(fc_base64),
|
||||
&mut indx,
|
||||
&mut binary,
|
||||
&mut binary_bytes,
|
||||
) != MAILIMF_NO_ERROR as libc::c_int
|
||||
|| binary.is_null()
|
||||
|| binary_bytes == 0)
|
||||
{
|
||||
/* decrypt symmetrically */
|
||||
match dc_pgp_symm_decrypt(
|
||||
as_str(passphrase),
|
||||
std::slice::from_raw_parts(binary as *const u8, binary_bytes),
|
||||
) {
|
||||
Ok(plain) => payload = Ok(String::from_utf8(plain).unwrap()),
|
||||
Err(err) => {
|
||||
error!(context, "Failed to decrypt message: {:?}", err);
|
||||
payload = Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !split_result || fc_headerline != "-----BEGIN PGP MESSAGE-----" || fc_base64.is_null() {
|
||||
bail!("Invalid armored data");
|
||||
}
|
||||
|
||||
free(fc_buf as *mut libc::c_void);
|
||||
if !binary.is_null() {
|
||||
mmap_string_unref(binary);
|
||||
}
|
||||
// convert base64 to binary
|
||||
let base64_encoded =
|
||||
unsafe { std::slice::from_raw_parts(fc_base64 as *const u8, libc::strlen(fc_base64)) };
|
||||
|
||||
payload
|
||||
let data = base64_decode(&base64_encoded)?;
|
||||
|
||||
// decrypt symmetrically
|
||||
let payload = dc_pgp_symm_decrypt(passphrase.as_ref(), &data)?;
|
||||
let payload_str = String::from_utf8(payload)?;
|
||||
|
||||
Ok(payload_str)
|
||||
}
|
||||
|
||||
pub fn dc_normalize_setup_code(s: &str) -> String {
|
||||
/// Decode the base64 encoded slice. Handles line breaks.
|
||||
fn base64_decode(input: &[u8]) -> Result<Vec<u8>> {
|
||||
use std::io::Read;
|
||||
let c = std::io::Cursor::new(input);
|
||||
let lr = pgp::line_reader::LineReader::new(c);
|
||||
let br = pgp::base64_reader::Base64Reader::new(lr);
|
||||
let mut reader = pgp::base64_decoder::Base64Decoder::new(br);
|
||||
|
||||
let mut data = Vec::new();
|
||||
reader.read_to_end(&mut data)?;
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn normalize_setup_code(s: &str) -> String {
|
||||
let mut out = String::new();
|
||||
for c in s.chars() {
|
||||
if c >= '0' && c <= '9' {
|
||||
@@ -400,9 +421,9 @@ pub fn dc_normalize_setup_code(s: &str) -> String {
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
|
||||
pub fn job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
|
||||
ensure!(dc_alloc_ongoing(context), "could not allocate ongoing");
|
||||
let what = job.param.get_int(Param::Cmd).unwrap_or_default();
|
||||
let what: Option<ImexMode> = job.param.get_int(Param::Cmd).and_then(ImexMode::from_i32);
|
||||
let param = job.param.get(Param::Arg).unwrap_or_default();
|
||||
|
||||
ensure!(!param.is_empty(), "No Import/export dir/file given.");
|
||||
@@ -410,8 +431,8 @@ pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
|
||||
context.call_cb(Event::ImexProgress(10));
|
||||
|
||||
ensure!(context.sql.is_open(), "Database not opened.");
|
||||
if what == DC_IMEX_EXPORT_BACKUP || what == DC_IMEX_EXPORT_SELF_KEYS {
|
||||
/* before we export anything, make sure the private key exists */
|
||||
if what == Some(ImexMode::ExportBackup) || what == Some(ImexMode::ExportSelfKeys) {
|
||||
// before we export anything, make sure the private key exists
|
||||
if e2ee::ensure_secret_key_exists(context).is_err() {
|
||||
dc_free_ongoing(context);
|
||||
bail!("Cannot create private key or private key not available.");
|
||||
@@ -421,12 +442,12 @@ pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
|
||||
}
|
||||
let path = Path::new(param);
|
||||
let success = match what {
|
||||
DC_IMEX_EXPORT_SELF_KEYS => export_self_keys(context, path),
|
||||
DC_IMEX_IMPORT_SELF_KEYS => import_self_keys(context, path),
|
||||
DC_IMEX_EXPORT_BACKUP => unsafe { export_backup(context, path) },
|
||||
DC_IMEX_IMPORT_BACKUP => import_backup(context, path),
|
||||
_ => {
|
||||
bail!("unknown IMEX type: {}", what);
|
||||
Some(ImexMode::ExportSelfKeys) => export_self_keys(context, path),
|
||||
Some(ImexMode::ImportSelfKeys) => import_self_keys(context, path),
|
||||
Some(ImexMode::ExportBackup) => export_backup(context, path),
|
||||
Some(ImexMode::ImportBackup) => import_backup(context, path),
|
||||
None => {
|
||||
bail!("unknown IMEX type");
|
||||
}
|
||||
};
|
||||
dc_free_ongoing(context);
|
||||
@@ -443,11 +464,7 @@ pub fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Import backup
|
||||
******************************************************************************/
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
/// Import Backup
|
||||
fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Result<()> {
|
||||
info!(
|
||||
context,
|
||||
@@ -523,13 +540,13 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
|
||||
continue;
|
||||
}
|
||||
|
||||
let pathNfilename = context.get_blobdir().join(file_name);
|
||||
if dc_write_file(context, &pathNfilename, &file_blob) {
|
||||
let path_filename = context.get_blobdir().join(file_name);
|
||||
if dc_write_file(context, &path_filename, &file_blob) {
|
||||
continue;
|
||||
}
|
||||
bail!(
|
||||
"Storage full? Cannot write file {} with {} bytes.",
|
||||
pathNfilename.display(),
|
||||
path_filename.display(),
|
||||
file_blob.len(),
|
||||
);
|
||||
}
|
||||
@@ -550,8 +567,7 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
|
||||
******************************************************************************/
|
||||
/* the FILE_PROGRESS macro calls the callback with the permille of files processed.
|
||||
The macro avoids weird values of 0% or 100% while still working. */
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
|
||||
fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
|
||||
let mut ok_to_continue = true;
|
||||
let mut success = false;
|
||||
|
||||
@@ -650,9 +666,9 @@ unsafe fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()>
|
||||
continue;
|
||||
} else {
|
||||
info!(context, "EXPORTing filename={}", name);
|
||||
let curr_pathNfilename = context.get_blobdir().join(entry.file_name());
|
||||
let curr_path_filename = context.get_blobdir().join(entry.file_name());
|
||||
if let Ok(buf) =
|
||||
dc_read_file(context, &curr_pathNfilename)
|
||||
dc_read_file(context, &curr_path_filename)
|
||||
{
|
||||
if buf.is_empty() {
|
||||
continue;
|
||||
@@ -661,7 +677,7 @@ unsafe fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()>
|
||||
error!(
|
||||
context,
|
||||
"Disk full? Cannot add file \"{}\" to backup.",
|
||||
curr_pathNfilename.display(),
|
||||
curr_path_filename.display(),
|
||||
);
|
||||
/* this is not recoverable! writing to the sqlite database should work! */
|
||||
ok_to_continue = false;
|
||||
@@ -773,7 +789,7 @@ fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
free(buf2 as *mut libc::c_void);
|
||||
libc::free(buf2 as *mut libc::c_void);
|
||||
}
|
||||
if split_res
|
||||
&& buf2_headerline.contains("-----BEGIN PGP PUBLIC KEY BLOCK-----")
|
||||
@@ -884,7 +900,7 @@ mod tests {
|
||||
let t = test_context(Some(Box::new(logging_cb)));
|
||||
|
||||
configure_alice_keypair(&t.ctx);
|
||||
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
|
||||
let msg = render_setup_file(&t.ctx, "hello").unwrap();
|
||||
println!("{}", &msg);
|
||||
// Check some substrings, indicating things got substituted.
|
||||
// In particular note the mixing of `\r\n` and `\n` depending
|
||||
@@ -910,10 +926,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_setup_file_newline_replace() {
|
||||
fn otest_render_setup_file_newline_replace() {
|
||||
let t = test_context(Some(Box::new(ac_setup_msg_cb)));
|
||||
configure_alice_keypair(&t.ctx);
|
||||
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
|
||||
let msg = render_setup_file(&t.ctx, "pw").unwrap();
|
||||
println!("{}", &msg);
|
||||
assert!(msg.contains("<p>hello<br>there</p>"));
|
||||
}
|
||||
@@ -921,7 +937,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_create_setup_code() {
|
||||
let t = dummy_context();
|
||||
let setupcode = dc_create_setup_code(&t.ctx);
|
||||
let setupcode = create_setup_code(&t.ctx);
|
||||
assert_eq!(setupcode.len(), 44);
|
||||
assert_eq!(setupcode.chars().nth(4).unwrap(), '-');
|
||||
assert_eq!(setupcode.chars().nth(9).unwrap(), '-');
|
||||
@@ -949,12 +965,65 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_normalize_setup_code() {
|
||||
let norm = dc_normalize_setup_code("123422343234423452346234723482349234");
|
||||
let norm = normalize_setup_code("123422343234423452346234723482349234");
|
||||
assert_eq!(norm, "1234-2234-3234-4234-5234-6234-7234-8234-9234");
|
||||
|
||||
let norm = dc_normalize_setup_code(
|
||||
"\t1 2 3422343234- foo bar-- 423-45 2 34 6234723482349234 ",
|
||||
);
|
||||
let norm =
|
||||
normalize_setup_code("\t1 2 3422343234- foo bar-- 423-45 2 34 6234723482349234 ");
|
||||
assert_eq!(norm, "1234-2234-3234-4234-5234-6234-7234-8234-9234");
|
||||
}
|
||||
|
||||
/* S_EM_SETUPFILE is a AES-256 symm. encrypted setup message created by Enigmail
|
||||
with an "encrypted session key", see RFC 4880. The code is in S_EM_SETUPCODE */
|
||||
const S_EM_SETUPCODE: &str = "1742-0185-6197-1303-7016-8412-3581-4441-0597";
|
||||
const S_EM_SETUPFILE: &str = include_str!("../test-data/message/stress.txt");
|
||||
|
||||
#[test]
|
||||
fn test_split_and_decrypt() {
|
||||
let ctx = dummy_context();
|
||||
let context = &ctx.ctx;
|
||||
|
||||
let mut headerline = String::default();
|
||||
let mut setupcodebegin = ptr::null();
|
||||
let mut preferencrypt = ptr::null();
|
||||
|
||||
let mut buf_1 = S_EM_SETUPFILE.to_string();
|
||||
|
||||
unsafe {
|
||||
assert!(dc_split_armored_data(
|
||||
buf_1.as_mut_ptr().cast(),
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
&mut preferencrypt,
|
||||
ptr::null_mut(),
|
||||
));
|
||||
}
|
||||
assert_eq!(headerline, "-----BEGIN PGP MESSAGE-----");
|
||||
assert!(!setupcodebegin.is_null());
|
||||
|
||||
// TODO: verify that this is the right check
|
||||
assert!(S_EM_SETUPCODE.starts_with(as_str(setupcodebegin)));
|
||||
|
||||
assert!(preferencrypt.is_null());
|
||||
|
||||
let mut setup_file = S_EM_SETUPFILE.to_string();
|
||||
let mut decrypted = unsafe {
|
||||
decrypt_setup_file(context, S_EM_SETUPCODE, setup_file.as_bytes_mut()).unwrap()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
assert!(dc_split_armored_data(
|
||||
decrypted.as_mut_ptr().cast(),
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
&mut preferencrypt,
|
||||
ptr::null_mut(),
|
||||
));
|
||||
}
|
||||
|
||||
assert_eq!(headerline, "-----BEGIN PGP PRIVATE KEY BLOCK-----");
|
||||
assert!(setupcodebegin.is_null());
|
||||
assert!(!preferencrypt.is_null());
|
||||
assert_eq!(as_str(preferencrypt), "mutual",);
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,10 @@ use crate::chat;
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_imex::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::events::Event;
|
||||
use crate::imap::*;
|
||||
use crate::imex::*;
|
||||
use crate::location;
|
||||
use crate::login_param::LoginParam;
|
||||
use crate::message::{self, Message, MessageState};
|
||||
@@ -824,7 +824,7 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
Action::MoveMsg => job.do_DC_JOB_MOVE_MSG(context),
|
||||
Action::SendMdn => job.do_DC_JOB_SEND(context),
|
||||
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context) },
|
||||
Action::ImexImap => match dc_job_do_DC_JOB_IMEX_IMAP(context, &job) {
|
||||
Action::ImexImap => match job_do_DC_JOB_IMEX_IMAP(context, &job) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
error!(context, "{}", err);
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
|
||||
use libc;
|
||||
use pgp::composed::{Deserializable, SignedPublicKey, SignedSecretKey};
|
||||
use pgp::ser::Serialize;
|
||||
use pgp::types::{KeyTrait, SecretKeyTrait};
|
||||
@@ -254,14 +253,14 @@ pub fn dc_key_save_self_keypair(
|
||||
public_key: &Key,
|
||||
private_key: &Key,
|
||||
addr: impl AsRef<str>,
|
||||
is_default: libc::c_int,
|
||||
is_default: bool,
|
||||
sql: &Sql,
|
||||
) -> bool {
|
||||
sql::execute(
|
||||
context,
|
||||
sql,
|
||||
"INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);",
|
||||
params![addr.as_ref(), is_default, public_key.to_bytes(), private_key.to_bytes(), time()],
|
||||
params![addr.as_ref(), is_default as i32, public_key.to_bytes(), private_key.to_bytes(), time()],
|
||||
).is_ok()
|
||||
}
|
||||
|
||||
|
||||
14
src/lib.rs
14
src/lib.rs
@@ -39,36 +39,36 @@ pub mod contact;
|
||||
pub mod context;
|
||||
mod e2ee;
|
||||
mod imap;
|
||||
pub mod imex;
|
||||
pub mod job;
|
||||
mod job_thread;
|
||||
pub mod key;
|
||||
pub mod keyring;
|
||||
pub mod location;
|
||||
mod login_param;
|
||||
pub mod lot;
|
||||
pub mod message;
|
||||
mod mimefactory;
|
||||
pub mod oauth2;
|
||||
mod param;
|
||||
pub mod peerstate;
|
||||
pub mod pgp;
|
||||
pub mod qr;
|
||||
pub mod securejoin;
|
||||
mod smtp;
|
||||
pub mod sql;
|
||||
mod stock;
|
||||
mod token;
|
||||
#[macro_use]
|
||||
mod wrapmime;
|
||||
|
||||
pub mod dc_array;
|
||||
mod dc_dehtml;
|
||||
pub mod dc_imex;
|
||||
pub mod dc_mimeparser;
|
||||
pub mod dc_receive_imf;
|
||||
mod dc_simplify;
|
||||
mod dc_strencode;
|
||||
pub mod dc_tools;
|
||||
mod login_param;
|
||||
mod mimefactory;
|
||||
pub mod securejoin;
|
||||
mod token;
|
||||
#[macro_use]
|
||||
mod wrapmime;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
|
||||
@@ -413,7 +413,7 @@ pub fn handle_securejoin_handshake(
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
if mimeparser.e2ee_helper.encrypted {
|
||||
if mimeparser.encrypted {
|
||||
"No valid signature."
|
||||
} else {
|
||||
"Not encrypted."
|
||||
@@ -693,17 +693,16 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> Res
|
||||
******************************************************************************/
|
||||
|
||||
fn encrypted_and_signed(mimeparser: &MimeParser, expected_fingerprint: impl AsRef<str>) -> bool {
|
||||
if !mimeparser.e2ee_helper.encrypted {
|
||||
if !mimeparser.encrypted {
|
||||
warn!(mimeparser.context, "Message not encrypted.",);
|
||||
false
|
||||
} else if mimeparser.e2ee_helper.signatures.len() <= 0 {
|
||||
} else if mimeparser.signatures.len() <= 0 {
|
||||
warn!(mimeparser.context, "Message not signed.",);
|
||||
false
|
||||
} else if expected_fingerprint.as_ref().is_empty() {
|
||||
warn!(mimeparser.context, "Fingerprint for comparison missing.",);
|
||||
false
|
||||
} else if !mimeparser
|
||||
.e2ee_helper
|
||||
.signatures
|
||||
.contains(expected_fingerprint.as_ref())
|
||||
{
|
||||
@@ -718,21 +717,24 @@ fn encrypted_and_signed(mimeparser: &MimeParser, expected_fingerprint: impl AsRe
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_degrade_event(context: &Context, peerstate: &Peerstate) {
|
||||
pub fn handle_degrade_event(context: &Context, peerstate: &Peerstate) -> Result<(), Error> {
|
||||
// - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal
|
||||
// - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes
|
||||
// together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother
|
||||
// with things they cannot fix, so the user is just kicked from the verified group
|
||||
// (and he will know this and can fix this)
|
||||
if Some(DegradeEvent::FingerprintChanged) == peerstate.degrade_event {
|
||||
let contact_id: i32 = context
|
||||
.sql
|
||||
.query_get_value(
|
||||
context,
|
||||
"SELECT id FROM contacts WHERE addr=?;",
|
||||
params![&peerstate.addr],
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let contact_id: i32 = match context.sql.query_get_value(
|
||||
context,
|
||||
"SELECT id FROM contacts WHERE addr=?;",
|
||||
params![&peerstate.addr],
|
||||
) {
|
||||
None => bail!(
|
||||
"contact with peerstate.addr {:?} not found",
|
||||
&peerstate.addr
|
||||
),
|
||||
Some(contact_id) => contact_id,
|
||||
};
|
||||
if contact_id > 0 {
|
||||
let (contact_chat_id, _) =
|
||||
chat::create_or_lookup_by_contact_id(context, contact_id as u32, Blocked::Deaddrop)
|
||||
@@ -748,4 +750,5 @@ pub fn handle_degrade_event(context: &Context, peerstate: &Peerstate) {
|
||||
emit_event!(context, Event::ChatModified(contact_chat_id));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ pub fn configure_alice_keypair(ctx: &Context) -> String {
|
||||
KeyType::Private,
|
||||
)
|
||||
.unwrap();
|
||||
let saved = key::dc_key_save_self_keypair(&ctx, &public, &private, &addr, 1, &ctx.sql);
|
||||
let saved = key::dc_key_save_self_keypair(&ctx, &public, &private, &addr, true, &ctx.sql);
|
||||
assert_eq!(saved, true, "Failed to save Alice's key");
|
||||
addr
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ pub fn get_ct_subtype(mime: *mut Mailmime) -> Option<String> {
|
||||
let ct: *mut mailmime_content = (*mime).mm_content_type;
|
||||
|
||||
if !ct.is_null() && !(*ct).ct_subtype.is_null() {
|
||||
println!("ct_subtype: {}", to_string((*ct).ct_subtype));
|
||||
Some(to_string((*ct).ct_subtype))
|
||||
} else {
|
||||
None
|
||||
@@ -48,10 +47,12 @@ pub fn get_autocrypt_mime(
|
||||
mime_undetermined: *mut Mailmime,
|
||||
) -> Result<(*mut Mailmime, *mut Mailmime), Error> {
|
||||
/* return Result with two mime pointers:
|
||||
First mime pointer is to the multipart-mime message
|
||||
(which is replaced with a decrypted version later)
|
||||
Second one is to the encrypted payload.
|
||||
For non-autocrypt message an Error is returned.
|
||||
|
||||
First mime pointer is to the multipart-mime message
|
||||
(which is replaced with a decrypted version later)
|
||||
|
||||
Second one is to the encrypted payload.
|
||||
For non-autocrypt message an Error is returned.
|
||||
*/
|
||||
unsafe {
|
||||
ensure!(
|
||||
|
||||
125
test-data/message/stress.txt
Normal file
125
test-data/message/stress.txt
Normal file
@@ -0,0 +1,125 @@
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
Passphrase-Format: numeric9x4
|
||||
Passphrase-Begin: 17
|
||||
|
||||
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
|
||||
mngm1QCMMw2v/j8=
|
||||
=9sJE
|
||||
-----END PGP MESSAGE-----
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user