Compare commits

..

10 Commits

Author SHA1 Message Date
dignifiedquire
5c6df6dbf6 no more memory corruption 2019-09-29 17:41:04 -06:00
dignifiedquire
416a1751a2 things compile 2019-09-29 09:06:32 -06:00
dignifiedquire
475ad0f93a compile 2019-09-28 23:05:10 -06:00
dignifiedquire
625bc90c30 start refactor of fields 2019-09-28 16:30:22 -06:00
dignifiedquire
347a4d110a refactor(mmime): rustify address_list 2019-09-28 14:51:00 -06:00
dignifiedquire
50ca75398f refactor(mmime): rustify mailbox 2019-09-28 14:42:06 -06:00
dignifiedquire
c18bdc39e5 refactor(mmime): rustify mailbox_list 2019-09-28 14:35:33 -06:00
dignifiedquire
8aa1a0cb3a refactor(mmime): rustify mailimf_group 2019-09-28 14:15:55 -06:00
dignifiedquire
c78753e8fa refactor(mmime): rustify mailimf_data_time 2019-09-28 14:09:53 -06:00
dignifiedquire
2abfd037ca refactor(mmime): rustify mailmime_address 2019-09-28 13:31:12 -06:00
56 changed files with 3487 additions and 5834 deletions

View File

@@ -3719,14 +3719,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
#define DC_MSG_GIF 21
/**
* Message containing a sticker, similar to image.
* If possible, the ui should display the image without borders in a transparent way.
* A click on a sticker will offer to install the sticker set in some future.
*/
#define DC_MSG_STICKER 23
/**
* Message containing an Audio file.
* File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
@@ -3904,45 +3896,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*/
#define DC_EVENT_SMTP_MESSAGE_SENT 103
/**
* Emitted when a message was successfully marked as deleted on the IMAP server.
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_IMAP_MESSAGE_DELETED 104
/**
* Emitted when a message was successfully moved on IMAP.
*
* @param data1 0
* @param data2 (const char*) Info string in english language.
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_IMAP_MESSAGE_MOVED 105
/**
* Emitted when a new blob file was successfully written
*
* @param data1 0
* @param data2 (const char*) path name
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_NEW_BLOB_FILE 150
/**
* Emitted when a blob file was successfully deleted
*
* @param data1 0
* @param data2 (const char*) path name
* Must not be free()'d or modified and is valid only until the callback returns.
* @return 0
*/
#define DC_EVENT_DELETED_BLOB_FILE 151
/**
* The library-user should write a warning string to the log.

View File

@@ -24,7 +24,9 @@ use num_traits::{FromPrimitive, ToPrimitive};
use deltachat::contact::Contact;
use deltachat::context::Context;
use deltachat::dc_tools::{as_path, as_str, dc_strdup, to_string_lossy, OsStrExt, StrExt};
use deltachat::dc_tools::{
as_path, as_str, dc_strdup, to_string, to_string_lossy, OsStrExt, StrExt,
};
use deltachat::*;
// as C lacks a good and portable error handling,
@@ -123,15 +125,11 @@ impl ContextWrapper {
| Event::SmtpConnected(msg)
| Event::ImapConnected(msg)
| Event::SmtpMessageSent(msg)
| Event::ImapMessageDeleted(msg)
| Event::ImapMessageMoved(msg)
| Event::NewBlobFile(msg)
| Event::DeletedBlobFile(msg)
| Event::Warning(msg)
| Event::Error(msg)
| Event::ErrorNetwork(msg)
| Event::ErrorSelfNotInGroup(msg) => {
let data2 = CString::new(msg).unwrap_or_default();
let data2 = CString::new(msg).unwrap();
ffi_cb(self, event_id, 0, data2.as_ptr() as uintptr_t)
}
Event::MsgsChanged { chat_id, msg_id }
@@ -150,7 +148,7 @@ impl ContextWrapper {
ffi_cb(self, event_id, progress as uintptr_t, 0)
}
Event::ImexFileWritten(file) => {
let data1 = file.to_c_string().unwrap_or_default();
let data1 = file.to_c_string().unwrap();
ffi_cb(self, event_id, data1.as_ptr() as uintptr_t, 0)
}
Event::SecurejoinInviterProgress {
@@ -379,8 +377,8 @@ pub unsafe extern "C" fn dc_get_oauth2_url(
return ptr::null_mut(); // NULL explicitly defined as "unknown"
}
let ffi_context = &*context;
let addr = to_string_lossy(addr);
let redirect = to_string_lossy(redirect);
let addr = to_string(addr);
let redirect = to_string(redirect);
ffi_context
.with_inner(|ctx| match oauth2::dc_get_oauth2_url(ctx, addr, redirect) {
Some(res) => res.strdup(),
@@ -1247,11 +1245,8 @@ pub unsafe extern "C" fn dc_forward_msgs(
let ffi_context = &*context;
ffi_context
.with_inner(|ctx| {
chat::forward_msgs(ctx, ids, chat_id)
.unwrap_or_log_default(ctx, "Failed to forward message")
})
.unwrap_or_default()
.with_inner(|ctx| chat::forward_msgs(ctx, ids, chat_id))
.unwrap_or(())
}
#[no_mangle]
@@ -2136,7 +2131,7 @@ pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut
let ffi_context = &*ffi_chat.context;
ffi_context
.with_inner(|ctx| match ffi_chat.chat.get_profile_image(ctx) {
Some(p) => p.to_str().unwrap_or_default().to_string().strdup(),
Some(p) => p.to_str().unwrap().to_string().strdup(),
None => ptr::null_mut(),
})
.unwrap_or_else(|_| ptr::null_mut())
@@ -2481,7 +2476,7 @@ pub unsafe extern "C" fn dc_msg_get_summarytext(
.with_inner(|ctx| {
ffi_msg
.message
.get_summarytext(ctx, approx_characters.try_into().unwrap_or_default())
.get_summarytext(ctx, approx_characters.try_into().unwrap())
})
.unwrap_or_default()
.strdup()
@@ -2773,7 +2768,7 @@ pub unsafe extern "C" fn dc_contact_get_profile_image(
ffi_contact
.contact
.get_profile_image(ctx)
.map(|p| p.to_str().unwrap_or_default().to_string().strdup())
.map(|p| p.to_str().unwrap().to_string().strdup())
.unwrap_or_else(|| std::ptr::null_mut())
})
.unwrap_or_else(|_| ptr::null_mut())

View File

@@ -119,13 +119,13 @@ fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int {
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
if !spec.is_null() {
real_spec = to_string_lossy(spec);
real_spec = to_string(spec);
context
.sql
.set_raw_config(context, "import_spec", Some(&real_spec))
.set_config(context, "import_spec", Some(&real_spec))
.unwrap();
} else {
let rs = context.sql.get_raw_config(context, "import_spec");
let rs = context.sql.get_config(context, "import_spec");
if rs.is_none() {
error!(context, "Import: No file or folder given.");
return 0;
@@ -864,7 +864,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let mut msg_ids = [0; 1];
let chat_id = arg2.parse()?;
msg_ids[0] = arg1.parse()?;
chat::forward_msgs(context, &msg_ids, chat_id)?;
chat::forward_msgs(context, &msg_ids, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");

View File

@@ -16,16 +16,13 @@ pub unsafe fn charconv(
) -> libc::c_int {
assert!(!fromcode.is_null(), "invalid fromcode");
assert!(!s.is_null(), "invalid input string");
if let Some(encoding) = charset::Charset::for_label(
CStr::from_ptr(fromcode)
.to_str()
.unwrap_or_default()
.as_bytes(),
) {
if let Some(encoding) =
charset::Charset::for_label(CStr::from_ptr(fromcode).to_str().unwrap().as_bytes())
{
let data = std::slice::from_raw_parts(s as *const u8, strlen(s));
let (res, _, _) = encoding.decode(data);
let res_c = CString::new(res.as_bytes()).unwrap_or_default();
let res_c = CString::new(res.as_bytes()).unwrap();
*result = strdup(res_c.as_ptr()) as *mut _;
MAIL_CHARCONV_NO_ERROR as libc::c_int

View File

@@ -50,10 +50,7 @@ pub unsafe fn display_mime(mut mime: *mut Mailmime) {
}
MAILMIME_MESSAGE => {
if !(*mime).mm_data.mm_message.mm_fields.is_null() {
if !(*(*(*mime).mm_data.mm_message.mm_fields).fld_list)
.first
.is_null()
{
if !(*(*mime).mm_data.mm_message.mm_fields).0.is_empty() {
println!("headers begin");
display_fields((*mime).mm_data.mm_message.mm_fields);
println!("headers end");
@@ -123,7 +120,7 @@ unsafe fn display_mime_discrete_type(mut discrete_type: *mut mailmime_discrete_t
_ => {}
};
}
pub unsafe fn display_mime_data(mut data: *mut mailmime_data) {
unsafe fn display_mime_data(mut data: *mut mailmime_data) {
match (*data).dt_type {
0 => {
println!(
@@ -207,115 +204,66 @@ unsafe fn display_mime_fields(mut fields: *mut mailmime_fields) {
unsafe fn display_date_time(mut d: *mut mailimf_date_time) {
print!(
"{:02}/{:02}/{:02} {:02}:{:02}:{:02} +{:04}",
(*d).dt_day,
(*d).dt_month,
(*d).dt_year,
(*d).dt_hour,
(*d).dt_min,
(*d).dt_sec,
(*d).dt_zone,
(*d).day,
(*d).month,
(*d).year,
(*d).hour,
(*d).min,
(*d).sec,
(*d).zone,
);
}
unsafe fn display_orig_date(mut orig_date: *mut mailimf_orig_date) {
display_date_time((*orig_date).dt_date_time);
}
unsafe fn display_mailbox(mut mb: *mut mailimf_mailbox) {
if !(*mb).mb_display_name.is_null() {
print!(
"{}",
CStr::from_ptr((*mb).mb_display_name).to_str().unwrap()
);
if !(*mb).display_name.is_null() {
print!("{}", CStr::from_ptr((*mb).display_name).to_str().unwrap());
}
print!("<{}>", CStr::from_ptr((*mb).mb_addr_spec).to_str().unwrap());
print!("<{}>", CStr::from_ptr((*mb).addr_spec).to_str().unwrap());
}
unsafe fn display_mailbox_list(mut mb_list: *mut mailimf_mailbox_list) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*mb_list).mb_list).first;
while !cur.is_null() {
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
mb = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_mailbox;
display_mailbox(mb);
if !if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
.is_null()
{
for (i, mb) in (*mb_list).0.iter().enumerate() {
display_mailbox(*mb);
if i < (*mb_list).0.len() - 1 {
print!(", ");
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
unsafe fn display_group(mut group: *mut mailimf_group) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
print!(
"{}: ",
CStr::from_ptr((*group).grp_display_name).to_str().unwrap()
CStr::from_ptr((*group).display_name).to_str().unwrap()
);
cur = (*(*(*group).grp_mb_list).mb_list).first;
while !cur.is_null() {
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
mb = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_mailbox;
display_mailbox(mb);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
for mb in &(*(*group).mb_list).0 {
display_mailbox(*mb);
}
print!("; ");
}
unsafe fn display_address(mut a: *mut mailimf_address) {
match (*a).ad_type {
2 => {
display_group((*a).ad_data.ad_group);
unsafe fn display_address(a: *mut mailimf_address) {
match *a {
mailimf_address::Group(data) => {
display_group(data);
}
1 => {
display_mailbox((*a).ad_data.ad_mailbox);
}
_ => {}
};
}
unsafe fn display_address_list(mut addr_list: *mut mailimf_address_list) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*addr_list).ad_list).first;
while !cur.is_null() {
let mut addr: *mut mailimf_address = 0 as *mut mailimf_address;
addr = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_address;
display_address(addr);
if !if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
.is_null()
{
print!(", ");
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
mailimf_address::Mailbox(data) => {
display_mailbox(data);
}
}
}
unsafe fn display_address_list(addr_list: *mut mailimf_address_list) {
for (i, addr) in (*addr_list).0.iter().enumerate() {
display_address(*addr);
if i < (*addr_list).0.len() - 1 {
print!(", ");
}
}
}
unsafe fn display_from(mut from: *mut mailimf_from) {
display_mailbox_list((*from).frm_mb_list);
}
@@ -328,59 +276,44 @@ unsafe fn display_cc(mut cc: *mut mailimf_cc) {
unsafe fn display_subject(mut subject: *mut mailimf_subject) {
print!("{}", CStr::from_ptr((*subject).sbj_value).to_str().unwrap());
}
unsafe fn display_field(mut field: *mut mailimf_field) {
match (*field).fld_type {
9 => {
unsafe fn display_field(field: &mailimf_field) {
match *field {
mailimf_field::OrigDate(date) => {
print!("Date: ");
display_orig_date((*field).fld_data.fld_orig_date);
display_orig_date(date);
println!("");
}
10 => {
mailimf_field::From(from) => {
print!("From: ");
display_from((*field).fld_data.fld_from);
display_from(from);
println!("");
}
13 => {
mailimf_field::To(to) => {
print!("To: ");
display_to((*field).fld_data.fld_to);
display_to(to);
println!("");
}
14 => {
mailimf_field::Cc(cc) => {
print!("Cc: ");
display_cc((*field).fld_data.fld_cc);
display_cc(cc);
println!("");
}
19 => {
mailimf_field::Subject(subject) => {
print!("Subject: ");
display_subject((*field).fld_data.fld_subject);
display_subject(subject);
println!("");
}
16 => {
mailimf_field::MessageId(message_id) => {
println!(
"Message-ID: {}",
CStr::from_ptr((*(*field).fld_data.fld_message_id).mid_value)
.to_str()
.unwrap(),
CStr::from_ptr((*message_id).mid_value).to_str().unwrap(),
);
}
_ => {}
};
}
unsafe fn display_fields(mut fields: *mut mailimf_fields) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*fields).fld_list).first;
while !cur.is_null() {
let mut f: *mut mailimf_field = 0 as *mut mailimf_field;
f = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_field;
unsafe fn display_fields(fields: *mut mailimf_fields) {
for f in &(*fields).0 {
display_field(f);
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,20 @@
use crate::clist::*;
use crate::mailimf::types::*;
use crate::other::*;
/*
this function creates a new mailimf_fields structure with no fields
*/
pub unsafe fn mailimf_fields_new_empty() -> *mut mailimf_fields {
let mut list: *mut clist = 0 as *mut clist;
let mut fields_list: *mut mailimf_fields = 0 as *mut mailimf_fields;
list = clist_new();
if list.is_null() {
return 0 as *mut mailimf_fields;
}
fields_list = mailimf_fields_new(list);
if fields_list.is_null() {
return 0 as *mut mailimf_fields;
}
return fields_list;
mailimf_fields_new(Vec::new())
}
/*
this function adds a field to the mailimf_fields structure
@return MAILIMF_NO_ERROR will be returned on success,
other code will be returned otherwise
*/
pub unsafe fn mailimf_fields_add(
mut fields: *mut mailimf_fields,
mut field: *mut mailimf_field,
) -> libc::c_int {
let mut r: libc::c_int = 0;
r = clist_insert_after(
(*fields).fld_list,
(*(*fields).fld_list).last,
field as *mut libc::c_void,
);
if r < 0i32 {
return MAILIMF_ERROR_MEMORY as libc::c_int;
}
return MAILIMF_NO_ERROR as libc::c_int;
pub unsafe fn mailimf_fields_add(fields: *mut mailimf_fields, field: mailimf_field) {
(*fields).0.push(field)
}
/*
@@ -47,43 +24,11 @@ pub unsafe fn mailimf_fields_add(
@param value should be allocated with malloc()
*/
pub unsafe fn mailimf_field_new_custom(
mut name: *mut libc::c_char,
mut value: *mut libc::c_char,
) -> *mut mailimf_field {
let mut opt_field: *mut mailimf_optional_field = 0 as *mut mailimf_optional_field;
let mut field: *mut mailimf_field = 0 as *mut mailimf_field;
opt_field = mailimf_optional_field_new(name, value);
if !opt_field.is_null() {
field = mailimf_field_new(
MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int,
0 as *mut mailimf_return,
0 as *mut mailimf_orig_date,
0 as *mut mailimf_from,
0 as *mut mailimf_sender,
0 as *mut mailimf_to,
0 as *mut mailimf_cc,
0 as *mut mailimf_bcc,
0 as *mut mailimf_message_id,
0 as *mut mailimf_orig_date,
0 as *mut mailimf_from,
0 as *mut mailimf_sender,
0 as *mut mailimf_reply_to,
0 as *mut mailimf_to,
0 as *mut mailimf_cc,
0 as *mut mailimf_bcc,
0 as *mut mailimf_message_id,
0 as *mut mailimf_in_reply_to,
0 as *mut mailimf_references,
0 as *mut mailimf_subject,
0 as *mut mailimf_comments,
0 as *mut mailimf_keywords,
opt_field,
);
if field.is_null() {
mailimf_optional_field_free(opt_field);
} else {
return field;
}
}
return 0 as *mut mailimf_field;
name: *mut libc::c_char,
value: *mut libc::c_char,
) -> mailimf_field {
let opt_field = mailimf_optional_field_new(name, value);
assert!(!opt_field.is_null(), "failed memory allocation");
mailimf_field::OptionalField(opt_field)
}

View File

@@ -166,35 +166,19 @@ pub unsafe fn mailimf_fields_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
>,
mut data: *mut libc::c_void,
mut col: *mut libc::c_int,
mut fields: *mut mailimf_fields,
data: *mut libc::c_void,
col: *mut libc::c_int,
fields: *mut mailimf_fields,
) -> libc::c_int {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*fields).fld_list).first;
while !cur.is_null() {
let mut r: libc::c_int = 0;
r = mailimf_field_write_driver(
do_write,
data,
col,
(if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_field,
);
for cur in &(*fields).0 {
let r = mailimf_field_write_driver(do_write, data, col, cur);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
return MAILIMF_NO_ERROR as libc::c_int;
MAILIMF_NO_ERROR as libc::c_int
}
/*
mailimf_field_write writes a field to a given stream
@@ -207,118 +191,38 @@ pub unsafe fn mailimf_field_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
>,
mut data: *mut libc::c_void,
mut col: *mut libc::c_int,
mut field: *mut mailimf_field,
data: *mut libc::c_void,
col: *mut libc::c_int,
field: &mailimf_field,
) -> libc::c_int {
let mut r: libc::c_int = 0;
match (*field).fld_type {
1 => {
r = mailimf_return_write_driver(do_write, data, col, (*field).fld_data.fld_return_path)
}
2 => {
r = mailimf_resent_date_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_resent_date,
)
}
3 => {
r = mailimf_resent_from_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_resent_from,
)
}
4 => {
r = mailimf_resent_sender_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_resent_sender,
)
}
5 => {
r = mailimf_resent_to_write_driver(do_write, data, col, (*field).fld_data.fld_resent_to)
}
6 => {
r = mailimf_resent_cc_write_driver(do_write, data, col, (*field).fld_data.fld_resent_cc)
}
7 => {
r = mailimf_resent_bcc_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_resent_bcc,
)
}
8 => {
r = mailimf_resent_msg_id_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_resent_msg_id,
)
}
9 => {
r = mailimf_orig_date_write_driver(do_write, data, col, (*field).fld_data.fld_orig_date)
}
10 => r = mailimf_from_write_driver(do_write, data, col, (*field).fld_data.fld_from),
11 => r = mailimf_sender_write_driver(do_write, data, col, (*field).fld_data.fld_sender),
12 => {
r = mailimf_reply_to_write_driver(do_write, data, col, (*field).fld_data.fld_reply_to)
}
13 => r = mailimf_to_write_driver(do_write, data, col, (*field).fld_data.fld_to),
14 => r = mailimf_cc_write_driver(do_write, data, col, (*field).fld_data.fld_cc),
15 => r = mailimf_bcc_write_driver(do_write, data, col, (*field).fld_data.fld_bcc),
16 => {
r = mailimf_message_id_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_message_id,
)
}
17 => {
r = mailimf_in_reply_to_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_in_reply_to,
)
}
18 => {
r = mailimf_references_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_references,
)
}
19 => r = mailimf_subject_write_driver(do_write, data, col, (*field).fld_data.fld_subject),
20 => {
r = mailimf_comments_write_driver(do_write, data, col, (*field).fld_data.fld_comments)
}
21 => {
r = mailimf_keywords_write_driver(do_write, data, col, (*field).fld_data.fld_keywords)
}
22 => {
r = mailimf_optional_field_write_driver(
do_write,
data,
col,
(*field).fld_data.fld_optional_field,
)
}
_ => r = MAILIMF_ERROR_INVAL as libc::c_int,
use mailimf_field::*;
match field {
ReturnPath(path) => mailimf_return_write_driver(do_write, data, col, *path),
ResentDate(date) => mailimf_resent_date_write_driver(do_write, data, col, *date),
ResentFrom(from) => mailimf_resent_from_write_driver(do_write, data, col, *from),
ResentSender(sender) => mailimf_resent_sender_write_driver(do_write, data, col, *sender),
ResentTo(to) => mailimf_resent_to_write_driver(do_write, data, col, *to),
ResentCc(cc) => mailimf_resent_cc_write_driver(do_write, data, col, *cc),
ResentBcc(bcc) => mailimf_resent_bcc_write_driver(do_write, data, col, *bcc),
ResentMsgId(id) => mailimf_resent_msg_id_write_driver(do_write, data, col, *id),
OrigDate(date) => mailimf_orig_date_write_driver(do_write, data, col, *date),
From(from) => mailimf_from_write_driver(do_write, data, col, *from),
Sender(sender) => mailimf_sender_write_driver(do_write, data, col, *sender),
ReplyTo(to) => mailimf_reply_to_write_driver(do_write, data, col, *to),
To(to) => mailimf_to_write_driver(do_write, data, col, *to),
Cc(cc) => mailimf_cc_write_driver(do_write, data, col, *cc),
Bcc(bcc) => mailimf_bcc_write_driver(do_write, data, col, *bcc),
MessageId(id) => mailimf_message_id_write_driver(do_write, data, col, *id),
InReplyTo(to) => mailimf_in_reply_to_write_driver(do_write, data, col, *to),
References(refs) => mailimf_references_write_driver(do_write, data, col, *refs),
Subject(s) => mailimf_subject_write_driver(do_write, data, col, *s),
Comments(c) => mailimf_comments_write_driver(do_write, data, col, *c),
Keywords(k) => mailimf_keywords_write_driver(do_write, data, col, *k),
OptionalField(f) => mailimf_optional_field_write_driver(do_write, data, col, *f),
}
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn mailimf_optional_field_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
@@ -328,16 +232,10 @@ unsafe fn mailimf_optional_field_write_driver(
mut field: *mut mailimf_optional_field,
) -> libc::c_int {
let mut r: libc::c_int = 0;
if strlen((*field).fld_name).wrapping_add(2i32 as libc::size_t) > 998i32 as libc::size_t {
if strlen((*field).name).wrapping_add(2i32 as libc::size_t) > 998i32 as libc::size_t {
return MAILIMF_ERROR_INVAL as libc::c_int;
}
r = mailimf_string_write_driver(
do_write,
data,
col,
(*field).fld_name,
strlen((*field).fld_name),
);
r = mailimf_string_write_driver(do_write, data, col, (*field).name, strlen((*field).name));
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
@@ -355,8 +253,8 @@ unsafe fn mailimf_optional_field_write_driver(
do_write,
data,
col,
(*field).fld_value,
strlen((*field).fld_value),
(*field).value,
strlen((*field).value),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
@@ -684,7 +582,7 @@ unsafe fn mailimf_references_write_driver(
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_msg_id_list_write_driver(do_write, data, col, (*references).mid_list);
r = mailimf_msg_id_list_write_driver(do_write, data, col, &(*references).0);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
@@ -701,28 +599,19 @@ unsafe fn mailimf_references_write_driver(
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn mailimf_msg_id_list_write_driver(
mut do_write: Option<
do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
>,
mut data: *mut libc::c_void,
mut col: *mut libc::c_int,
mut mid_list: *mut clist,
data: *mut libc::c_void,
col: *mut libc::c_int,
mid_list: &Vec<*mut libc::c_char>,
) -> libc::c_int {
let mut cur: *mut clistiter = 0 as *mut clistiter;
let mut r: libc::c_int = 0;
let mut first: libc::c_int = 0;
first = 1i32;
cur = (*mid_list).first;
while !cur.is_null() {
let mut msgid: *mut libc::c_char = 0 as *mut libc::c_char;
let mut len: size_t = 0;
msgid = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut libc::c_char;
len = strlen(msgid);
if 0 == first {
let mut r = 0;
let mut first = true;
for msgid in mid_list {
let len = strlen(*msgid);
if !first {
if *col > 1i32 {
if (*col as libc::size_t).wrapping_add(len) >= 72i32 as libc::size_t {
r = mailimf_string_write_driver(
@@ -735,11 +624,11 @@ unsafe fn mailimf_msg_id_list_write_driver(
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
first = 1i32
first = true;
}
}
}
if 0 == first {
if !first {
r = mailimf_string_write_driver(
do_write,
data,
@@ -751,7 +640,7 @@ unsafe fn mailimf_msg_id_list_write_driver(
return r;
}
} else {
first = 0i32
first = false;
}
r = mailimf_string_write_driver(
do_write,
@@ -763,7 +652,7 @@ unsafe fn mailimf_msg_id_list_write_driver(
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_string_write_driver(do_write, data, col, msgid, len);
r = mailimf_string_write_driver(do_write, data, col, *msgid, len);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
@@ -777,14 +666,11 @@ unsafe fn mailimf_msg_id_list_write_driver(
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
return MAILIMF_NO_ERROR as libc::c_int;
MAILIMF_NO_ERROR as libc::c_int
}
unsafe fn mailimf_in_reply_to_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
@@ -804,7 +690,7 @@ unsafe fn mailimf_in_reply_to_write_driver(
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
r = mailimf_msg_id_list_write_driver(do_write, data, col, (*in_reply_to).mid_list);
r = mailimf_msg_id_list_write_driver(do_write, data, col, &(*in_reply_to).0);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
@@ -930,14 +816,8 @@ pub unsafe fn mailimf_address_list_write_driver(
let mut r: libc::c_int = 0;
let mut first: libc::c_int = 0;
first = 1i32;
cur = (*(*addr_list).ad_list).first;
while !cur.is_null() {
let mut addr: *mut mailimf_address = 0 as *mut mailimf_address;
addr = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_address;
for addr in &(*addr_list).0 {
if 0 == first {
r = mailimf_string_write_driver(
do_write,
@@ -952,18 +832,14 @@ pub unsafe fn mailimf_address_list_write_driver(
} else {
first = 0i32
}
r = mailimf_address_write_driver(do_write, data, col, addr);
r = mailimf_address_write_driver(do_write, data, col, *addr);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
return MAILIMF_NO_ERROR as libc::c_int;
MAILIMF_NO_ERROR as libc::c_int
}
unsafe fn mailimf_address_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
@@ -972,24 +848,24 @@ unsafe fn mailimf_address_write_driver(
mut col: *mut libc::c_int,
mut addr: *mut mailimf_address,
) -> libc::c_int {
let mut r: libc::c_int = 0;
match (*addr).ad_type {
1 => {
r = mailimf_mailbox_write_driver(do_write, data, col, (*addr).ad_data.ad_mailbox);
match *addr {
mailimf_address::Mailbox(mb_data) => {
let r = mailimf_mailbox_write_driver(do_write, data, col, mb_data);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
}
2 => {
r = mailimf_group_write_driver(do_write, data, col, (*addr).ad_data.ad_group);
mailimf_address::Group(gr_data) => {
let r = mailimf_group_write_driver(do_write, data, col, gr_data);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
}
_ => {}
}
return MAILIMF_NO_ERROR as libc::c_int;
MAILIMF_NO_ERROR as libc::c_int
}
unsafe fn mailimf_group_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
@@ -1003,8 +879,8 @@ unsafe fn mailimf_group_write_driver(
do_write,
data,
col,
(*group).grp_display_name,
strlen((*group).grp_display_name),
(*group).display_name,
strlen((*group).display_name),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
@@ -1019,8 +895,8 @@ unsafe fn mailimf_group_write_driver(
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
if !(*group).grp_mb_list.is_null() {
r = mailimf_mailbox_list_write_driver(do_write, data, col, (*group).grp_mb_list);
if !(*group).mb_list.is_null() {
r = mailimf_mailbox_list_write_driver(do_write, data, col, (*group).mb_list);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
@@ -1050,14 +926,8 @@ pub unsafe fn mailimf_mailbox_list_write_driver(
let mut r: libc::c_int = 0;
let mut first: libc::c_int = 0;
first = 1i32;
cur = (*(*mb_list).mb_list).first;
while !cur.is_null() {
let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox;
mb = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_mailbox;
for mb in &(*mb_list).0 {
if 0 == first {
r = mailimf_string_write_driver(
do_write,
@@ -1072,18 +942,14 @@ pub unsafe fn mailimf_mailbox_list_write_driver(
} else {
first = 0i32
}
r = mailimf_mailbox_write_driver(do_write, data, col, mb);
r = mailimf_mailbox_write_driver(do_write, data, col, *mb);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
return MAILIMF_NO_ERROR as libc::c_int;
MAILIMF_NO_ERROR as libc::c_int
}
unsafe fn mailimf_mailbox_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,
@@ -1094,21 +960,21 @@ unsafe fn mailimf_mailbox_write_driver(
) -> libc::c_int {
let mut r: libc::c_int = 0;
let mut do_fold: libc::c_int = 0;
if !(*mb).mb_display_name.is_null() {
if 0 != is_atext((*mb).mb_display_name) {
if !(*mb).display_name.is_null() {
if 0 != is_atext((*mb).display_name) {
r = mailimf_header_string_write_driver(
do_write,
data,
col,
(*mb).mb_display_name,
strlen((*mb).mb_display_name),
(*mb).display_name,
strlen((*mb).display_name),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
}
} else {
if !(*mb).mb_display_name.is_null() {
if (*col as libc::size_t).wrapping_add(strlen((*mb).mb_display_name))
if !(*mb).display_name.is_null() {
if (*col as libc::size_t).wrapping_add(strlen((*mb).display_name))
>= 72i32 as libc::size_t
{
r = mailimf_string_write_driver(
@@ -1123,15 +989,15 @@ unsafe fn mailimf_mailbox_write_driver(
}
}
}
if strlen((*mb).mb_display_name) > (998i32 / 2i32) as libc::size_t {
if strlen((*mb).display_name) > (998i32 / 2i32) as libc::size_t {
return MAILIMF_ERROR_INVAL as libc::c_int;
}
r = mailimf_quoted_string_write_driver(
do_write,
data,
col,
(*mb).mb_display_name,
strlen((*mb).mb_display_name),
(*mb).display_name,
strlen((*mb).display_name),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
@@ -1140,7 +1006,7 @@ unsafe fn mailimf_mailbox_write_driver(
do_fold = 0i32;
if *col > 1i32 {
if (*col as libc::size_t)
.wrapping_add(strlen((*mb).mb_addr_spec))
.wrapping_add(strlen((*mb).addr_spec))
.wrapping_add(3i32 as libc::size_t)
>= 72i32 as libc::size_t
{
@@ -1181,8 +1047,8 @@ unsafe fn mailimf_mailbox_write_driver(
do_write,
data,
col,
(*mb).mb_addr_spec,
strlen((*mb).mb_addr_spec),
(*mb).addr_spec,
strlen((*mb).addr_spec),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
@@ -1198,8 +1064,7 @@ unsafe fn mailimf_mailbox_write_driver(
return r;
}
} else {
if (*col as libc::size_t).wrapping_add(strlen((*mb).mb_addr_spec)) >= 72i32 as libc::size_t
{
if (*col as libc::size_t).wrapping_add(strlen((*mb).addr_spec)) >= 72i32 as libc::size_t {
r = mailimf_string_write_driver(
do_write,
data,
@@ -1215,8 +1080,8 @@ unsafe fn mailimf_mailbox_write_driver(
do_write,
data,
col,
(*mb).mb_addr_spec,
strlen((*mb).mb_addr_spec),
(*mb).addr_spec,
strlen((*mb).addr_spec),
);
if r != MAILIMF_NO_ERROR as libc::c_int {
return r;
@@ -1581,24 +1446,20 @@ unsafe fn mailimf_date_time_write_driver(
mut col: *mut libc::c_int,
mut date_time: *mut mailimf_date_time,
) -> libc::c_int {
let wday = dayofweek(
(*date_time).dt_year,
(*date_time).dt_month,
(*date_time).dt_day,
);
let wday = dayofweek((*date_time).year, (*date_time).month, (*date_time).day);
let date_str = format!(
"{}, {} {} {} {:02}:{:02}:{:02} {:+05}",
week_of_day_str[wday as usize],
(*date_time).dt_day,
month_str[((*date_time).dt_month - 1i32) as usize],
(*date_time).dt_year,
(*date_time).dt_hour,
(*date_time).dt_min,
(*date_time).dt_sec,
(*date_time).dt_zone,
wday,
(*date_time).day,
month_str[((*date_time).month - 1) as usize],
(*date_time).year,
(*date_time).hour,
(*date_time).min,
(*date_time).sec,
(*date_time).zone,
);
let date_str_c = std::ffi::CString::new(date_str).unwrap_or_default();
let date_str_c = std::ffi::CString::new(date_str).unwrap();
let r = mailimf_string_write_driver(
do_write,
data,
@@ -1611,27 +1472,16 @@ unsafe fn mailimf_date_time_write_driver(
}
return MAILIMF_NO_ERROR as libc::c_int;
}
static mut month_str: [&'static str; 12] = [
static mut month_str: [&str; 12] = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
static mut week_of_day_str: [&'static str; 7] = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
/* 0 = Sunday */
/* y > 1752 */
unsafe fn dayofweek(
mut year: libc::c_int,
mut month: libc::c_int,
mut day: libc::c_int,
) -> libc::c_int {
static mut offset: [libc::c_int; 12] = [
0i32, 3i32, 2i32, 5i32, 0i32, 3i32, 5i32, 1i32, 4i32, 6i32, 2i32, 4i32,
];
year -= (month < 3i32) as libc::c_int;
return (year + year / 4i32 - year / 100i32
+ year / 400i32
+ offset[(month - 1i32) as usize]
+ day)
% 7i32;
fn dayofweek(year: i32, month: u32, day: u32) -> String {
chrono::NaiveDate::from_ymd(year, month, day)
.format("%a")
.to_string()
}
unsafe fn mailimf_resent_msg_id_write_driver(
mut do_write: Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: size_t) -> libc::c_int,

View File

@@ -1243,30 +1243,21 @@ unsafe fn mailmime_preamble_parse(
*indx = cur_token;
return MAILIMF_NO_ERROR as libc::c_int;
}
unsafe fn remove_unparsed_mime_headers(mut fields: *mut mailimf_fields) {
let mut cur: *mut clistiter = 0 as *mut clistiter;
cur = (*(*fields).fld_list).first;
while !cur.is_null() {
let mut field: *mut mailimf_field = 0 as *mut mailimf_field;
let mut delete: libc::c_int = 0;
field = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_field;
match (*field).fld_type {
22 => {
delete = 0i32;
unsafe fn remove_unparsed_mime_headers(fields: *mut mailimf_fields) {
(*fields).0.retain(|field| {
let mut delete = false;
match field {
mailimf_field::OptionalField(data) => {
delete = false;
if strncasecmp(
(*(*field).fld_data.fld_optional_field).fld_name,
(**data).name,
b"Content-\x00" as *const u8 as *const libc::c_char,
8i32 as libc::size_t,
) == 0i32
{
let mut name: *mut libc::c_char = 0 as *mut libc::c_char;
name = (*(*field).fld_data.fld_optional_field)
.fld_name
.offset(8isize);
name = (**data).name.offset(8isize);
if strcasecmp(name, b"Type\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcasecmp(
name,
@@ -1280,35 +1271,22 @@ unsafe fn remove_unparsed_mime_headers(mut fields: *mut mailimf_fields) {
|| strcasecmp(name, b"Language\x00" as *const u8 as *const libc::c_char)
== 0i32
{
delete = 1i32
delete = true;
}
} else if strcasecmp(
(*(*field).fld_data.fld_optional_field).fld_name,
(**data).name,
b"MIME-Version\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
delete = 1i32
}
if 0 != delete {
cur = clist_delete((*fields).fld_list, cur);
mailimf_field_free(field);
} else {
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
}
_ => {
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
delete = true;
}
}
_ => {}
}
}
// retain keeps everything true
!delete
});
}
pub unsafe fn mailmime_extract_boundary(

View File

@@ -684,8 +684,8 @@ unsafe fn mailmime_mechanism_parse(
}
pub unsafe fn mailmime_field_parse(
mut field: *mut mailimf_optional_field,
mut result: *mut *mut mailmime_field,
field: *mut mailimf_optional_field,
result: *mut *mut mailmime_field,
) -> libc::c_int {
let mut name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut value: *mut libc::c_char = 0 as *mut libc::c_char;
@@ -702,8 +702,8 @@ pub unsafe fn mailmime_field_parse(
let mut location: *mut libc::c_char = 0 as *mut libc::c_char;
let mut res: libc::c_int = 0;
let mut r: libc::c_int = 0;
name = (*field).fld_name;
value = (*field).fld_value;
name = (*field).name;
value = (*field).value;
cur_token = 0i32 as size_t;
content = 0 as *mut mailmime_content;
encoding = 0 as *mut mailmime_mechanism;
@@ -1068,8 +1068,6 @@ pub unsafe fn mailmime_fields_parse(
mut fields: *mut mailimf_fields,
mut result: *mut *mut mailmime_fields,
) -> libc::c_int {
let mut current_block: u64;
let mut cur: *mut clistiter = 0 as *mut clistiter;
let mut mime_fields: *mut mailmime_fields = 0 as *mut mailmime_fields;
let mut list: *mut clist = 0 as *mut clist;
let mut r: libc::c_int = 0;
@@ -1078,58 +1076,27 @@ pub unsafe fn mailmime_fields_parse(
if list.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
cur = (*(*fields).fld_list).first;
loop {
if cur.is_null() {
current_block = 1109700713171191020;
break;
}
let mut field: *mut mailimf_field = 0 as *mut mailimf_field;
let mut mime_field: *mut mailmime_field = 0 as *mut mailmime_field;
field = (if !cur.is_null() {
(*cur).data
} else {
0 as *mut libc::c_void
}) as *mut mailimf_field;
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
r = mailmime_field_parse((*field).fld_data.fld_optional_field, &mut mime_field);
for field in &(*fields).0 {
let mut mime_field = 0 as *mut mailmime_field;
if let mailimf_field::OptionalField(opt_field) = field {
r = mailmime_field_parse(*opt_field, &mut mime_field);
if r == MAILIMF_NO_ERROR as libc::c_int {
r = clist_insert_after(list, (*list).last, mime_field as *mut libc::c_void);
if r < 0i32 {
mailmime_field_free(mime_field);
res = MAILIMF_ERROR_MEMORY as libc::c_int;
current_block = 17592539310030730040;
break;
}
} else if !(r == MAILIMF_ERROR_PARSE as libc::c_int) {
/* do nothing */
res = r;
current_block = 17592539310030730040;
break;
}
}
cur = if !cur.is_null() {
(*cur).next
} else {
0 as *mut clistcell
}
}
match current_block {
1109700713171191020 => {
if (*list).first.is_null() {
res = MAILIMF_ERROR_PARSE as libc::c_int
} else {
mime_fields = mailmime_fields_new(list);
if mime_fields.is_null() {
res = MAILIMF_ERROR_MEMORY as libc::c_int
} else {
*result = mime_fields;
return MAILIMF_NO_ERROR as libc::c_int;
}
}
}
_ => {}
}
clist_foreach(
list,
::std::mem::transmute::<Option<unsafe fn(_: *mut mailmime_field) -> ()>, clist_func>(

View File

@@ -705,7 +705,7 @@ pub unsafe fn mailmime_new(
}
_ => {}
}
return mime;
mime
}
pub unsafe fn mailmime_new_simple(

View File

@@ -848,7 +848,7 @@ pub unsafe fn mailmime_generate_boundary() -> *mut libc::c_char {
hex::encode(&std::process::id().to_le_bytes()[..2])
);
let c = std::ffi::CString::new(raw).unwrap_or_default();
let c = std::ffi::CString::new(raw).unwrap();
strdup(c.as_ptr())
}

View File

@@ -338,7 +338,7 @@ unsafe fn mailmime_disposition_param_write_driver(
4 => {
let value = (*param).pa_data.pa_size as u32;
let raw = format!("{}", value);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
sizestr = strdup(raw_c.as_ptr());
len = strlen(b"size=\x00" as *const u8 as *const libc::c_char)
.wrapping_add(strlen(sizestr))
@@ -542,7 +542,7 @@ unsafe fn mailmime_version_write_driver(
}
let raw = format!("{}.{}", (version >> 16) as i32, (version & 0xffff) as i32);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut versionstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(do_write, data, col, versionstr, strlen(versionstr));
if r != MAILIMF_NO_ERROR as libc::c_int {
@@ -1516,7 +1516,7 @@ pub unsafe fn mailmime_data_write_driver(
1 => {
let filename = CStr::from_ptr((*mime_data).dt_data.dt_filename)
.to_str()
.unwrap_or_default();
.unwrap();
if let Ok(file) = std::fs::File::open(filename) {
if let Ok(mut text) = memmap::MmapOptions::new().map_copy(&file) {
if 0 != (*mime_data).dt_encoded {
@@ -1797,7 +1797,7 @@ pub unsafe fn mailmime_quoted_printable_write_driver(
start = text.offset(i as isize).offset(1isize);
let raw = format!("={:02X}", (ch as libc::c_int));
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut hexstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(
do_write,
@@ -1822,7 +1822,7 @@ pub unsafe fn mailmime_quoted_printable_write_driver(
}
start = text.offset(i as isize).offset(1isize);
let raw = format!("={:02X}", ch as libc::c_int);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut hexstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(
do_write,
@@ -1866,7 +1866,7 @@ pub unsafe fn mailmime_quoted_printable_write_driver(
}
start = text.offset(i as isize);
let raw = format!("={:02X}", b'\r' as i32);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut hexstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(do_write, data, col, hexstr, 3i32 as size_t);
if r != MAILIMF_NO_ERROR as libc::c_int {
@@ -1890,7 +1890,7 @@ pub unsafe fn mailmime_quoted_printable_write_driver(
"={:02X}\r\n",
*text.offset(i.wrapping_sub(1i32 as libc::size_t) as isize) as libc::c_int
);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut hexstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(do_write, data, col, hexstr, strlen(hexstr));
@@ -1917,7 +1917,7 @@ pub unsafe fn mailmime_quoted_printable_write_driver(
"={:02X}\r\n",
*text.offset(i.wrapping_sub(2i32 as libc::size_t) as isize) as libc::c_int
);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut hexstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(do_write, data, col, hexstr, strlen(hexstr));
@@ -1938,7 +1938,7 @@ pub unsafe fn mailmime_quoted_printable_write_driver(
(*text.offset(i.wrapping_sub(2i32 as libc::size_t) as isize) as u8 as char),
b'\r' as i32
);
let raw_c = std::ffi::CString::new(raw).unwrap_or_default();
let raw_c = std::ffi::CString::new(raw).unwrap();
let mut hexstr = strdup(raw_c.as_ptr());
r = mailimf_string_write_driver(do_write, data, col, hexstr, strlen(hexstr));

File diff suppressed because it is too large Load Diff

View File

@@ -97,7 +97,7 @@ If you want to run "liveconfig" functional tests you can set
chat devs.
- or the path of a file that contains two lines, each describing
via "addr=... mail_pw=..." a test account login that will
via "addr=... mail_pwd=..." a test account login that will
be used for the live tests.
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real

View File

@@ -574,11 +574,11 @@ class EventLogger:
else:
assert not rex.match(ev[0]), "event found {}".format(ev)
def get_matching(self, event_name_regex, check_error=True, timeout=None):
def get_matching(self, event_name_regex, check_error=True):
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
ev = self.get(timeout=timeout, check_error=check_error)
ev = self.get()
if rex.match(ev[0]):
return ev

View File

@@ -8,6 +8,9 @@ from os.path import join as joinpath
# this works well when you in a git-checkout
# run "python deltachat/const.py" to regenerate events
# begin const generated
DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2
DC_PROVIDER_STATUS_BROKEN = 3
DC_GCL_ARCHIVED_ONLY = 0x01
DC_GCL_NO_SPECIALS = 0x02
DC_GCL_ADD_ALLDONE_HINT = 0x04
@@ -52,7 +55,6 @@ DC_CONTACT_ID_LAST_SPECIAL = 9
DC_MSG_TEXT = 10
DC_MSG_IMAGE = 20
DC_MSG_GIF = 21
DC_MSG_STICKER = 23
DC_MSG_AUDIO = 40
DC_MSG_VOICE = 41
DC_MSG_VIDEO = 50
@@ -61,10 +63,6 @@ DC_EVENT_INFO = 100
DC_EVENT_SMTP_CONNECTED = 101
DC_EVENT_IMAP_CONNECTED = 102
DC_EVENT_SMTP_MESSAGE_SENT = 103
DC_EVENT_IMAP_MESSAGE_DELETED = 104
DC_EVENT_IMAP_MESSAGE_MOVED = 105
DC_EVENT_NEW_BLOB_FILE = 150
DC_EVENT_DELETED_BLOB_FILE = 151
DC_EVENT_WARNING = 300
DC_EVENT_ERROR = 400
DC_EVENT_ERROR_NETWORK = 401
@@ -85,9 +83,6 @@ DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
DC_EVENT_GET_STRING = 2091
DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081
DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2
DC_PROVIDER_STATUS_BROKEN = 3
# end const generated

View File

@@ -157,11 +157,6 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
self.live_count += 1
if "e2ee_enabled" not in configdict:
configdict["e2ee_enabled"] = "1"
# Enable strict certificate checks for online accounts
configdict["imap_certificate_checks"] = "1"
configdict["smtp_certificate_checks"] = "1"
tmpdb = tmpdir.join("livedb%d" % self.live_count)
ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count))
ac._evlogger.init_time = self.init_time

View File

@@ -1,7 +1,6 @@
from __future__ import print_function
import pytest
import os
import queue
from deltachat import const, Account
from deltachat.message import Message
from datetime import datetime, timedelta
@@ -20,7 +19,6 @@ class TestOfflineAccountBasic:
d = ac1.get_info()
assert d["arch"]
assert d["number_of_chats"] == "0"
assert d["bcc_self"] == "1"
def test_is_not_configured(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
@@ -39,11 +37,6 @@ class TestOfflineAccountBasic:
ac1 = acfactory.get_unconfigured_account()
assert "save_mime_headers" in ac1.get_config("sys.config_keys").split()
def test_has_bccself(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
assert "bcc_self" in ac1.get_config("sys.config_keys").split()
assert ac1.get_config("bcc_self") == "1"
def test_selfcontact_if_unconfigured(self, acfactory):
ac1 = acfactory.get_unconfigured_account()
with pytest.raises(ValueError):
@@ -100,7 +93,7 @@ class TestOfflineContact:
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("one message")
msg = chat.send_text("one messae")
assert not ac1.delete_contact(contact1)
assert not msg.filemime
@@ -229,9 +222,7 @@ class TestOfflineChat:
chat1.send_image(path="notexists")
fn = data.get_path("d.png")
lp.sec("sending image")
chat1.account._evlogger.consume_events()
msg = chat1.send_image(fn)
chat1.account._evlogger.get_matching("DC_EVENT_NEW_BLOB_FILE")
assert msg.is_image()
assert msg
assert msg.id > 0
@@ -360,34 +351,18 @@ class TestOnlineAccount:
ac1._evlogger.consume_events()
ac1.import_self_keys(dir.strpath)
def test_one_account_send_bcc_setting(self, acfactory, lp):
def test_one_account_send(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
c2 = ac1.create_contact(email="notexists@testrun.org")
c2 = ac1.create_contact(email=ac1.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
lp.sec("send out message with bcc to ourselves")
msg_out = chat.send_text("message2")
ev = ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
# wait for send out (BCC)
assert ac1.get_config("bcc_self") == "1"
self_addr = ac1.get_config("addr")
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
assert self_addr in ev[2]
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
ac1._evlogger.consume_events()
lp.sec("send out message without bcc")
ac1.set_config("bcc_self", "0")
msg_out = chat.send_text("message3")
ev = ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
assert self_addr not in ev[2]
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
# wait for own account to receive
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[1] == msg_out.id
def test_mvbox_sentbox_threads(self, acfactory):
ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True)
@@ -399,17 +374,6 @@ class TestOnlineAccount:
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
def test_move_works(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account(mvbox=True)
wait_configuration_progress(ac2, 1000)
wait_configuration_progress(ac1, 1000)
chat = self.get_chat(ac1, ac2)
chat.send_text("message1")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
ev = ac2._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
def test_forward_messages(self, acfactory):
ac1, ac2 = acfactory.get_two_online_accounts()
chat = self.get_chat(ac1, ac2)
@@ -480,14 +444,6 @@ class TestOnlineAccount:
lp.step("2")
assert msg_out.is_out_mdn_received()
lp.sec("check that a second call to mark_seen does not create change or smtp job")
ac2._evlogger.consume_events()
ac2.mark_seen_messages([msg_in])
try:
ac2._evlogger.get_matching("DC_EVENT_MSG_READ", timeout=0.01)
except queue.Empty:
pass # mark_seen_messages() has generated events before it returns
def test_send_and_receive_will_encrypt_decrypt(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -516,14 +472,6 @@ class TestOnlineAccount:
assert msg_back.text == "message-back"
assert msg_back.is_encrypted()
lp.sec("create group chat with two members, one of which has no encrypt state")
chat = ac1.create_group_chat("encryption test")
chat.add_contact(ac1.create_contact(ac2.get_config("addr")))
chat.add_contact(ac1.create_contact("notexisting@testrun.org"))
msg = chat.send_text("test not encrypt")
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
assert not msg.is_encrypted()
def test_saved_mime_on_received_message(self, acfactory, lp):
ac1, ac2 = acfactory.get_two_online_accounts()
@@ -628,9 +576,6 @@ class TestOnlineAccount:
lp.sec("ac2: start QR-code based join-group protocol")
ch = ac2.qr_join_chat(qr)
assert ch.id >= 10
# check that at least some of the handshake messages are deleted
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
ac2._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED")
wait_securejoin_inviter_progress(ac1, 1000)
def test_qr_verified_group_and_chatting(self, acfactory, lp):

View File

@@ -1,5 +1,4 @@
use std::collections::BTreeMap;
use std::ffi::CStr;
use std::str::FromStr;
use std::{fmt, str};
@@ -7,6 +6,7 @@ use mmime::mailimf::types::*;
use crate::constants::*;
use crate::contact::*;
use crate::dc_tools::as_str;
use crate::key::*;
/// Possible values for encryption preference
@@ -69,42 +69,29 @@ impl Aheader {
}
let mut fine_header = None;
let mut cur = unsafe { (*(*header).fld_list).first };
while !cur.is_null() {
let field = unsafe { (*cur).data as *mut mailimf_field };
if !field.is_null()
&& unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
{
let optional_field = unsafe { (*field).fld_data.fld_optional_field };
for field in unsafe { &(*header).0 } {
if let mailimf_field::OptionalField(optional_field) = *field {
if !optional_field.is_null()
&& unsafe { !(*optional_field).fld_name.is_null() }
&& unsafe {
CStr::from_ptr((*optional_field).fld_name)
.to_str()
.unwrap_or_default()
} == "Autocrypt"
&& unsafe { !(*optional_field).name.is_null() }
&& unsafe { as_str((*optional_field).name) } == "Autocrypt"
{
let value = unsafe {
CStr::from_ptr((*optional_field).fld_value)
.to_str()
.unwrap_or_default()
};
let value = unsafe { as_str((*optional_field).value) };
if let Ok(test) = Self::from_str(value) {
if addr_cmp(&test.addr, wanted_from) {
if fine_header.is_none() {
fine_header = Some(test);
} else {
// TODO: figure out what kind of error case this is
return None;
match Self::from_str(value) {
Ok(test) => {
if addr_cmp(&test.addr, wanted_from) {
if fine_header.is_none() {
fine_header = Some(test);
} else {
// TODO: figure out what kind of error case this is
return None;
}
}
}
_ => {}
}
}
}
cur = unsafe { (*cur).next };
}
fine_header
@@ -131,9 +118,9 @@ impl str::FromStr for Aheader {
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut attributes: BTreeMap<String, String> = s
.split(';')
.split(";")
.filter_map(|a| {
let attribute: Vec<&str> = a.trim().splitn(2, '=').collect();
let attribute: Vec<&str> = a.trim().splitn(2, "=").collect();
if attribute.len() < 2 {
return None;
}
@@ -178,7 +165,7 @@ impl str::FromStr for Aheader {
// Autocrypt-Level0: unknown attributes starting with an underscore can be safely ignored
// Autocrypt-Level0: unknown attribute, treat the header as invalid
if attributes.keys().any(|k| !k.starts_with('_')) {
if attributes.keys().find(|k| !k.starts_with("_")).is_some() {
return Err(());
}

View File

@@ -152,23 +152,21 @@ impl Chat {
return context.stock_str(StockMessage::DeadDrop).into();
}
let cnt = get_chat_contact_cnt(context, self.id);
return context.stock_string_repl_int(StockMessage::Member, cnt as i32);
return context
.stock_string_repl_int(StockMessage::Member, cnt as i32)
.into();
}
"Err".to_string()
return "Err".into();
}
pub fn get_parent_mime_headers(&self, context: &Context) -> Option<(String, String, String)> {
let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?));
let params = params![self.id as i32, DC_CONTACT_ID_SELF as i32];
let sql = &context.sql;
// use the last messsage of another user in the group as the parent
let main_query = "SELECT rfc724_mid, mime_in_reply_to, mime_references \
FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT max(timestamp) \
FROM msgs WHERE chat_id=?1 AND from_id!=?2);";
// there are no messages of other users - use the first message if SELF as parent
let fallback_query = "SELECT rfc724_mid, mime_in_reply_to, mime_references \
FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT min(timestamp) \
FROM msgs WHERE chat_id=?1 AND from_id==?2);";
@@ -291,7 +289,7 @@ impl Chat {
if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup {
if self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 {
self.param.remove(Param::Unpromoted);
self.update_param(context)?;
self.update_param(context).unwrap();
}
}
}
@@ -301,15 +299,16 @@ impl Chat {
so that E2EE is no longer available at a later point (reset, changed settings),
we do not send the message out at all */
do_guarantee_e2ee = false;
e2ee_enabled = context.get_config_bool(Config::E2eeEnabled);
e2ee_enabled = context
.sql
.get_config_int(context, "e2ee_enabled")
.unwrap_or_else(|| 1)
== 1;
if e2ee_enabled && msg.param.get_int(Param::ForcePlaintext).unwrap_or_default() == 0 {
let mut can_encrypt = true;
let mut all_mutual = true;
let mut can_encrypt = 1;
let mut all_mutual = 1;
// take care that this statement returns NULL rows
// if there is no peerstates for a chat member!
// for DC_PARAM_SELFTALK this statement does not return any row
let res = context.sql.query_map(
let res = context.sql.query_row(
"SELECT ps.prefer_encrypted, c.addr \
FROM chats_contacts cc \
LEFT JOIN contacts c ON cc.contact_id=c.id \
@@ -317,32 +316,29 @@ impl Chat {
WHERE cc.chat_id=? AND cc.contact_id>9;",
params![self.id],
|row| {
let addr: String = row.get(1)?;
let state: String = row.get(1)?;
if let Some(prefer_encrypted) = row.get::<_, Option<i32>>(0)? {
// the peerstate exist, so we have either public_key or gossip_key
// and can encrypt potentially
if prefer_encrypted != 1 {
info!(
context,
"[autocrypt] peerstate for {} is {}",
addr,
state,
if prefer_encrypted == 0 {
"NOPREFERENCE"
} else {
"RESET"
},
);
all_mutual = false;
all_mutual = 0;
}
} else {
info!(context, "[autocrypt] no peerstate for {}", addr,);
can_encrypt = false;
all_mutual = false;
info!(context, "[autocrypt] no peerstate for {}", state,);
can_encrypt = 0;
all_mutual = 0;
}
Ok(())
},
|rows| rows.collect::<Result<Vec<_>, _>>().map_err(Into::into),
);
match res {
Ok(_) => {}
@@ -351,8 +347,8 @@ impl Chat {
}
}
if can_encrypt {
if all_mutual {
if 0 != can_encrypt {
if 0 != all_mutual {
do_guarantee_e2ee = true;
} else if last_msg_in_chat_encrypted(context, &context.sql, self.id) {
do_guarantee_e2ee = true;
@@ -362,15 +358,7 @@ impl Chat {
if do_guarantee_e2ee {
msg.param.set_int(Param::GuranteeE2ee, 1);
}
// reset eg. for forwarding
msg.param.remove(Param::ErroneousE2ee);
// set "In-Reply-To:" to identify the message to which the composed message is a reply;
// set "References:" to identify the "thread" of the conversation;
// both according to RFC 5322 3.6.4, page 25
//
// as self-talks are mainly used to transfer data between devices,
// we do not set In-Reply-To/References in this case.
if !self.is_self_talk() {
if let Some((parent_rfc724_mid, parent_in_reply_to, parent_references)) =
self.get_parent_mime_headers(context)
@@ -378,9 +366,6 @@ impl Chat {
if !parent_rfc724_mid.is_empty() {
new_in_reply_to = parent_rfc724_mid.clone();
}
// the whole list of messages referenced may be huge;
// only use the oldest and and the parent message
let parent_references = if let Some(n) = parent_references.find(' ') {
&parent_references[0..n]
} else {
@@ -388,7 +373,6 @@ impl Chat {
};
if !parent_references.is_empty() && !parent_rfc724_mid.is_empty() {
// angle brackets are added by the mimefactory later
new_references = format!("{} {}", parent_references, parent_rfc724_mid);
} else if !parent_references.is_empty() {
new_references = parent_references.to_string();
@@ -662,7 +646,6 @@ pub fn msgtype_has_file(msgtype: Viewtype) -> bool {
match msgtype {
Viewtype::Image => true,
Viewtype::Gif => true,
Viewtype::Sticker => true,
Viewtype::Audio => true,
Viewtype::Voice => true,
Viewtype::Video => true,
@@ -800,7 +783,10 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u3
message::update_msg_state(context, msg.id, MessageState::OutPending);
}
job_send_msg(context, msg.id)?;
ensure!(
job_send_msg(context, msg.id) != 0,
"Failed to initiate send job"
);
context.call_cb(Event::MsgsChanged {
chat_id: msg.chat_id,
@@ -819,8 +805,11 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result<u3
if 0 == id {
// avoid hanging if user tampers with db
break;
} else if let Ok(mut copy) = Message::load_from_db(context, id as u32) {
send_msg(context, 0, &mut copy)?;
} else {
if let Ok(mut copy) = Message::load_from_db(context, id as u32) {
// TODO: handle cleanup and return early instead
send_msg(context, 0, &mut copy).unwrap();
}
}
}
msg.param.remove(Param::PrepForwards);
@@ -973,7 +962,10 @@ pub fn get_chat_msgs(context: &Context, chat_id: u32, flags: u32, marker1before:
};
let success = if chat_id == 1 {
let show_emails = context.get_config_int(Config::ShowEmails);
let show_emails = context
.sql
.get_config_int(context, "show_emails")
.unwrap_or_default();
context.sql.query_map(
"SELECT m.id, m.timestamp FROM msgs m \
LEFT JOIN chats ON m.chat_id=chats.id \
@@ -1389,10 +1381,11 @@ pub(crate) fn add_contact_to_chat_ex(
}
if from_handshake && chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 {
chat.param.remove(Param::Unpromoted);
chat.update_param(context)?;
chat.update_param(context).unwrap();
}
let self_addr = context
.get_config(Config::ConfiguredAddr)
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if contact.get_addr() == &self_addr {
bail!("invalid attempt to add self e-mail address to group");
@@ -1406,14 +1399,14 @@ pub(crate) fn add_contact_to_chat_ex(
}
} else {
// else continue and send status mail
if chat.typ == Chattype::VerifiedGroup
&& contact.is_verified(context) != VerifiedStatus::BidirectVerified
{
error!(
context,
"Only bidirectional verified contacts can be added to verified groups."
);
return Ok(false);
if chat.typ == Chattype::VerifiedGroup {
if contact.is_verified(context) != VerifiedStatus::BidirectVerified {
error!(
context,
"Only bidirectional verified contacts can be added to verified groups."
);
return Ok(false);
}
}
if !add_to_chat_contacts_table(context, chat_id, contact_id) {
return Ok(false);
@@ -1430,14 +1423,14 @@ pub(crate) fn add_contact_to_chat_ex(
msg.param.set_int(Param::Cmd, 4);
msg.param.set(Param::Arg, contact.get_addr());
msg.param.set_int(Param::Arg2, from_handshake.into());
msg.id = send_msg(context, chat_id, &mut msg)?;
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: msg.id,
});
}
context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 });
Ok(true)
return Ok(true);
}
fn real_group_exists(context: &Context, chat_id: u32) -> bool {
@@ -1524,7 +1517,7 @@ pub fn remove_contact_from_chat(
if chat.is_promoted() {
msg.type_0 = Viewtype::Text;
if contact.id == DC_CONTACT_ID_SELF {
set_group_explicitly_left(context, chat.grpid)?;
set_group_explicitly_left(context, chat.grpid).unwrap();
msg.text = Some(context.stock_system_msg(
StockMessage::MsgGroupLeft,
"",
@@ -1541,7 +1534,7 @@ pub fn remove_contact_from_chat(
}
msg.param.set_int(Param::Cmd, 5);
msg.param.set(Param::Arg, contact.get_addr());
msg.id = send_msg(context, chat_id, &mut msg)?;
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: msg.id,
@@ -1605,7 +1598,7 @@ pub fn set_chat_name(
let mut msg = Message::default();
if real_group_exists(context, chat_id) {
if chat.name == new_name.as_ref() {
if &chat.name == new_name.as_ref() {
success = true;
} else if !is_contact_in_chat(context, chat_id, 1) {
emit_event!(
@@ -1638,7 +1631,7 @@ pub fn set_chat_name(
if !chat.name.is_empty() {
msg.param.set(Param::Arg, &chat.name);
}
msg.id = send_msg(context, chat_id, &mut msg)?;
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: msg.id,
@@ -1707,7 +1700,7 @@ pub fn set_chat_profile_image(
"",
DC_CONTACT_ID_SELF,
));
msg.id = send_msg(context, chat_id, &mut msg)?;
msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default();
emit_event!(
context,
Event::MsgsChanged {
@@ -1723,35 +1716,36 @@ pub fn set_chat_profile_image(
bail!("Failed to set profile image");
}
pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<(), Error> {
ensure!(!msg_ids.is_empty(), "empty msgs_ids: no one to forward to");
ensure!(
chat_id > DC_CHAT_ID_LAST_SPECIAL,
"can not forward to special chat"
);
pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) {
if msg_ids.is_empty() || chat_id <= DC_CHAT_ID_LAST_SPECIAL {
return;
}
let mut created_db_entries = Vec::new();
let mut curr_timestamp: i64;
unarchive(context, chat_id)?;
unarchive(context, chat_id).unwrap();
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len());
let idsstr = msg_ids
.iter()
.into_iter()
.enumerate()
.fold(String::with_capacity(2 * msg_ids.len()), |acc, (i, n)| {
(if i == 0 { acc } else { acc + "," }) + &n.to_string()
});
let ids = context.sql.query_map(
format!(
"SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id",
idsstr
),
params![],
|row| row.get::<_, i32>(0),
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
)?;
let ids = context
.sql
.query_map(
format!(
"SELECT id FROM msgs WHERE id IN({}) ORDER BY timestamp,id",
idsstr
),
params![],
|row| row.get::<_, i32>(0),
|ids| ids.collect::<Result<Vec<_>, _>>().map_err(Into::into),
)
.unwrap(); // TODO: better error handling
for id in ids {
let src_msg_id = id;
@@ -1771,7 +1765,7 @@ pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<
let new_msg_id: u32;
if msg.state == MessageState::OutPreparing {
let fresh9 = curr_timestamp;
curr_timestamp += 1;
curr_timestamp = curr_timestamp + 1;
new_msg_id = chat
.prepare_msg_raw(context, &mut msg, fresh9)
.unwrap_or_default();
@@ -1791,11 +1785,11 @@ pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<
} else {
msg.state = MessageState::OutPending;
let fresh10 = curr_timestamp;
curr_timestamp += 1;
curr_timestamp = curr_timestamp + 1;
new_msg_id = chat
.prepare_msg_raw(context, &mut msg, fresh10)
.unwrap_or_default();
job_send_msg(context, new_msg_id)?;
job_send_msg(context, new_msg_id);
}
created_db_entries.push(chat_id);
created_db_entries.push(new_msg_id);
@@ -1808,8 +1802,6 @@ pub fn forward_msgs(context: &Context, msg_ids: &[u32], chat_id: u32) -> Result<
msg_id: created_db_entries[i + 1],
});
}
Ok(())
}
pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> usize {

View File

@@ -182,7 +182,7 @@ impl Chatlist {
if 0 == listflags & DC_GCL_NO_SPECIALS {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg(context);
if last_deaddrop_fresh_msg_id > 0 {
ids.insert(0, (DC_CHAT_ID_DEADDROP, last_deaddrop_fresh_msg_id));
ids.push((1, last_deaddrop_fresh_msg_id));
}
add_archived_link_item = 1;
}
@@ -258,11 +258,13 @@ impl Chatlist {
let chat_loaded: Chat;
let chat = if let Some(chat) = chat {
chat
} else if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
if let Ok(chat) = Chat::load_from_db(context, self.ids[index].0) {
chat_loaded = chat;
&chat_loaded
} else {
return ret;
}
};
let lastmsg_id = self.ids[index].1;

View File

@@ -19,12 +19,10 @@ pub enum Config {
MailUser,
MailPw,
MailPort,
ImapCertificateChecks,
SendServer,
SendUser,
SendPw,
SendPort,
SmtpCertificateChecks,
ServerFlags,
#[strum(props(default = "INBOX"))]
ImapFolder,
@@ -32,8 +30,6 @@ pub enum Config {
Selfstatus,
Selfavatar,
#[strum(props(default = "1"))]
BccSelf,
#[strum(props(default = "1"))]
E2eeEnabled,
#[strum(props(default = "1"))]
MdnsEnabled,
@@ -54,12 +50,10 @@ pub enum Config {
ConfiguredMailPw,
ConfiguredMailPort,
ConfiguredMailSecurity,
ConfiguredImapCertificateChecks,
ConfiguredSendServer,
ConfiguredSendUser,
ConfiguredSendPw,
ConfiguredSendPort,
ConfiguredSmtpCertificateChecks,
ConfiguredServerFlags,
ConfiguredSendSecurity,
ConfiguredE2EEEnabled,
@@ -78,13 +72,13 @@ impl Context {
pub fn get_config(&self, key: Config) -> Option<String> {
let value = match key {
Config::Selfavatar => {
let rel_path = self.sql.get_raw_config(self, key);
rel_path.map(|p| dc_get_abs_path(self, &p).to_string_lossy().into_owned())
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| dc_get_abs_path(self, &p).to_str().unwrap().to_string())
}
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
Config::SysConfigKeys => Some(get_config_keys_string()),
_ => self.sql.get_raw_config(self, key),
_ => self.sql.get_config(self, key),
};
if value.is_some() {
@@ -98,16 +92,6 @@ impl Context {
}
}
pub fn get_config_int(&self, key: Config) -> i32 {
self.get_config(key)
.and_then(|s| s.parse().ok())
.unwrap_or_default()
}
pub fn get_config_bool(&self, key: Config) -> bool {
self.get_config_int(key) != 0
}
/// Set the given config key.
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> {
@@ -115,20 +99,20 @@ impl Context {
Config::Selfavatar if value.is_some() => {
let rel_path = std::fs::canonicalize(value.unwrap())?;
self.sql
.set_raw_config(self, key, Some(&rel_path.to_string_lossy()))
.set_config(self, key, Some(&rel_path.to_string_lossy()))
}
Config::InboxWatch => {
let ret = self.sql.set_raw_config(self, key, value);
let ret = self.sql.set_config(self, key, value);
interrupt_imap_idle(self);
ret
}
Config::SentboxWatch => {
let ret = self.sql.set_raw_config(self, key, value);
let ret = self.sql.set_config(self, key, value);
interrupt_sentbox_idle(self);
ret
}
Config::MvboxWatch => {
let ret = self.sql.set_raw_config(self, key, value);
let ret = self.sql.set_config(self, key, value);
interrupt_mvbox_idle(self);
ret
}
@@ -140,9 +124,9 @@ impl Context {
value
};
self.sql.set_raw_config(self, key, val)
self.sql.set_config(self, key, val)
}
_ => self.sql.set_raw_config(self, key, value),
_ => self.sql.set_config(self, key, value),
}
}
}

View File

@@ -1,8 +1,10 @@
use libc::free;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::login_param::LoginParam;
use super::read_autoconf_file;
@@ -22,28 +24,26 @@ struct moz_autoconfigure_t<'a> {
pub tag_config: libc::c_int,
}
pub fn moz_autoconfigure(
pub unsafe fn moz_autoconfigure(
context: &Context,
url: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let xml_raw = match read_autoconf_file(context, url) {
Err(err) => {
info!(context, "can't read file: {}", err);
return None;
}
Ok(content) => content,
};
let xml_raw = read_autoconf_file(context, url);
if xml_raw.is_null() {
return None;
}
// Split address into local part and domain part.
let p = param_in.addr.find("@");
if p.is_none() {
free(xml_raw as *mut libc::c_void);
return None;
}
let (in_emaillocalpart, in_emaildomain) = param_in.addr.split_at(p.unwrap());
let in_emaildomain = &in_emaildomain[1..];
let mut reader = quick_xml::Reader::from_str(&xml_raw);
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
let mut buf = Vec::new();
@@ -88,9 +88,11 @@ pub fn moz_autoconfigure(
{
let r = moz_ac.out.to_string();
warn!(context, "Bad or incomplete autoconfig: {}", r,);
free(xml_raw as *mut libc::c_void);
return None;
}
free(xml_raw as *mut libc::c_void);
Some(moz_ac.out)
}

View File

@@ -1,10 +1,13 @@
use std::collections::HashMap;
use std::ptr;
use libc::free;
use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::login_param::LoginParam;
use quick_xml::events::{BytesEnd};
use super::read_autoconf_file;
/* ******************************************************************************
@@ -14,52 +17,62 @@ use super::read_autoconf_file;
struct outlk_autodiscover_t<'a> {
pub in_0: &'a LoginParam,
pub out: LoginParam,
pub out_imap_set: bool,
pub out_smtp_set: bool,
pub config: HashMap<String, String>,
pub out_imap_set: libc::c_int,
pub out_smtp_set: libc::c_int,
pub tag_config: libc::c_int,
pub config: [*mut libc::c_char; 6],
pub redirect: *mut libc::c_char,
}
pub fn outlk_autodiscover(
pub unsafe fn outlk_autodiscover(
context: &Context,
url__: &str,
param_in: &LoginParam,
) -> Option<LoginParam> {
let mut url = url__.to_string();
let mut xml_raw: *mut libc::c_char = ptr::null_mut();
let mut url = url__.strdup();
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: LoginParam::new(),
out_imap_set: false,
out_smtp_set: false,
config: HashMap::new(),
out_imap_set: 0,
out_smtp_set: 0,
tag_config: 0,
config: [ptr::null_mut(); 6],
redirect: ptr::null_mut(),
};
for i in 0..10 {
let xml_raw = read_autoconf_file(context, &url);
if xml_raw.is_err() {
return Some(outlk_ad.out);
let ok_to_continue;
let mut i = 0;
loop {
if !(i < 10) {
ok_to_continue = true;
break;
}
libc::memset(
&mut outlk_ad as *mut outlk_autodiscover_t as *mut libc::c_void,
0,
::std::mem::size_of::<outlk_autodiscover_t>(),
);
xml_raw = read_autoconf_file(context, as_str(url));
if xml_raw.is_null() {
ok_to_continue = false;
break;
}
let xml_raw = xml_raw.unwrap();
let mut reader = quick_xml::Reader::from_str(&xml_raw);
let mut reader = quick_xml::Reader::from_str(as_str(xml_raw));
reader.trim_text(true);
let mut buf = Vec::new();
let current_tag: Option<String> = None;
loop {
match reader.read_event(&mut buf) {
Ok(quick_xml::events::Event::Start(ref e)) => {
current_tag = Some(String::from_utf8_lossy(e.name()).trim().to_lowercase());
outlk_autodiscover_starttag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::End(ref e)) => {
if "protocol" == String::from_utf8_lossy(e.name()).trim().to_lowercase() {
finish_settings(e, &mut outlk_ad);
}
current_tag = None;
outlk_autodiscover_endtag_cb(e, &mut outlk_ad)
}
Ok(quick_xml::events::Event::Text(ref e)) => {
if let Some(current_tag) = current_tag {
let val = e.unescape_and_decode(&reader).unwrap_or_default();
&outlk_ad.config.insert(current_tag, val);
}
outlk_autodiscover_text_cb(e, &mut outlk_ad, &reader)
}
Err(e) => {
error!(
@@ -68,70 +81,134 @@ pub fn outlk_autodiscover(
reader.buffer_position(),
e
);
break;
}
Ok(quick_xml::events::Event::Eof) => break,
_ => (),
}
buf.clear();
}
if let Some(next_url) = outlk_ad.config.get("redirecturl") {
if !next_url.is_empty() {
url = next_url.to_string();
continue;
}
if !(!outlk_ad.config[5].is_null()
&& 0 != *outlk_ad.config[5usize].offset(0isize) as libc::c_int)
{
ok_to_continue = true;
break;
}
break;
free(url as *mut libc::c_void);
url = dc_strdup(outlk_ad.config[5usize]);
outlk_clean_config(&mut outlk_ad);
free(xml_raw as *mut libc::c_void);
xml_raw = ptr::null_mut();
i += 1;
}
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = outlk_ad.out.to_string();
warn!(context, "Bad or incomplete autoconfig: {}", r,);
if ok_to_continue {
if outlk_ad.out.mail_server.is_empty()
|| outlk_ad.out.mail_port == 0
|| outlk_ad.out.send_server.is_empty()
|| outlk_ad.out.send_port == 0
{
let r = outlk_ad.out.to_string();
warn!(context, "Bad or incomplete autoconfig: {}", r,);
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
return None;
return None;
}
}
free(url as *mut libc::c_void);
free(xml_raw as *mut libc::c_void);
outlk_clean_config(&mut outlk_ad);
Some(outlk_ad.out)
}
fn finish_settings(event: &BytesEnd, outlk_ad: &mut outlk_autodiscover_t) {
let ssl_on = false;
let ssl_off = false;
let config = &outlk_ad.config;
if let Some(type_val) = &config.get("type") {
let port = match config.get("port") {
None => 0,
Some(r) => {
r.parse::<i32>().unwrap_or_default()
}
};
if let Some(ssl) = &config.get("ssl") {
ssl_on = *ssl == "on";
ssl_off = *ssl == "off";
}
let type_val = *type_val;
if !outlk_ad.out_imap_set && type_val == "imap" {
outlk_ad.out.mail_server = config.get("server").unwrap_or("".to_string());
outlk_ad.out.mail_port = port;
if ssl_on {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32;
} else if ssl_off {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32;
}
outlk_ad.out_imap_set = true;
} else if !outlk_ad.out_smtp_set && type_val == "smtp" {
outlk_ad.out.send_server = &config.get("server").unwrap_or_default();
outlk_ad.out.send_port = port;
if ssl_on {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
} else if ssl_off {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
}
outlk_ad.out_smtp_set = true;
}
unsafe fn outlk_clean_config(mut outlk_ad: *mut outlk_autodiscover_t) {
for i in 0..6 {
free((*outlk_ad).config[i] as *mut libc::c_void);
(*outlk_ad).config[i] = ptr::null_mut();
}
}
fn outlk_autodiscover_text_cb<B: std::io::BufRead>(
event: &BytesText,
outlk_ad: &mut outlk_autodiscover_t,
reader: &quick_xml::Reader<B>,
) {
let val = event.unescape_and_decode(reader).unwrap_or_default();
unsafe {
free(outlk_ad.config[outlk_ad.tag_config as usize].cast());
outlk_ad.config[outlk_ad.tag_config as usize] = val.trim().strdup();
}
}
unsafe fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut outlk_autodiscover_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "protocol" {
if !outlk_ad.config[1].is_null() {
let port = dc_atoi_null_is_0(outlk_ad.config[3]);
let ssl_on = (!outlk_ad.config[4].is_null()
&& strcasecmp(
outlk_ad.config[4],
b"on\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
let ssl_off = (!outlk_ad.config[4].is_null()
&& strcasecmp(
outlk_ad.config[4],
b"off\x00" as *const u8 as *const libc::c_char,
) == 0) as libc::c_int;
if strcasecmp(
outlk_ad.config[1],
b"imap\x00" as *const u8 as *const libc::c_char,
) == 0
&& outlk_ad.out_imap_set == 0
{
outlk_ad.out.mail_server = to_string(outlk_ad.config[2]);
outlk_ad.out.mail_port = port;
if 0 != ssl_on {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_SSL as i32
} else if 0 != ssl_off {
outlk_ad.out.server_flags |= DC_LP_IMAP_SOCKET_PLAIN as i32
}
outlk_ad.out_imap_set = 1
} else if strcasecmp(
outlk_ad.config[1usize],
b"smtp\x00" as *const u8 as *const libc::c_char,
) == 0
&& outlk_ad.out_smtp_set == 0
{
outlk_ad.out.send_server = to_string(outlk_ad.config[2]);
outlk_ad.out.send_port = port;
if 0 != ssl_on {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_SSL as i32
} else if 0 != ssl_off {
outlk_ad.out.server_flags |= DC_LP_SMTP_SOCKET_PLAIN as i32
}
outlk_ad.out_smtp_set = 1
}
}
outlk_clean_config(outlk_ad);
}
outlk_ad.tag_config = 0;
}
fn outlk_autodiscover_starttag_cb(event: &BytesStart, outlk_ad: &mut outlk_autodiscover_t) {
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
if tag == "protocol" {
unsafe { outlk_clean_config(outlk_ad) };
} else if tag == "type" {
outlk_ad.tag_config = 1
} else if tag == "server" {
outlk_ad.tag_config = 2
} else if tag == "port" {
outlk_ad.tag_config = 3
} else if tag == "ssl" {
outlk_ad.tag_config = 4
} else if tag == "redirecturl" {
outlk_ad.tag_config = 5
};
}

View File

@@ -1,11 +1,9 @@
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::e2ee;
use crate::error::*;
use crate::imap::*;
use crate::job::*;
use crate::login_param::LoginParam;
@@ -28,7 +26,7 @@ macro_rules! progress {
}
// connect
pub fn configure(context: &Context) {
pub unsafe fn configure(context: &Context) {
if dc_has_ongoing(context) {
warn!(context, "There is already another ongoing process running.",);
return;
@@ -39,7 +37,7 @@ pub fn configure(context: &Context) {
/// Check if the context is already configured.
pub fn dc_is_configured(context: &Context) -> bool {
context.sql.get_raw_config_bool(context, "configured")
context.sql.get_config_bool(context, "configured")
}
/*******************************************************************************
@@ -47,7 +45,7 @@ pub fn dc_is_configured(context: &Context) -> bool {
******************************************************************************/
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
#[allow(non_snake_case, unused_must_use)]
pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
let mut success = false;
let mut imap_connected_here = false;
let mut smtp_connected_here = false;
@@ -115,7 +113,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
param.addr = oauth2_addr;
context
.sql
.set_raw_config(context, "addr", Some(param.addr.as_str()))
.set_config(context, "addr", Some(param.addr.as_str()))
.ok();
}
progress!(context, 20);
@@ -354,7 +352,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
ok_to_continue8 = true;
break;
}
if param_autoconfig.is_some() {
if !param_autoconfig.is_none() {
ok_to_continue8 = false;
break;
}
@@ -425,7 +423,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
.unwrap()
.connect(context, &param)
{
if param_autoconfig.is_some() {
if !param_autoconfig.is_none() {
success = false;
} else if s.shall_stop_ongoing {
success = false;
@@ -478,8 +476,15 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
}
16 => {
progress!(context, 900);
let flags: libc::c_int = if context.get_config_bool(Config::MvboxWatch)
|| context.get_config_bool(Config::MvboxMove)
let flags: libc::c_int = if 0
!= context
.sql
.get_config_int(context, "mvbox_watch")
.unwrap_or_else(|| 1)
|| 0 != context
.sql
.get_config_int(context, "mvbox_move")
.unwrap_or_else(|| 1)
{
DC_CREATE_MVBOX as i32
} else {
@@ -502,7 +507,7 @@ pub fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) {
)
.ok();
context.sql.set_raw_config_bool(context, "configured", true);
context.sql.set_config_bool(context, "configured", true);
true
}
18 => {
@@ -603,7 +608,12 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_
if imap.is_connected() {
ret_connected = 1
} else if !context.sql.get_raw_config_bool(context, "configured") {
} else if context
.sql
.get_config_int(context, "configured")
.unwrap_or_default()
== 0
{
warn!(context, "Not configured, cannot connect.",);
} else {
let param = LoginParam::from_database(context, "configured_");
@@ -634,7 +644,7 @@ pub fn dc_stop_ongoing_process(context: &Context) {
};
}
pub fn read_autoconf_file(context: &Context, url: &str) -> Result<String> {
pub fn read_autoconf_file(context: &Context, url: &str) -> *mut libc::c_char {
info!(context, "Testing {} ...", url);
match reqwest::Client::new()
@@ -642,10 +652,12 @@ pub fn read_autoconf_file(context: &Context, url: &str) -> Result<String> {
.send()
.and_then(|mut res| res.text())
{
Err(err) => {
bail!("{}", err);
Ok(res) => unsafe { res.strdup() },
Err(_err) => {
info!(context, "Can\'t read file.",);
std::ptr::null_mut()
}
Ok(res) => Ok(res.to_string()),
}
}
@@ -663,6 +675,8 @@ mod tests {
.set_config(Config::Addr, Some("probably@unexistant.addr"))
.unwrap();
t.ctx.set_config(Config::MailPw, Some("123456")).unwrap();
dc_job_do_DC_JOB_CONFIGURE_IMAP(&t.ctx);
unsafe {
dc_job_do_DC_JOB_CONFIGURE_IMAP(&t.ctx);
}
}
}

View File

@@ -25,6 +25,7 @@ impl Default for MoveState {
// some defaults
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
@@ -194,11 +195,6 @@ pub enum Viewtype {
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
Gif = 21,
/// Message containing a sticker, similar to image.
/// If possible, the ui should display the image without borders in a transparent way.
/// A click on a sticker will offer to install the sticker set in some future.
Sticker = 23,
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
@@ -303,8 +299,7 @@ const DC_STR_MSGACTIONBYME: usize = 63;
const DC_STR_MSGLOCATIONENABLED: usize = 64;
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
const DC_STR_LOCATION: usize = 66;
const DC_STR_STICKER: usize = 67;
const DC_STR_COUNT: usize = 67;
const DC_STR_COUNT: usize = 66;
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;

View File

@@ -390,18 +390,20 @@ impl Contact {
}
sth_modified = Modifier::Modified;
}
} else if sql::execute(
context,
&context.sql,
"INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);",
params![name.as_ref(), addr, origin,],
)
.is_ok()
{
row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr);
sth_modified = Modifier::Created;
} else {
error!(context, "Cannot add contact.");
if sql::execute(
context,
&context.sql,
"INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);",
params![name.as_ref(), addr, origin,],
)
.is_ok()
{
row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr);
sth_modified = Modifier::Created;
} else {
error!(context, "Cannot add contact.");
}
}
Ok((row_id, sth_modified))
@@ -825,7 +827,7 @@ impl Contact {
if let Ok(contact) = Contact::load_from_db(context, contact_id) {
if !contact.addr.is_empty() {
let normalized_addr = addr_normalize(addr.as_ref());
if contact.addr == normalized_addr {
if &contact.addr == &normalized_addr {
return true;
}
}
@@ -961,9 +963,9 @@ pub fn normalize_name(full_name: impl AsRef<str>) -> String {
if len > 0 {
let firstchar = full_name.as_bytes()[0];
let lastchar = full_name.as_bytes()[len - 1];
if firstchar == b'\'' && lastchar == b'\''
|| firstchar == b'\"' && lastchar == b'\"'
|| firstchar == b'<' && lastchar == b'>'
if firstchar == '\'' as u8 && lastchar == '\'' as u8
|| firstchar == '\"' as u8 && lastchar == '\"' as u8
|| firstchar == '<' as u8 && lastchar == '>' as u8
{
full_name = &full_name[1..len - 1];
}

View File

@@ -1,17 +1,13 @@
use std::collections::HashMap;
use std::ffi::OsString;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Condvar, Mutex, RwLock};
use libc::uintptr_t;
use crate::chat::*;
use crate::config::Config;
use crate::constants::*;
use crate::contact::*;
use crate::dc_tools::{dc_copy_file, dc_derive_safe_stem_ext};
use crate::error::*;
use crate::events::Event;
use crate::imap::*;
@@ -24,7 +20,6 @@ use crate::message::{self, Message};
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
use rand::{thread_rng, Rng};
/// Callback function type for [Context]
///
@@ -163,59 +158,6 @@ impl Context {
self.blobdir.as_path()
}
pub fn copy_to_blobdir(&self, orig_filename: impl AsRef<str>) -> Result<String> {
// return a $BLOBDIR/<filename> with the content of orig_filename
// copied into it. The <filename> will be safely derived from
// orig_filename, and will not clash with existing filenames.
let dest = self.new_blob_file(&orig_filename, b"")?;
if dc_copy_file(
&self,
PathBuf::from(orig_filename.as_ref()),
PathBuf::from(&dest),
) {
Ok(dest)
} else {
bail!("could not copy {} to {}", orig_filename.as_ref(), dest);
}
}
pub fn new_blob_file(&self, orig_filename: impl AsRef<str>, data: &[u8]) -> Result<String> {
// return a $BLOBDIR/<FILENAME> string which corresponds to the
// respective file in the blobdir, and which contains the data.
// FILENAME is computed by looking and possibly mangling the
// basename of orig_filename. The resulting filenames are meant
// to be human-readable.
let (stem, ext) = dc_derive_safe_stem_ext(orig_filename.as_ref());
// ext starts with "." or is empty string, so we can always resconstruct
for i in 0..3 {
let candidate_basename = match i {
// first a try to just use the (possibly mangled) original basename
0 => format!("{}{}", stem, ext),
// otherwise extend stem with random numbers
_ => {
let mut rng = thread_rng();
let random_id: u32 = rng.gen();
format!("{}-{}{}", stem, random_id, ext)
}
};
let path = self.get_blobdir().join(&candidate_basename);
if let Ok(mut file) = fs::OpenOptions::new()
.create_new(true)
.write(true)
.open(&path)
{
file.write_all(data)?;
let db_entry = format!("$BLOBDIR/{}", candidate_basename);
self.call_cb(Event::NewBlobFile(db_entry.clone()));
return Ok(db_entry);
}
}
bail!("out of luck to create new blob file");
}
pub fn call_cb(&self, event: Event) -> uintptr_t {
(*self.cb)(self, event)
}
@@ -224,20 +166,27 @@ impl Context {
let unset = "0";
let l = LoginParam::from_database(self, "");
let l2 = LoginParam::from_database(self, "configured_");
let displayname = self.get_config(Config::Displayname);
let displayname = self.sql.get_config(self, "displayname");
let chats = get_chat_cnt(self) as usize;
let real_msgs = message::get_real_msg_cnt(self) as usize;
let deaddrop_msgs = message::get_deaddrop_msg_cnt(self) as usize;
let contacts = Contact::get_real_cnt(self) as usize;
let is_configured = self.get_config_int(Config::Configured);
let is_configured = self
.sql
.get_config_int(self, "configured")
.unwrap_or_default();
let dbversion = self
.sql
.get_raw_config_int(self, "dbversion")
.get_config_int(self, "dbversion")
.unwrap_or_default();
let e2ee_enabled = self.get_config_int(Config::E2eeEnabled);
let mdns_enabled = self.get_config_int(Config::MdnsEnabled);
let bcc_self = self.get_config_int(Config::BccSelf);
let e2ee_enabled = self
.sql
.get_config_int(self, "e2ee_enabled")
.unwrap_or_else(|| 1);
let mdns_enabled = self
.sql
.get_config_int(self, "mdns_enabled")
.unwrap_or_else(|| 1);
let prv_key_cnt: Option<isize> =
self.sql
@@ -255,22 +204,33 @@ impl Context {
"<Not yet calculated>".into()
};
let inbox_watch = self.get_config_int(Config::InboxWatch);
let sentbox_watch = self.get_config_int(Config::SentboxWatch);
let mvbox_watch = self.get_config_int(Config::MvboxWatch);
let mvbox_move = self.get_config_int(Config::MvboxMove);
let inbox_watch = self
.sql
.get_config_int(self, "inbox_watch")
.unwrap_or_else(|| 1);
let sentbox_watch = self
.sql
.get_config_int(self, "sentbox_watch")
.unwrap_or_else(|| 1);
let mvbox_watch = self
.sql
.get_config_int(self, "mvbox_watch")
.unwrap_or_else(|| 1);
let mvbox_move = self
.sql
.get_config_int(self, "mvbox_move")
.unwrap_or_else(|| 1);
let folders_configured = self
.sql
.get_raw_config_int(self, "folders_configured")
.get_config_int(self, "folders_configured")
.unwrap_or_default();
let configured_sentbox_folder = self
.sql
.get_raw_config(self, "configured_sentbox_folder")
.get_config(self, "configured_sentbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let configured_mvbox_folder = self
.sql
.get_raw_config(self, "configured_mvbox_folder")
.get_config(self, "configured_mvbox_folder")
.unwrap_or_else(|| "<unset>".to_string());
let mut res = get_info();
@@ -294,7 +254,6 @@ impl Context {
res.insert("configured_mvbox_folder", configured_mvbox_folder);
res.insert("mdns_enabled", mdns_enabled.to_string());
res.insert("e2ee_enabled", e2ee_enabled.to_string());
res.insert("bcc_self", bcc_self.to_string());
res.insert(
"private_key_count",
prv_key_cnt.unwrap_or_default().to_string(),
@@ -330,7 +289,7 @@ impl Context {
Ok(ret)
},
)
.unwrap_or_default()
.unwrap()
}
#[allow(non_snake_case)]
@@ -374,7 +333,7 @@ impl Context {
}
pub fn is_sentbox(&self, folder_name: impl AsRef<str>) -> bool {
let sentbox_name = self.sql.get_raw_config(self, "configured_sentbox_folder");
let sentbox_name = self.sql.get_config(self, "configured_sentbox_folder");
if let Some(name) = sentbox_name {
name == folder_name.as_ref()
} else {
@@ -383,7 +342,7 @@ impl Context {
}
pub fn is_mvbox(&self, folder_name: impl AsRef<str>) -> bool {
let mvbox_name = self.sql.get_raw_config(self, "configured_mvbox_folder");
let mvbox_name = self.sql.get_config(self, "configured_mvbox_folder");
if let Some(name) = mvbox_name {
name == folder_name.as_ref()
@@ -393,7 +352,12 @@ impl Context {
}
pub fn do_heuristics_moves(&self, folder: &str, msg_id: u32) {
if !self.get_config_bool(Config::MvboxMove) {
if self
.sql
.get_config_int(self, "mvbox_move")
.unwrap_or_else(|| 1)
== 0
{
return;
}
@@ -474,7 +438,6 @@ pub fn get_version_str() -> &'static str {
mod tests {
use super::*;
use crate::dc_tools::*;
use crate::test_utils::*;
#[test]
@@ -512,51 +475,6 @@ mod tests {
assert!(res.is_err());
}
#[test]
fn test_new_blob_file() {
let t = dummy_context();
let context = t.ctx;
let x = &context.new_blob_file("hello", b"data").unwrap();
assert!(dc_file_exist(&context, x));
assert!(x.starts_with("$BLOBDIR"));
assert!(dc_read_file(&context, x).unwrap() == b"data");
let y = &context.new_blob_file("hello", b"data").unwrap();
assert!(dc_file_exist(&context, y));
assert!(y.starts_with("$BLOBDIR/hello-"));
let x = &context.new_blob_file("xyz/hello.png", b"data").unwrap();
assert!(dc_file_exist(&context, x));
assert_eq!(x, "$BLOBDIR/hello.png");
let y = &context.new_blob_file("hello\\world.png", b"data").unwrap();
assert!(dc_file_exist(&context, y));
assert_eq!(y, "$BLOBDIR/world.png");
}
#[test]
fn test_new_blob_file_long_names() {
let t = dummy_context();
let context = t.ctx;
let s = "12312312039182039182039812039810293810293810293810293801293801293123123";
let x = &context.new_blob_file(s, b"data").unwrap();
println!("blobfilename '{}'", x);
println!("xxxxfilename '{}'", s);
assert!(x.len() < s.len());
assert!(dc_file_exist(&context, x));
assert!(x.starts_with("$BLOBDIR"));
}
#[test]
fn test_new_blob_file_unicode() {
let t = dummy_context();
let context = t.ctx;
let s = "helloäworld.qwe";
let x = &context.new_blob_file(s, b"data").unwrap();
assert_eq!(x, "$BLOBDIR/hello-world.qwe");
assert_eq!(dc_read_file(&context, x).unwrap(), b"data");
}
#[test]
fn test_sqlite_parent_not_exists() {
let tmp = tempfile::tempdir().unwrap();

View File

@@ -11,6 +11,7 @@ use mmime::mailmime::content::*;
use mmime::mailmime::disposition::*;
use mmime::mailmime::types::*;
use mmime::mailmime::*;
use mmime::mmapstring::*;
use mmime::other::*;
use crate::constants::Viewtype;
@@ -24,14 +25,13 @@ use crate::error::Error;
use crate::location;
use crate::param::*;
use crate::stock::StockMessage;
use crate::wrapmime;
#[derive(Debug)]
pub struct MimeParser<'a> {
pub context: &'a Context,
pub parts: Vec<Part>,
pub mimeroot: *mut Mailmime,
pub header: HashMap<String, *mut mailimf_field>,
pub header: HashMap<String, mailimf_field>,
pub header_root: *mut mailimf_fields,
pub header_protected: *mut mailimf_fields,
pub subject: Option<String>,
@@ -124,10 +124,10 @@ impl<'a> MimeParser<'a> {
self.parse_mime_recursive(self.mimeroot);
if let Some(field) = self.lookup_field("Subject") {
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
let subj = (*(*field).fld_data.fld_subject).sbj_value;
if let mailimf_field::Subject(subject) = *field {
let subj = (*subject).sbj_value;
self.subject = as_opt_str(subj).map(dc_decode_header_words);
self.subject = as_opt_str(subj).map(dc_decode_header_words_safe);
}
}
@@ -164,17 +164,21 @@ impl<'a> MimeParser<'a> {
}
}
}
} else if let Some(optional_field) = self.lookup_optional_field("Chat-Content") {
if optional_field == "location-streaming-enabled" {
self.is_system_message = SystemMessage::LocationStreamingEnabled;
} else {
if let Some(optional_field) = self.lookup_optional_field("Chat-Content") {
if optional_field == "location-streaming-enabled" {
self.is_system_message = SystemMessage::LocationStreamingEnabled;
}
}
}
if self.lookup_field("Chat-Group-Image").is_some() && !self.parts.is_empty() {
let textpart = &self.parts[0];
if textpart.typ == Viewtype::Text && self.parts.len() >= 2 {
let imgpart = &mut self.parts[1];
if imgpart.typ == Viewtype::Image {
imgpart.is_meta = true;
if textpart.typ == Viewtype::Text {
if self.parts.len() >= 2 {
let imgpart = &mut self.parts[1];
if imgpart.typ == Viewtype::Image {
imgpart.is_meta = true;
}
}
}
}
@@ -185,7 +189,6 @@ impl<'a> MimeParser<'a> {
textpart.typ == Viewtype::Text
&& (filepart.typ == Viewtype::Image
|| filepart.typ == Viewtype::Gif
|| filepart.typ == Viewtype::Sticker
|| filepart.typ == Viewtype::Audio
|| filepart.typ == Viewtype::Voice
|| filepart.typ == Viewtype::Video
@@ -253,14 +256,6 @@ impl<'a> MimeParser<'a> {
part_mut.typ = Viewtype::Voice;
}
}
if self.parts[0].typ == Viewtype::Image {
if let Some(content_type) = self.lookup_optional_field("Chat-Content") {
if content_type == "sticker" {
let part_mut = &mut self.parts[0];
part_mut.typ = Viewtype::Sticker;
}
}
}
let part = &self.parts[0];
if part.typ == Viewtype::Audio
|| part.typ == Viewtype::Voice
@@ -282,7 +277,7 @@ impl<'a> MimeParser<'a> {
if self.get_last_nonmeta().is_some() {
let mut mb_list: *mut mailimf_mailbox_list = ptr::null_mut();
let mut index_0 = 0;
let dn_field_c = CString::new(dn_field).unwrap_or_default();
let dn_field_c = CString::new(dn_field).unwrap();
if mailimf_mailbox_list_parse(
dn_field_c.as_ptr(),
@@ -292,14 +287,11 @@ impl<'a> MimeParser<'a> {
) == MAILIMF_NO_ERROR as libc::c_int
&& !mb_list.is_null()
{
if let Some(dn_to_addr) = wrapmime::mailimf_find_first_addr(mb_list) {
if let Some(dn_to_addr) = mailimf_find_first_addr(mb_list) {
if let Some(from_field) = self.lookup_field("From") {
if (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int
&& !(*from_field).fld_data.fld_from.is_null()
{
let from_addr = wrapmime::mailimf_find_first_addr(
(*(*from_field).fld_data.fld_from).frm_mb_list,
);
if let mailimf_field::From(from) = *from_field {
let from_addr =
mailimf_find_first_addr((*from).frm_mb_list);
if let Some(from_addr) = from_addr {
if from_addr == dn_to_addr {
if let Some(part_4) = self.get_last_nonmeta() {
@@ -338,44 +330,24 @@ impl<'a> MimeParser<'a> {
/* the following functions can be used only after a call to parse() */
pub fn lookup_field(&self, field_name: &str) -> Option<*mut mailimf_field> {
match self.header.get(field_name) {
Some(v) => {
if v.is_null() {
None
} else {
Some(*v)
}
}
None => None,
}
pub fn lookup_field(&self, field_name: &str) -> Option<&mailimf_field> {
self.header.get(field_name)
}
pub fn lookup_optional_field(&self, field_name: &str) -> Option<String> {
if let Some(field) = self.lookup_field_typ(field_name, MAILIMF_FIELD_OPTIONAL_FIELD) {
let val = unsafe { (*field).fld_data.fld_optional_field };
if val.is_null() {
return None;
} else {
return Some(unsafe { to_string_lossy((*val).fld_value) });
if let Some(field) = self.lookup_field(field_name) {
if let mailimf_field::OptionalField(val) = *field {
if val.is_null() {
return None;
} else {
return Some(unsafe { to_string_lossy((*val).value) });
}
}
}
None
}
pub fn lookup_field_typ(&self, name: &str, typ: u32) -> Option<*const mailimf_field> {
if let Some(field) = self.lookup_field(name) {
if unsafe { (*field).fld_type } == typ as libc::c_int {
Some(field)
} else {
None
}
} else {
None
}
}
unsafe fn parse_mime_recursive(&mut self, mime: *mut Mailmime) -> bool {
if mime.is_null() {
return false;
@@ -595,7 +567,7 @@ impl<'a> MimeParser<'a> {
return false;
}
let mut decoded_data = match wrapmime::mailmime_transfer_decode(mime) {
let mut decoded_data = match mailmime_transfer_decode(mime) {
Ok(decoded_data) => decoded_data,
Err(_) => {
// Note that it's now always an error - might be no data
@@ -620,12 +592,9 @@ impl<'a> MimeParser<'a> {
&& strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char) != 0i32
&& strcmp(charset, b"UTF-8\x00" as *const u8 as *const libc::c_char) != 0i32
{
if let Some(encoding) = Charset::for_label(
CStr::from_ptr(charset)
.to_str()
.unwrap_or_default()
.as_bytes(),
) {
if let Some(encoding) =
Charset::for_label(CStr::from_ptr(charset).to_str().unwrap().as_bytes())
{
let (res, _, _) = encoding.decode(&decoded_data);
if res.is_empty() {
/* no error - but nothing to add */
@@ -712,7 +681,7 @@ impl<'a> MimeParser<'a> {
// might be a wrongly encoded filename
let s = to_string_lossy((*dsp_param).pa_data.pa_filename);
// this is used only if the parts buffer stays empty
desired_filename = dc_decode_header_words(&s)
desired_filename = dc_decode_header_words_safe(&s)
}
}
}
@@ -784,34 +753,28 @@ impl<'a> MimeParser<'a> {
decoded_data: &[u8],
desired_filename: &str,
) {
/* write decoded data to new blob file */
let bpath = match self.context.new_blob_file(desired_filename, decoded_data) {
Ok(path) => path,
Err(err) => {
error!(
self.context,
"Could not add blob for mime part {}, error {}", desired_filename, err
);
return;
}
};
/* create a free file name to use */
let path_filename = dc_get_fine_path_filename(self.context, "$BLOBDIR", desired_filename);
let mut part = Part::default();
part.typ = msg_type;
part.mimetype = mime_type;
part.bytes = decoded_data.len() as libc::c_int;
part.param.set(Param::File, bpath);
if let Some(raw_mime) = raw_mime {
part.param.set(Param::MimeType, raw_mime);
}
if mime_type == DC_MIMETYPE_IMAGE {
if let Ok((width, height)) = dc_get_filemeta(decoded_data) {
part.param.set_int(Param::Width, width as i32);
part.param.set_int(Param::Height, height as i32);
/* copy data to file */
if dc_write_file(self.context, &path_filename, decoded_data) {
let mut part = Part::default();
part.typ = msg_type;
part.mimetype = mime_type;
part.bytes = decoded_data.len() as libc::c_int;
part.param.set(Param::File, path_filename.to_string_lossy());
if let Some(raw_mime) = raw_mime {
part.param.set(Param::MimeType, raw_mime);
}
if mime_type == DC_MIMETYPE_IMAGE {
if let Ok((width, height)) = dc_get_filemeta(decoded_data) {
part.param.set_int(Param::Width, width as i32);
part.param.set_int(Param::Height, height as i32);
}
}
self.do_add_single_part(part);
}
self.do_add_single_part(part);
}
fn do_add_single_part(&mut self, mut part: Part) {
@@ -843,30 +806,23 @@ impl<'a> MimeParser<'a> {
}
let mut sender_equals_recipient = false;
let mut fld_from: *const mailimf_from = ptr::null();
/* get From: and check there is exactly one sender */
let fld = wrapmime::mailimf_find_field(self.header_root, MAILIMF_FIELD_FROM as libc::c_int);
if !(fld.is_null()
|| {
fld_from = (*fld).fld_data.fld_from;
fld_from.is_null()
}
|| (*fld_from).frm_mb_list.is_null()
|| (*(*fld_from).frm_mb_list).mb_list.is_null()
|| (*(*(*fld_from).frm_mb_list).mb_list).count != 1i32)
{
let mb = (if !(*(*(*fld_from).frm_mb_list).mb_list).first.is_null() {
(*(*(*(*fld_from).frm_mb_list).mb_list).first).data
} else {
ptr::null_mut()
}) as *mut mailimf_mailbox;
if let Some(fld_from) = mailimf_find_from_field(self.header_root) {
if (*fld_from).frm_mb_list.is_null() || (*(*(*fld_from).frm_mb_list).0).len() != 1 {
let mb = (*(*(*fld_from).frm_mb_list).0)
.first()
.map(|v| *v)
.unwrap_or_else(|| ptr::null_mut());
if !mb.is_null() {
let from_addr_norm = addr_normalize(as_str((*mb).mb_addr_spec));
let recipients = wrapmime::mailimf_get_recipients(self.header_root);
if recipients.len() == 1 && recipients.contains(from_addr_norm) {
sender_equals_recipient = true;
if !mb.is_null() {
let from_addr_norm = addr_normalize(as_str((*mb).addr_spec));
let recipients = mailimf_get_recipients(self.header_root);
if recipients.len() == 1 {
if recipients.contains(from_addr_norm) {
sender_equals_recipient = true;
}
}
}
}
}
@@ -889,11 +845,10 @@ impl<'a> MimeParser<'a> {
pub fn get_rfc724_mid(&mut self) -> Option<String> {
// get Message-ID from header
if let Some(field) = self.lookup_field_typ("Message-ID", MAILIMF_FIELD_MESSAGE_ID) {
unsafe {
let fld_message_id = (*field).fld_data.fld_message_id;
if !fld_message_id.is_null() {
return Some(to_string_lossy((*fld_message_id).mid_value));
if let Some(field) = self.lookup_field("Message-ID") {
if let mailimf_field::MessageId(id) = *field {
if !id.is_null() {
return Some(unsafe { to_string((*id).mid_value) });
}
}
}
@@ -923,31 +878,44 @@ pub struct Part {
pub param: Params,
}
unsafe fn hash_header(out: &mut HashMap<String, *mut mailimf_field>, in_0: *const mailimf_fields) {
pub fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> Option<String> {
if mb_list.is_null() {
return None;
}
for mb in unsafe { &(*mb_list).0 } {
let mb = *mb;
if !mb.is_null() && !unsafe { (*mb).addr_spec.is_null() } {
let addr = unsafe { as_str((*mb).addr_spec) };
return Some(addr_normalize(addr).to_string());
}
}
None
}
unsafe fn hash_header(out: &mut HashMap<String, mailimf_field>, in_0: *const mailimf_fields) {
if in_0.is_null() {
return;
}
for cur in (*(*in_0).fld_list).into_iter() {
let field = cur as *mut mailimf_field;
// TODO match on enums /rtn
let key = match (*field).fld_type as libc::c_uint {
MAILIMF_FIELD_RETURN_PATH => Some("Return-Path".to_string()),
MAILIMF_FIELD_ORIG_DATE => Some("Date".to_string()),
MAILIMF_FIELD_FROM => Some("From".to_string()),
MAILIMF_FIELD_SENDER => Some("Sender".to_string()),
MAILIMF_FIELD_REPLY_TO => Some("Reply-To".to_string()),
MAILIMF_FIELD_TO => Some("To".to_string()),
MAILIMF_FIELD_CC => Some("Cc".to_string()),
MAILIMF_FIELD_BCC => Some("Bcc".to_string()),
MAILIMF_FIELD_MESSAGE_ID => Some("Message-ID".to_string()),
MAILIMF_FIELD_IN_REPLY_TO => Some("In-Reply-To".to_string()),
MAILIMF_FIELD_REFERENCES => Some("References".to_string()),
MAILIMF_FIELD_SUBJECT => Some("Subject".to_string()),
MAILIMF_FIELD_OPTIONAL_FIELD => {
for field in &(*in_0).0 {
use mailimf_field::*;
let key = match *field {
ReturnPath(_) => Some("Return-Path".to_string()),
OrigDate(_) => Some("Date".to_string()),
From(_) => Some("From".to_string()),
Sender(_) => Some("Sender".to_string()),
ReplyTo(_) => Some("Reply-To".to_string()),
To(_) => Some("To".to_string()),
Cc(_) => Some("Cc".to_string()),
Bcc(_) => Some("Bcc".to_string()),
MessageId(_) => Some("Message-ID".to_string()),
InReplyTo(_) => Some("In-Reply-To".to_string()),
References(_) => Some("References".to_string()),
Subject(_) => Some("Subject".to_string()),
OptionalField(optional_field) => {
// MAILIMF_FIELD_OPTIONAL_FIELD
let optional_field = (*field).fld_data.fld_optional_field;
// XXX the optional field sometimes contains invalid UTF8
// which should not happen (according to the mime standard).
// This might point to a bug in our mime parsing/processing
@@ -955,7 +923,7 @@ unsafe fn hash_header(out: &mut HashMap<String, *mut mailimf_field>, in_0: *cons
// anyway we just use a lossy conversion.
if !optional_field.is_null() {
Some(to_string_lossy((*optional_field).fld_name))
Some(to_string_lossy((*optional_field).name))
} else {
None
}
@@ -964,9 +932,9 @@ unsafe fn hash_header(out: &mut HashMap<String, *mut mailimf_field>, in_0: *cons
};
if let Some(key) = key {
if !out.contains_key(&key) || // key already exists, only overwrite known types (protected headers)
(*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key.starts_with("Chat-")
field.is_optional_field() || key.starts_with("Chat-")
{
out.insert(key, field);
out.insert(key, *field);
}
}
}
@@ -993,12 +961,14 @@ unsafe fn mailmime_get_mime_type(mime: *mut Mailmime) -> (libc::c_int, Viewtype,
) == 0i32
{
return (DC_MIMETYPE_TEXT_PLAIN, Viewtype::Text, None);
} else if strcmp(
(*c).ct_subtype,
b"html\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return (DC_MIMETYPE_TEXT_HTML, Viewtype::Text, None);
} else {
if strcmp(
(*c).ct_subtype,
b"html\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
return (DC_MIMETYPE_TEXT_HTML, Viewtype::Text, None);
}
}
}
@@ -1142,12 +1112,199 @@ pub unsafe fn mailmime_find_ct_parameter(
ptr::null_mut()
}
pub unsafe fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result<Vec<u8>, Error> {
ensure!(!mime.is_null(), "invalid inputs");
let mut mime_transfer_encoding = MAILMIME_MECHANISM_BINARY as libc::c_int;
let mime_data = (*mime).mm_data.mm_single;
if !(*mime).mm_mime_fields.is_null() {
for cur in (*(*(*mime).mm_mime_fields).fld_list).into_iter() {
let field = cur as *mut mailmime_field;
if !field.is_null()
&& (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
&& !(*field).fld_data.fld_encoding.is_null()
{
mime_transfer_encoding = (*(*field).fld_data.fld_encoding).enc_type;
break;
}
}
}
if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int
|| mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int
|| mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int
{
let decoded_data = (*mime_data).dt_data.dt_text.dt_data;
let decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
if decoded_data.is_null() || decoded_data_bytes <= 0 {
bail!("No data to decode found");
} else {
let result = std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes);
return Ok(result.to_vec());
}
}
let mut current_index = 0;
let mut transfer_decoding_buffer = ptr::null_mut();
let mut decoded_data_bytes = 0;
let r = mailmime_part_parse(
(*mime_data).dt_data.dt_text.dt_data,
(*mime_data).dt_data.dt_text.dt_length,
&mut current_index,
mime_transfer_encoding,
&mut transfer_decoding_buffer,
&mut decoded_data_bytes,
);
if r == MAILIMF_NO_ERROR as libc::c_int
&& !transfer_decoding_buffer.is_null()
&& decoded_data_bytes > 0
{
let result =
std::slice::from_raw_parts(transfer_decoding_buffer as *const u8, decoded_data_bytes)
.to_vec();
mmap_string_unref(transfer_decoding_buffer);
return Ok(result);
}
Err(format_err!("Failed to to decode"))
}
pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<String> {
/* returned addresses are normalized. */
let mut recipients: HashSet<String> = Default::default();
for fld in unsafe { &(*imffields).0 } {
let mut addr_list: *mut mailimf_address_list = ptr::null_mut();
match *fld {
mailimf_field::To(fld_to) => {
if !fld_to.is_null() {
addr_list = unsafe { (*fld_to).to_addr_list };
}
}
mailimf_field::Cc(fld_cc) => {
if !fld_cc.is_null() {
addr_list = unsafe { (*fld_cc).cc_addr_list };
}
}
_ => {}
}
if !addr_list.is_null() {
for adr in unsafe { &(*(*addr_list).0) } {
if adr.is_null() {
continue;
}
match unsafe { &**adr } {
mailimf_address::Mailbox(mailbox) => {
mailimf_get_recipients_add_addr(&mut recipients, *mailbox);
}
mailimf_address::Group(group) => {
if !group.is_null() && unsafe { !(**group).mb_list.is_null() } {
for mb in unsafe { &((*(**group).mb_list).0) } {
mailimf_get_recipients_add_addr(&mut recipients, *mb);
}
}
}
}
}
}
}
recipients
}
fn mailimf_get_recipients_add_addr(recipients: &mut HashSet<String>, mb: *mut mailimf_mailbox) {
if !mb.is_null() {
let addr_norm = addr_normalize(as_str(unsafe { (*mb).addr_spec }));
recipients.insert(addr_norm.into());
}
}
pub fn mailimf_find_from_field(header: *mut mailimf_fields) -> Option<*mut mailimf_from> {
if header.is_null() {
return None;
}
unsafe { (*header).0.iter() }.find_map(|field| {
if let mailimf_field::From(f) = field {
Some(*f)
} else {
None
}
})
}
pub fn mailimf_find_orig_date_field(header: *mut mailimf_fields) -> Option<*mut mailimf_orig_date> {
if header.is_null() {
return None;
}
unsafe { (*header).0.iter() }.find_map(|field| {
if let mailimf_field::OrigDate(d) = field {
Some(*d)
} else {
None
}
})
}
/*the result is a pointer to mime, must not be freed*/
pub unsafe fn mailmime_find_mailimf_fields(mime: *mut Mailmime) -> *mut mailimf_fields {
if mime.is_null() {
return ptr::null_mut();
}
match (*mime).mm_type as _ {
MAILMIME_MULTIPLE => {
for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() {
let header = mailmime_find_mailimf_fields(cur_data as *mut _);
if !header.is_null() {
return header;
}
}
}
MAILMIME_MESSAGE => return (*mime).mm_data.mm_message.mm_fields,
_ => {}
}
ptr::null_mut()
}
pub unsafe fn mailimf_find_optional_field(
header: *mut mailimf_fields,
wanted_fld_name: *const libc::c_char,
) -> *mut mailimf_optional_field {
if header.is_null() {
return ptr::null_mut();
}
for field in &(*header).0 {
if let mailimf_field::OptionalField(optional_field) = *field {
if !optional_field.is_null()
&& !(*optional_field).name.is_null()
&& !(*optional_field).value.is_null()
&& strcasecmp((*optional_field).name, wanted_fld_name) == 0i32
{
return optional_field;
}
}
}
ptr::null_mut()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::*;
use proptest::prelude::*;
use std::ffi::CStr;
#[test]
fn test_mailmime_parse() {
@@ -1157,65 +1314,41 @@ mod tests {
let mut mime: *mut Mailmime = ptr::null_mut();
let mut dummy = 0;
let res = mailmime_parse(txt, strlen(txt), &mut dummy, &mut mime);
assert_eq!(res, MAIL_NO_ERROR as libc::c_int);
assert!(!mime.is_null());
let fields: *mut mailimf_fields = wrapmime::mailmime_find_mailimf_fields(mime);
let fields: *mut mailimf_fields = mailmime_find_mailimf_fields(mime);
assert!(!fields.is_null());
let mut of_a: *mut mailimf_optional_field = wrapmime::mailimf_find_optional_field(
let mut of_a: *mut mailimf_optional_field = mailimf_find_optional_field(
fields,
b"fielda\x00" as *const u8 as *const libc::c_char,
);
assert!(!of_a.is_null());
assert!(!(*of_a).fld_value.is_null());
assert_eq!(
CStr::from_ptr((*of_a).fld_name as *const libc::c_char)
.to_str()
.unwrap(),
"FieldA",
);
assert_eq!(
CStr::from_ptr((*of_a).fld_value as *const libc::c_char)
.to_str()
.unwrap(),
"ValueA",
);
assert!(!(*of_a).value.is_null());
assert_eq!(as_str((*of_a).name), "FieldA");
assert_eq!(as_str((*of_a).value), "ValueA");
of_a = wrapmime::mailimf_find_optional_field(
of_a = mailimf_find_optional_field(
fields,
b"FIELDA\x00" as *const u8 as *const libc::c_char,
);
assert!(!of_a.is_null());
assert!(!(*of_a).fld_value.is_null());
assert_eq!(
CStr::from_ptr((*of_a).fld_name as *const libc::c_char)
.to_str()
.unwrap(),
"FieldA",
);
assert_eq!(
CStr::from_ptr((*of_a).fld_value as *const libc::c_char)
.to_str()
.unwrap(),
"ValueA",
);
assert!(!(*of_a).value.is_null());
assert_eq!(as_str((*of_a).name), "FieldA");
assert_eq!(as_str((*of_a).value), "ValueA");
let of_b: *mut mailimf_optional_field = wrapmime::mailimf_find_optional_field(
let of_b = mailimf_find_optional_field(
fields,
b"FieldB\x00" as *const u8 as *const libc::c_char,
);
assert!(!of_b.is_null());
assert!(!(*of_b).fld_value.is_null());
assert_eq!(
CStr::from_ptr((*of_b).fld_value as *const libc::c_char)
.to_str()
.unwrap(),
"ValueB",
);
assert!(!(*of_b).value.is_null());
assert_eq!(as_str((*of_b).value), "ValueB");
mailmime_free(mime);
}
@@ -1226,9 +1359,7 @@ mod tests {
let context = dummy_context();
let raw = include_bytes!("../test-data/message/issue_523.txt");
let mut mimeparser = MimeParser::new(&context.ctx);
unsafe {
mimeparser.parse(&raw[..]).unwrap();
};
unsafe { mimeparser.parse(&raw[..]).unwrap() };
assert_eq!(mimeparser.subject, None);
assert_eq!(mimeparser.parts.len(), 1);
}
@@ -1236,13 +1367,9 @@ mod tests {
proptest! {
#[test]
fn test_dc_mailmime_parse_crash_fuzzy(data in "[!-~\t ]{2000,}") {
// this test doesn't exercise much of dc_mimeparser anymore
// because a missing From-field early aborts parsing
let context = dummy_context();
let mut mimeparser = MimeParser::new(&context.ctx);
unsafe {
assert!(mimeparser.parse(data.as_bytes()).is_err());
}
unsafe { mimeparser.parse(data.as_bytes()).unwrap() };
}
}
@@ -1271,7 +1398,7 @@ mod tests {
fn test_mimeparser_with_context() {
unsafe {
let context = dummy_context();
let raw = b"From: hello\nContent-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00";
let raw = b"Content-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00";
let mut mimeparser = MimeParser::new(&context.ctx);
mimeparser.parse(&raw[..]).unwrap();

View File

@@ -2,9 +2,9 @@ use std::ffi::CString;
use std::ptr;
use itertools::join;
use libc::{free, strcmp};
use mmime::clist::*;
use libc::{free, strcmp, strlen};
use mmime::mailimf::types::*;
use mmime::mailimf::*;
use mmime::mailmime::content::*;
use mmime::mailmime::types::*;
use mmime::mailmime::*;
@@ -12,7 +12,6 @@ use mmime::other::*;
use sha2::{Digest, Sha256};
use crate::chat::{self, Chat};
use crate::config::Config;
use crate::constants::*;
use crate::contact::*;
use crate::context::Context;
@@ -29,7 +28,6 @@ use crate::peerstate::*;
use crate::securejoin::handle_securejoin_handshake;
use crate::sql;
use crate::stock::StockMessage;
use crate::wrapmime;
#[derive(Debug, PartialEq, Eq)]
enum CreateEvent {
@@ -82,7 +80,7 @@ pub unsafe fn dc_receive_imf(
let mut chat_id = 0;
let mut hidden = 0;
let mut needs_delete_job = false;
let mut add_delete_job: libc::c_int = 0;
let mut insert_msg_id = 0;
let mut sent_timestamp = 0;
@@ -120,61 +118,65 @@ pub unsafe fn dc_receive_imf(
}
};
if let Some(field) = mime_parser.lookup_field_typ("Date", MAILIMF_FIELD_ORIG_DATE) {
let orig_date = (*field).fld_data.fld_orig_date;
if !orig_date.is_null() {
// is not yet checked against bad times! we do this later if we have the database information.
sent_timestamp = dc_timestamp_from_date((*orig_date).dt_date_time)
if let Some(field) = mime_parser.lookup_field("Date") {
if let mailimf_field::OrigDate(orig_date) = *field {
if !orig_date.is_null() {
// is not yet checked against bad times! we do this later if we have the database information.
sent_timestamp = dc_timestamp_from_date((*orig_date).dt_date_time)
}
}
}
// get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass)
// or if From: is equal to SELF (in this case, it is any outgoing messages,
// we do not check Return-Path any more as this is unreliable, see issue #150
if let Some(field) = mime_parser.lookup_field_typ("From", MAILIMF_FIELD_FROM) {
let fld_from = (*field).fld_data.fld_from;
if !fld_from.is_null() {
let mut check_self = 0;
let mut from_list = Vec::with_capacity(16);
dc_add_or_lookup_contacts_by_mailbox_list(
context,
(*fld_from).frm_mb_list,
Origin::IncomingUnknownFrom,
&mut from_list,
&mut check_self,
);
if 0 != check_self {
incoming = 0;
if mime_parser.sender_equals_recipient() {
from_id = DC_CONTACT_ID_SELF;
if let Some(field) = mime_parser.lookup_field("From") {
if let mailimf_field::From(fld_from) = *field {
if !fld_from.is_null() {
let mut check_self = 0;
let mut from_list = Vec::with_capacity(16);
dc_add_or_lookup_contacts_by_mailbox_list(
context,
(*fld_from).frm_mb_list,
Origin::IncomingUnknownFrom,
&mut from_list,
&mut check_self,
);
if 0 != check_self {
incoming = 0;
if mime_parser.sender_equals_recipient() {
from_id = DC_CONTACT_ID_SELF;
}
} else if from_list.len() >= 1 {
// if there is no from given, from_id stays 0 which is just fine. These messages
// are very rare, however, we have to add them to the database (they go to the
// "deaddrop" chat) to avoid a re-download from the server. See also [**]
from_id = from_list[0];
incoming_origin =
Contact::get_origin_by_id(context, from_id, &mut from_id_blocked)
}
} else if !from_list.is_empty() {
// if there is no from given, from_id stays 0 which is just fine. These messages
// are very rare, however, we have to add them to the database (they go to the
// "deaddrop" chat) to avoid a re-download from the server. See also [**]
from_id = from_list[0];
incoming_origin = Contact::get_origin_by_id(context, from_id, &mut from_id_blocked)
}
}
}
// Make sure, to_ids starts with the first To:-address (Cc: is added in the loop below pass)
if let Some(field) = mime_parser.lookup_field_typ("To", MAILIMF_FIELD_TO) {
let fld_to = (*field).fld_data.fld_to;
if !fld_to.is_null() {
dc_add_or_lookup_contacts_by_address_list(
context,
(*fld_to).to_addr_list,
if 0 == incoming {
Origin::OutgoingTo
} else if incoming_origin.is_verified() {
Origin::IncomingTo
} else {
Origin::IncomingUnknownTo
},
&mut to_ids,
&mut to_self,
);
if let Some(field) = mime_parser.lookup_field("To") {
if let mailimf_field::To(fld_to) = *field {
if !fld_to.is_null() {
dc_add_or_lookup_contacts_by_address_list(
context,
(*fld_to).to_addr_list,
if 0 == incoming {
Origin::OutgoingTo
} else if incoming_origin.is_verified() {
Origin::IncomingTo
} else {
Origin::IncomingUnknownTo
},
&mut to_ids,
&mut to_self,
);
}
}
}
@@ -219,7 +221,7 @@ pub unsafe fn dc_receive_imf(
&mut chat_id,
&mut to_id,
flags,
&mut needs_delete_job,
&mut add_delete_job,
to_self,
&mut insert_msg_id,
&mut created_db_entries,
@@ -250,7 +252,7 @@ pub unsafe fn dc_receive_imf(
from_id,
sent_timestamp,
&mut rr_event_to_send,
&server_folder,
server_folder,
server_uid,
);
}
@@ -266,8 +268,7 @@ pub unsafe fn dc_receive_imf(
);
}
// if we delete we don't need to try moving messages
if needs_delete_job && !created_db_entries.is_empty() {
if 0 != add_delete_job && !created_db_entries.is_empty() {
job_add(
context,
Action::DeleteMsgOnImap,
@@ -275,8 +276,6 @@ pub unsafe fn dc_receive_imf(
Params::new(),
0,
);
} else {
context.do_heuristics_moves(server_folder.as_ref(), insert_msg_id);
}
info!(
@@ -309,7 +308,7 @@ unsafe fn add_parts(
chat_id: &mut u32,
to_id: &mut u32,
flags: u32,
needs_delete_job: &mut bool,
add_delete_job: &mut libc::c_int,
to_self: i32,
insert_msg_id: &mut u32,
created_db_entries: &mut Vec<(usize, usize)>,
@@ -331,22 +330,23 @@ unsafe fn add_parts(
// collect the rest information, CC: is added to the to-list, BCC: is ignored
// (we should not add BCC to groups as this would split groups. We could add them as "known contacts",
// however, the benefit is very small and this may leak data that is expected to be hidden)
if let Some(field) = mime_parser.lookup_field_typ("Cc", MAILIMF_FIELD_CC) {
let fld_cc = (*field).fld_data.fld_cc;
if !fld_cc.is_null() {
dc_add_or_lookup_contacts_by_address_list(
context,
(*fld_cc).cc_addr_list,
if 0 == incoming {
Origin::OutgoingCc
} else if incoming_origin.is_verified() {
Origin::IncomingCc
} else {
Origin::IncomingUnknownCc
},
to_ids,
std::ptr::null_mut(),
);
if let Some(field) = mime_parser.lookup_field("Cc") {
if let mailimf_field::Cc(fld_cc) = *field {
if !fld_cc.is_null() {
dc_add_or_lookup_contacts_by_address_list(
context,
(*fld_cc).cc_addr_list,
if 0 == incoming {
Origin::OutgoingCc
} else if incoming_origin.is_verified() {
Origin::IncomingCc
} else {
Origin::IncomingUnknownCc
},
to_ids,
std::ptr::null_mut(),
);
}
}
}
@@ -374,7 +374,10 @@ unsafe fn add_parts(
// maybe this can be optimized later, by checking the state before the message body is downloaded
let mut allow_creation = 1;
if mime_parser.is_system_message != SystemMessage::AutocryptSetupMessage && msgrmsg == 0 {
let show_emails = context.get_config_int(Config::ShowEmails);
let show_emails = context
.sql
.get_config_int(context, "show_emails")
.unwrap_or_default();
if show_emails == 0 {
*chat_id = 3;
allow_creation = 0
@@ -404,7 +407,7 @@ unsafe fn add_parts(
let handshake = handle_securejoin_handshake(context, mime_parser, *from_id);
if 0 != handshake & DC_HANDSHAKE_STOP_NORMAL_PROCESSING {
*hidden = 1;
*needs_delete_job = 0 != handshake & DC_HANDSHAKE_ADD_DELETE_JOB;
*add_delete_job = handshake & DC_HANDSHAKE_ADD_DELETE_JOB;
state = MessageState::InSeen;
}
}
@@ -495,12 +498,10 @@ unsafe fn add_parts(
// if the chat_id is blocked,
// for unknown senders and non-delta messages set the state to NOTICED
// to not result in a contact request (this would require the state FRESH)
if Blocked::Not != chat_id_blocked
&& state == MessageState::InFresh
&& !incoming_origin.is_verified()
&& msgrmsg == 0
{
state = MessageState::InNoticed;
if Blocked::Not != chat_id_blocked && state == MessageState::InFresh {
if !incoming_origin.is_verified() && msgrmsg == 0 {
state = MessageState::InNoticed;
}
}
} else {
// Outgoing
@@ -581,28 +582,30 @@ unsafe fn add_parts(
);
// unarchive chat
chat::unarchive(context, *chat_id)?;
chat::unarchive(context, *chat_id).unwrap();
// if the mime-headers should be saved, find out its size
// (the mime-header ends with an empty line)
let save_mime_headers = context.get_config_bool(Config::SaveMimeHeaders);
if let Some(field) = mime_parser.lookup_field_typ("In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) {
let fld_in_reply_to = (*field).fld_data.fld_in_reply_to;
if !fld_in_reply_to.is_null() {
mime_in_reply_to = dc_str_from_clist(
(*(*field).fld_data.fld_in_reply_to).mid_list,
b" \x00" as *const u8 as *const libc::c_char,
)
let save_mime_headers = context.sql.get_config_bool(context, "save_mime_headers");
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
if let mailimf_field::InReplyTo(fld_in_reply_to) = *field {
if !fld_in_reply_to.is_null() {
mime_in_reply_to = dc_str_from_vec(
&(*fld_in_reply_to).0,
b" \x00" as *const u8 as *const libc::c_char,
);
}
}
}
if let Some(field) = mime_parser.lookup_field_typ("References", MAILIMF_FIELD_REFERENCES) {
let fld_references = (*field).fld_data.fld_references;
if !fld_references.is_null() {
mime_references = dc_str_from_clist(
(*(*field).fld_data.fld_references).mid_list,
b" \x00" as *const u8 as *const libc::c_char,
)
if let Some(field) = mime_parser.lookup_field("References") {
if let mailimf_field::References(fld_references) = *field {
if !fld_references.is_null() {
mime_references = dc_str_from_vec(
&(*fld_references).0,
b" \x00" as *const u8 as *const libc::c_char,
)
}
}
}
@@ -629,7 +632,7 @@ unsafe fn add_parts(
}
if let Some(ref msg) = part.msg {
if mime_parser.location_kml.is_some()
if !mime_parser.location_kml.is_none()
&& icnt == 1
&& (msg == "-location-" || msg.is_empty())
{
@@ -688,8 +691,8 @@ unsafe fn add_parts(
} else {
None
},
to_string_lossy(mime_in_reply_to),
to_string_lossy(mime_references),
to_string(mime_in_reply_to),
to_string(mime_references),
])?;
txt_raw = None;
@@ -723,6 +726,7 @@ unsafe fn add_parts(
}
}
context.do_heuristics_moves(server_folder.as_ref(), *insert_msg_id);
cleanup(mime_in_reply_to, mime_references);
Ok(())
@@ -738,7 +742,10 @@ unsafe fn handle_reports(
server_folder: impl AsRef<str>,
server_uid: u32,
) {
let mdns_enabled = context.get_config_bool(Config::MdnsEnabled);
let mdns_enabled = context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| DC_MDNS_DEFAULT_ENABLED);
for report_root in &mime_parser.reports {
let report_root = *report_root;
@@ -757,7 +764,7 @@ unsafe fn handle_reports(
&& (*(*report_root).mm_data.mm_multipart.mm_mp_list).count >= 2
{
// to get a clear functionality, do not show incoming MDNs if the options is disabled
if mdns_enabled {
if 0 != mdns_enabled {
let report_data = (if !if !(*(*report_root).mm_data.mm_multipart.mm_mp_list)
.first
.is_null()
@@ -794,7 +801,7 @@ unsafe fn handle_reports(
b"disposition-notification\x00" as *const u8 as *const libc::c_char,
) == 0
{
if let Ok(report_body) = wrapmime::mailmime_transfer_decode(report_data) {
if let Ok(report_body) = mailmime_transfer_decode(report_data) {
let mut report_parsed = std::ptr::null_mut();
let mut dummy = 0;
@@ -806,32 +813,39 @@ unsafe fn handle_reports(
) == MAIL_NO_ERROR as libc::c_int
&& !report_parsed.is_null()
{
let report_fields =
wrapmime::mailmime_find_mailimf_fields(report_parsed);
let report_fields = mailmime_find_mailimf_fields(report_parsed);
if !report_fields.is_null() {
let of_disposition = wrapmime::mailimf_find_optional_field(
let of_disposition = mailimf_find_optional_field(
report_fields,
b"Disposition\x00" as *const u8 as *const libc::c_char,
);
let of_org_msgid = wrapmime::mailimf_find_optional_field(
let of_org_msgid = mailimf_find_optional_field(
report_fields,
b"Original-Message-ID\x00" as *const u8 as *const libc::c_char,
);
if !of_disposition.is_null()
&& !(*of_disposition).fld_value.is_null()
&& !(*of_disposition).value.is_null()
&& !of_org_msgid.is_null()
&& !(*of_org_msgid).fld_value.is_null()
&& !(*of_org_msgid).value.is_null()
{
if let Ok(rfc724_mid) = wrapmime::parse_message_id(as_str(
(*of_org_msgid).fld_value,
)) {
let mut rfc724_mid_0 = std::ptr::null_mut();
dummy = 0;
if mailimf_msg_id_parse(
(*of_org_msgid).value,
strlen((*of_org_msgid).value),
&mut dummy,
&mut rfc724_mid_0,
) == MAIL_NO_ERROR as libc::c_int
&& !rfc724_mid_0.is_null()
{
let mut chat_id_0 = 0;
let mut msg_id = 0;
if message::mdn_from_ext(
context,
from_id,
&rfc724_mid,
as_str(rfc724_mid_0),
sent_timestamp,
&mut chat_id_0,
&mut msg_id,
@@ -839,6 +853,7 @@ unsafe fn handle_reports(
rr_event_to_send.push((chat_id_0, msg_id));
}
mdn_consumed = (msg_id != 0) as libc::c_int;
free(rfc724_mid_0.cast());
}
}
}
@@ -852,7 +867,12 @@ unsafe fn handle_reports(
let mut param = Params::new();
param.set(Param::ServerFolder, server_folder.as_ref());
param.set_int(Param::ServerUid, server_uid as i32);
if mime_parser.is_send_by_messenger && context.get_config_bool(Config::MvboxMove) {
if mime_parser.is_send_by_messenger
&& 0 != context
.sql
.get_config_int(context, "mvbox_move")
.unwrap_or_else(|| 1)
{
param.set_int(Param::AlsoMove, 1);
}
job_add(context, Action::MarkseenMdnOnImap, 0, param, 0);
@@ -1008,38 +1028,36 @@ unsafe fn create_or_lookup_group(
}
if grpid.is_empty() {
if let Some(field) = mime_parser.lookup_field_typ("Message-ID", MAILIMF_FIELD_MESSAGE_ID) {
let fld_message_id = (*field).fld_data.fld_message_id;
if !fld_message_id.is_null() {
if let Some(extracted_grpid) =
dc_extract_grpid_from_rfc724_mid(as_str((*fld_message_id).mid_value))
{
grpid = extracted_grpid.to_string();
} else {
grpid = "".to_string();
if let Some(field) = mime_parser.lookup_field("Message-ID") {
if let mailimf_field::MessageId(fld_message_id) = *field {
if !fld_message_id.is_null() {
if let Some(extracted_grpid) =
dc_extract_grpid_from_rfc724_mid(as_str((*fld_message_id).mid_value))
{
grpid = extracted_grpid.to_string();
} else {
grpid = "".to_string();
}
}
}
}
if grpid.is_empty() {
if let Some(field) =
mime_parser.lookup_field_typ("In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO)
{
let fld_in_reply_to = (*field).fld_data.fld_in_reply_to;
if !fld_in_reply_to.is_null() {
grpid = to_string_lossy(dc_extract_grpid_from_rfc724_mid_list(
(*fld_in_reply_to).mid_list,
));
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
if let mailimf_field::InReplyTo(fld_in_reply_to) = *field {
if !fld_in_reply_to.is_null() {
grpid =
to_string(dc_extract_grpid_from_rfc724_mid_list(&(*fld_in_reply_to).0));
}
}
}
if grpid.is_empty() {
if let Some(field) =
mime_parser.lookup_field_typ("References", MAILIMF_FIELD_REFERENCES)
{
let fld_references = (*field).fld_data.fld_references;
if !fld_references.is_null() {
grpid = to_string_lossy(dc_extract_grpid_from_rfc724_mid_list(
(*fld_references).mid_list,
));
if let Some(field) = mime_parser.lookup_field("References") {
if let mailimf_field::References(fld_references) = *field {
if !fld_references.is_null() {
grpid = to_string(dc_extract_grpid_from_rfc724_mid_list(
&(*fld_references).0,
));
}
}
}
@@ -1062,7 +1080,7 @@ unsafe fn create_or_lookup_group(
}
if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Name") {
grpname = Some(dc_decode_header_words(&optional_field));
grpname = Some(dc_decode_header_words_safe(&optional_field));
}
if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Removed") {
X_MrRemoveFromGrp = Some(optional_field);
@@ -1154,7 +1172,8 @@ unsafe fn create_or_lookup_group(
group_explicitly_left = chat::is_group_explicitly_left(context, &grpid).unwrap_or_default();
let self_addr = context
.get_config(Config::ConfiguredAddr)
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if chat_id == 0
&& !mime_parser.is_mailinglist_message()
@@ -1264,7 +1283,7 @@ unsafe fn create_or_lookup_group(
} else {
chat.param.set(Param::ProfileImage, grpimage);
}
chat.update_param(context).unwrap_or_default();
chat.update_param(context).unwrap();
send_EVENT_CHAT_MODIFIED = 1;
}
}
@@ -1475,7 +1494,7 @@ fn create_group_record(
sql::get_rowid(context, &context.sql, "chats", "grpid", grpid.as_ref())
}
fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String {
fn create_adhoc_grp_id(context: &Context, member_ids: &Vec<u32>) -> String {
/* algorithm:
- sort normalized, lowercased, e-mail addresses alphabetically
- put all e-mail addresses into a single string, separate the address by a single comma
@@ -1484,7 +1503,8 @@ fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String {
*/
let member_ids_str = join(member_ids.iter().map(|x| x.to_string()), ",");
let member_cs = context
.get_config(Config::ConfiguredAddr)
.sql
.get_config(context, "configured_addr")
.unwrap_or_else(|| "no-self".to_string())
.to_lowercase();
@@ -1575,7 +1595,7 @@ fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec<
}
Ok(())
}
).unwrap_or_default(); // TODO: better error handling
).unwrap(); // TODO: better error handling
}
}
@@ -1586,7 +1606,7 @@ fn check_verified_properties(
context: &Context,
mimeparser: &MimeParser,
from_id: u32,
to_ids: &[u32],
to_ids: &Vec<u32>,
) -> Result<()> {
let contact = Contact::load_from_db(context, from_id)?;
@@ -1654,7 +1674,7 @@ fn check_verified_properties(
let fp = peerstate.gossip_key_fingerprint.clone();
if let Some(fp) = fp {
peerstate.set_verified(0, &fp, 2);
peerstate.save_to_db(&context.sql, false)?;
peerstate.save_to_db(&context.sql, false).unwrap();
is_verified = true;
}
}
@@ -1683,20 +1703,16 @@ unsafe fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimePars
/* check if the message is a reply to a known message; the replies are identified by the Message-ID from
`In-Reply-To`/`References:` (to support non-Delta-Clients) or from `Chat-Predecessor:` (Delta clients, see comment in dc_chat.c) */
if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Predecessor") {
let optional_field_c = CString::new(optional_field).unwrap_or_default();
let optional_field_c = CString::new(optional_field).unwrap();
if 0 != is_known_rfc724_mid(context, optional_field_c.as_ptr()) {
return 1;
}
}
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
if (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int {
let fld_in_reply_to = (*field).fld_data.fld_in_reply_to;
if let mailimf_field::InReplyTo(fld_in_reply_to) = *field {
if !fld_in_reply_to.is_null() {
if is_known_rfc724_mid_in_list(
context,
(*(*field).fld_data.fld_in_reply_to).mid_list,
) {
if is_known_rfc724_mid_in_list(context, &(*fld_in_reply_to).0) {
return 1;
}
}
@@ -1704,15 +1720,11 @@ unsafe fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimePars
}
if let Some(field) = mime_parser.lookup_field("References") {
if (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int {
let fld_references = (*field).fld_data.fld_references;
if !fld_references.is_null()
&& is_known_rfc724_mid_in_list(
context,
(*(*field).fld_data.fld_references).mid_list,
)
{
return 1;
if let mailimf_field::References(fld_references) = *field {
if !fld_references.is_null() {
if is_known_rfc724_mid_in_list(context, &(*fld_references).0) {
return 1;
}
}
}
}
@@ -1720,18 +1732,17 @@ unsafe fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimePars
0
}
unsafe fn is_known_rfc724_mid_in_list(context: &Context, mid_list: *const clist) -> bool {
if mid_list.is_null() {
return false;
}
for data in &*mid_list {
if is_known_rfc724_mid(context, data.cast()) != 0 {
unsafe fn is_known_rfc724_mid_in_list(
context: &Context,
mid_list: &Vec<*mut libc::c_char>,
) -> bool {
for data in mid_list {
if 0 != is_known_rfc724_mid(context, *data) {
return true;
}
}
false
return false;
}
/// Check if a message is a reply to a known message (messenger or non-messenger).
@@ -1762,13 +1773,9 @@ unsafe fn dc_is_reply_to_messenger_message(
- no check for the Chat-* headers (function is only called if it is no messenger message itself) */
if let Some(field) = mime_parser.lookup_field("In-Reply-To") {
if (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int {
let fld_in_reply_to = (*field).fld_data.fld_in_reply_to;
if let mailimf_field::InReplyTo(fld_in_reply_to) = *field {
if !fld_in_reply_to.is_null() {
if 0 != is_msgrmsg_rfc724_mid_in_list(
context,
(*(*field).fld_data.fld_in_reply_to).mid_list,
) {
if 0 != is_msgrmsg_rfc724_mid_in_list(context, &(*fld_in_reply_to).0) {
return 1;
}
}
@@ -1776,13 +1783,9 @@ unsafe fn dc_is_reply_to_messenger_message(
}
if let Some(field) = mime_parser.lookup_field("References") {
if (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int {
let fld_references: *mut mailimf_references = (*field).fld_data.fld_references;
if let mailimf_field::References(fld_references) = *field {
if !fld_references.is_null() {
if 0 != is_msgrmsg_rfc724_mid_in_list(
context,
(*(*field).fld_data.fld_references).mid_list,
) {
if 0 != is_msgrmsg_rfc724_mid_in_list(context, &(*fld_references).0) {
return 1;
}
}
@@ -1792,25 +1795,13 @@ unsafe fn dc_is_reply_to_messenger_message(
0
}
unsafe fn is_msgrmsg_rfc724_mid_in_list(context: &Context, mid_list: *const clist) -> libc::c_int {
if !mid_list.is_null() {
let mut cur: *mut clistiter = (*mid_list).first;
while !cur.is_null() {
if 0 != is_msgrmsg_rfc724_mid(
context,
if !cur.is_null() {
as_str((*cur).data as *const libc::c_char)
} else {
""
},
) {
return 1;
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
unsafe fn is_msgrmsg_rfc724_mid_in_list(
context: &Context,
mid_list: &Vec<*mut libc::c_char>,
) -> libc::c_int {
for cur in mid_list {
if 0 != is_msgrmsg_rfc724_mid(context, if !cur.is_null() { as_str(*cur) } else { "" }) {
return 1;
}
}
0
@@ -1840,43 +1831,36 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list(
if adr_list.is_null() {
return;
}
let mut cur: *mut clistiter = (*(*adr_list).ad_list).first;
while !cur.is_null() {
let adr: *mut mailimf_address = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailimf_address;
if !adr.is_null() {
if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int {
let mb: *mut mailimf_mailbox = (*adr).ad_data.ad_mailbox;
for adr in &(*(*adr_list).0) {
if adr.is_null() {
continue;
}
match **adr {
mailimf_address::Mailbox(mb) => {
if !mb.is_null() {
add_or_lookup_contact_by_addr(
context,
(*mb).mb_display_name,
(*mb).mb_addr_spec,
origin,
ids,
check_self,
);
}
} 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() {
dc_add_or_lookup_contacts_by_mailbox_list(
context,
(*group).grp_mb_list,
(*mb).display_name,
(*mb).addr_spec,
origin,
ids,
check_self,
);
}
}
mailimf_address::Group(group) => {
if !group.is_null() && !(*group).mb_list.is_null() {
dc_add_or_lookup_contacts_by_mailbox_list(
context,
(*group).mb_list,
origin,
ids,
check_self,
);
}
}
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
@@ -1891,28 +1875,18 @@ unsafe fn dc_add_or_lookup_contacts_by_mailbox_list(
if mb_list.is_null() {
return;
}
let mut cur: *mut clistiter = (*(*mb_list).mb_list).first;
while !cur.is_null() {
let mb: *mut mailimf_mailbox = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *mut mailimf_mailbox;
for mb in &(*mb_list).0 {
let mb = *mb;
if !mb.is_null() {
add_or_lookup_contact_by_addr(
context,
(*mb).mb_display_name,
(*mb).mb_addr_spec,
(*mb).display_name,
(*mb).addr_spec,
origin,
ids,
check_self,
);
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
}
}
}
@@ -1935,7 +1909,8 @@ unsafe fn add_or_lookup_contact_by_addr(
}
*check_self = 0;
let self_addr = context
.get_config(Config::ConfiguredAddr)
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if addr_cmp(self_addr, as_str(addr_spec)) {
@@ -1948,15 +1923,17 @@ unsafe fn add_or_lookup_contact_by_addr(
/* add addr_spec if missing, update otherwise */
let mut display_name_dec = "".to_string();
if !display_name_enc.is_null() {
let tmp = dc_decode_header_words(as_str(display_name_enc));
let tmp = as_str(dc_decode_header_words(display_name_enc));
display_name_dec = normalize_name(&tmp);
}
/*can be NULL*/
let row_id = Contact::add_or_lookup(context, display_name_dec, as_str(addr_spec), origin)
.map(|(id, _)| id)
.unwrap_or_default();
if 0 != row_id && !ids.contains(&row_id) {
ids.push(row_id);
if 0 != row_id {
if !ids.contains(&row_id) {
ids.push(row_id);
}
};
}

View File

@@ -10,16 +10,18 @@ pub struct Simplify {
///
/// Also return whether not-standard (rfc3676, §4.3) footer is found.
fn find_message_footer(lines: &[&str]) -> (usize, bool) {
for (ix, &line) in lines.iter().enumerate() {
for ix in 0..lines.len() {
let line = lines[ix];
// quoted-printable may encode `-- ` to `-- =20` which is converted
// back to `-- `
match line {
match line.as_ref() {
"-- " | "-- " => return (ix, false),
"--" | "---" | "----" => return (ix, true),
_ => (),
}
}
(lines.len(), false)
return (lines.len(), false);
}
impl Simplify {
@@ -101,8 +103,10 @@ impl Simplify {
if let Some(last_quoted_line) = l_lastQuotedLine {
l_last = last_quoted_line;
is_cut_at_end = true;
if l_last > 1 && is_empty_line(lines[l_last - 1]) {
l_last -= 1
if l_last > 1 {
if is_empty_line(lines[l_last - 1]) {
l_last -= 1
}
}
if l_last > 1 {
let line = lines[l_last - 1];
@@ -201,7 +205,7 @@ fn is_quoted_headline(buf: &str) -> bool {
}
fn is_plain_quote(buf: &str) -> bool {
buf.starts_with('>')
buf.starts_with(">")
}
#[cfg(test)]

View File

@@ -4,7 +4,7 @@ use std::ffi::CString;
use std::ptr;
use charset::Charset;
use libc::free;
use libc::{free, strlen};
use mmime::mailmime::decode::mailmime_encoded_phrase_parse;
use mmime::other::*;
use percent_encoding::{percent_decode, utf8_percent_encode, AsciiSet, CONTROLS};
@@ -68,7 +68,28 @@ fn quote_word(word: &[u8]) -> String {
* Encode/decode header words, RFC 2047
******************************************************************************/
pub(crate) fn dc_decode_header_words(input: &str) -> String {
pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_char {
if in_0.is_null() {
return ptr::null_mut();
}
let mut out: *mut libc::c_char = ptr::null_mut();
let mut cur_token = 0;
let r: libc::c_int = mailmime_encoded_phrase_parse(
b"iso-8859-1\x00" as *const u8 as *const libc::c_char,
in_0,
strlen(in_0),
&mut cur_token,
b"utf-8\x00" as *const u8 as *const libc::c_char,
&mut out,
);
if r != MAILIMF_NO_ERROR as libc::c_int || out.is_null() {
out = dc_strdup(in_0)
}
out
}
pub fn dc_decode_header_words_safe(input: &str) -> String {
static FROM_ENCODING: &[u8] = b"iso-8859-1\x00";
static TO_ENCODING: &[u8] = b"utf-8\x00";
let mut out = ptr::null_mut();
@@ -86,7 +107,7 @@ pub(crate) fn dc_decode_header_words(input: &str) -> String {
if r as u32 != MAILIMF_NO_ERROR || out.is_null() {
input.to_string()
} else {
let res = to_string_lossy(out);
let res = to_string(out);
free(out.cast());
res
}
@@ -156,31 +177,60 @@ pub fn dc_decode_ext_header(to_decode: &[u8]) -> Cow<str> {
mod tests {
use super::*;
use libc::strcmp;
use std::ffi::CStr;
#[test]
fn test_dc_decode_header_words() {
assert_eq!(
dc_decode_header_words("=?utf-8?B?dGVzdMOkw7bDvC50eHQ=?="),
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
);
assert_eq!(dc_decode_header_words("just ascii test"), "just ascii test");
assert_eq!(dc_encode_header_words("abcdef"), "abcdef");
let r = dc_encode_header_words(
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
);
assert!(r.starts_with("=?utf-8"));
assert_eq!(
dc_decode_header_words(&r),
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec()).unwrap(),
);
assert_eq!(
dc_decode_header_words("=?ISO-8859-1?Q?attachment=3B=0D=0A_filename=3D?= =?ISO-8859-1?Q?=22test=E4=F6=FC=2Etxt=22=3B=0D=0A_size=3D39?="),
std::string::String::from_utf8(b"attachment;\r\n filename=\"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\";\r\n size=39".to_vec()).unwrap(),
unsafe {
let mut buf1: *mut libc::c_char = dc_decode_header_words(
b"=?utf-8?B?dGVzdMOkw7bDvC50eHQ=?=\x00" as *const u8 as *const libc::c_char,
);
assert_eq!(
strcmp(
buf1,
b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\x00" as *const u8 as *const libc::c_char
),
0
);
free(buf1 as *mut libc::c_void);
buf1 =
dc_decode_header_words(b"just ascii test\x00" as *const u8 as *const libc::c_char);
assert_eq!(CStr::from_ptr(buf1).to_str().unwrap(), "just ascii test");
free(buf1 as *mut libc::c_void);
assert_eq!(dc_encode_header_words("abcdef"), "abcdef");
let r = dc_encode_header_words(
std::string::String::from_utf8(b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt".to_vec())
.unwrap(),
);
assert!(r.starts_with("=?utf-8"));
buf1 = r.strdup();
let buf2: *mut libc::c_char = dc_decode_header_words(buf1);
assert_eq!(
strcmp(
buf2,
b"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\x00" as *const u8 as *const libc::c_char
),
0
);
free(buf2 as *mut libc::c_void);
buf1 = dc_decode_header_words(
b"=?ISO-8859-1?Q?attachment=3B=0D=0A_filename=3D?= =?ISO-8859-1?Q?=22test=E4=F6=FC=2Etxt=22=3B=0D=0A_size=3D39?=\x00" as *const u8 as *const libc::c_char
);
assert_eq!(
strcmp(
buf1,
b"attachment;\r\n filename=\"test\xc3\xa4\xc3\xb6\xc3\xbc.txt\";\r\n size=39\x00" as *const u8 as *const libc::c_char,
),
0
);
}
}
#[test]
@@ -243,7 +293,7 @@ mod tests {
#[test]
fn test_dc_header_roundtrip(input: String) {
let encoded = dc_encode_header_words(&input);
let decoded = dc_decode_header_words(&encoded);
let decoded = dc_decode_header_words_safe(&encoded);
assert_eq!(input, decoded);
}

View File

@@ -1,9 +1,8 @@
//! Some tools and enhancements to the used libraries, there should be
//! no references to Context and other "larger" entities here.
use core::cmp::max;
use std::borrow::Cow;
use std::ffi::{CStr, CString};
use std::ffi::{CStr, CString, OsString};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::time::SystemTime;
@@ -11,13 +10,11 @@ use std::{fmt, fs, ptr};
use chrono::{Local, TimeZone};
use libc::{memcpy, strlen};
use mmime::clist::*;
use mmime::mailimf::types::*;
use rand::{thread_rng, Rng};
use crate::context::Context;
use crate::error::Error;
use crate::events::Event;
pub(crate) fn dc_exactly_one_bit_set(v: libc::c_int) -> bool {
0 != v && 0 == v & (v - 1)
@@ -30,11 +27,11 @@ pub(crate) fn dc_exactly_one_bit_set(v: libc::c_int) -> bool {
/// # Examples
///
/// ```
/// use deltachat::dc_tools::{dc_strdup, to_string_lossy};
/// use deltachat::dc_tools::{dc_strdup, to_string};
/// unsafe {
/// let str_a = b"foobar\x00" as *const u8 as *const libc::c_char;
/// let str_a_copy = dc_strdup(str_a);
/// assert_eq!(to_string_lossy(str_a_copy), "foobar");
/// assert_eq!(to_string(str_a_copy), "foobar");
/// assert_ne!(str_a, str_a_copy);
/// }
/// ```
@@ -142,7 +139,7 @@ pub(crate) fn dc_truncate(buf: &str, approx_chars: usize, do_unwrap: bool) -> Co
.unwrap_or_default();
if let Some(index) = buf[..end_pos].rfind(|c| c == ' ' || c == '\n') {
Cow::Owned(format!("{}{}", &buf[..=index], ellipse))
Cow::Owned(format!("{}{}", &buf[..index + 1], ellipse))
} else {
Cow::Owned(format!("{}{}", &buf[..end_pos], ellipse))
}
@@ -151,46 +148,26 @@ pub(crate) fn dc_truncate(buf: &str, approx_chars: usize, do_unwrap: bool) -> Co
}
}
pub(crate) unsafe fn dc_str_from_clist(
list: *const clist,
pub(crate) unsafe fn dc_str_from_vec(
list: &Vec<*mut libc::c_char>,
delimiter: *const libc::c_char,
) -> *mut libc::c_char {
let mut res = String::new();
if !list.is_null() {
let mut cur: *mut clistiter = (*list).first;
while !cur.is_null() {
let rfc724_mid = (if !cur.is_null() {
(*cur).data
} else {
ptr::null_mut()
}) as *const libc::c_char;
if !rfc724_mid.is_null() {
if !res.is_empty() && !delimiter.is_null() {
res += as_str(delimiter);
}
res += as_str(rfc724_mid);
}
cur = if !cur.is_null() {
(*cur).next
} else {
ptr::null_mut()
for val in list {
if !val.is_null() {
if !res.is_empty() && !delimiter.is_null() {
res += as_str(delimiter);
}
res += as_str(*val);
}
}
res.strdup()
}
pub(crate) fn dc_str_to_clist(str: &str, delimiter: &str) -> *mut clist {
unsafe {
let list: *mut clist = clist_new();
for cur in str.split(&delimiter) {
clist_insert_after(list, (*list).last, cur.strdup().cast());
}
list
}
pub(crate) fn dc_str_to_vec(s: &str, delimiter: &str) -> Vec<*mut libc::c_char> {
s.split(&delimiter).map(|s| unsafe { s.strdup() }).collect()
}
/* the colors must fulfill some criterions as:
@@ -221,24 +198,24 @@ pub(crate) fn dc_str_to_color(s: impl AsRef<str>) -> u32 {
/* the result is UTC or DC_INVALID_TIMESTAMP */
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 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 sec = dt.sec;
let min = dt.min;
let hour = dt.hour;
let day = dt.day;
let month = dt.month;
let year = 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),
chrono::NaiveDate::from_ymd(year, month, day),
chrono::NaiveTime::from_hms(hour, min, sec),
);
let (zone_hour, zone_min) = if dt.dt_zone >= 0 {
(dt.dt_zone / 100, dt.dt_zone % 100)
let (zone_hour, zone_min) = if dt.zone >= 0 {
(dt.zone / 100, dt.zone % 100)
} else {
(-(-dt.dt_zone / 100), -(-dt.dt_zone % 100))
(-(-dt.zone / 100), -(-dt.zone % 100))
};
ts.timestamp() - (zone_hour * 3600 + zone_min * 60) as i64
@@ -335,14 +312,14 @@ fn encode_66bits_as_base64(v1: u32, v2: u32, fill: u32) -> String {
enc.write_u8(((fill & 0x3) as u8) << 6).unwrap();
enc.finish().unwrap();
}
assert_eq!(wrapped_writer.pop(), Some(b'A')); // Remove last "A"
assert_eq!(wrapped_writer.pop(), Some('A' as u8)); // Remove last "A"
String::from_utf8(wrapped_writer).unwrap()
}
pub(crate) fn dc_create_incoming_rfc724_mid(
message_timestamp: i64,
contact_id_from: u32,
contact_ids_to: &[u32],
contact_ids_to: &Vec<u32>,
) -> Option<String> {
if contact_ids_to.is_empty() {
return None;
@@ -394,15 +371,15 @@ pub(crate) fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> {
None
}
pub(crate) fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut libc::c_char {
if !list.is_null() {
unsafe {
for cur in (*list).into_iter() {
let mid = as_str(cur as *const libc::c_char);
pub(crate) fn dc_extract_grpid_from_rfc724_mid_list(
list: &Vec<*mut libc::c_char>,
) -> *mut libc::c_char {
unsafe {
for cur in list {
let mid = as_str(*cur);
if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(mid) {
return grpid.strdup();
}
if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(mid) {
return grpid.strdup();
}
}
}
@@ -417,41 +394,12 @@ pub(crate) fn dc_ensure_no_slash_safe(path: &str) -> &str {
path
}
// Function returns a sanitized basename that does not contain
// win/linux path separators and also not any non-ascii chars
fn get_safe_basename(filename: &str) -> String {
// return the (potentially mangled) basename of the input filename
// this might be a path that comes in from another operating system
let mut index: usize = 0;
if let Some(unix_index) = filename.rfind('/') {
index = unix_index + 1;
}
if let Some(win_index) = filename.rfind('\\') {
index = max(index, win_index + 1);
}
if index >= filename.len() {
"nobasename".to_string()
} else {
// we don't allow any non-ascii to be super-safe
filename[index..].replace(|c: char| !c.is_ascii() || c == ':', "-")
}
}
pub fn dc_derive_safe_stem_ext(filename: &str) -> (String, String) {
let basename = get_safe_basename(&filename);
let (mut stem, mut ext) = if let Some(index) = basename.rfind('.') {
(
basename[0..index].to_string(),
basename[index..].to_string(),
)
} else {
(basename, "".to_string())
};
// limit length of stem and ext
stem.truncate(32);
ext.truncate(32);
(stem, ext)
/// Function modifies the given buffer and replaces all characters not valid in filenames by a "-".
fn validate_filename(filename: &str) -> String {
filename
.replace('/', "-")
.replace('\\', "-")
.replace(':', "-")
}
// the returned suffix is lower-case
@@ -513,14 +461,10 @@ pub(crate) fn dc_delete_file(context: &Context, path: impl AsRef<std::path::Path
return false;
}
let dpath = format!("{}", path.as_ref().to_string_lossy());
match fs::remove_file(path_abs) {
Ok(_) => {
context.call_cb(Event::DeletedBlobFile(dpath));
true
}
Ok(_) => true,
Err(_err) => {
warn!(context, "Cannot delete \"{}\".", dpath);
warn!(context, "Cannot delete \"{}\".", path.as_ref().display());
false
}
}
@@ -601,24 +545,49 @@ pub fn dc_read_file<P: AsRef<std::path::Path>>(
}
}
pub(crate) fn dc_get_next_backup_path(
pub(crate) fn dc_get_fine_path_filename(
context: &Context,
folder: impl AsRef<Path>,
backup_time: i64,
) -> Result<PathBuf, Error> {
let folder = PathBuf::from(folder.as_ref());
let stem = chrono::NaiveDateTime::from_timestamp(backup_time, 0)
.format("delta-chat-%Y-%m-%d")
.to_string();
desired_filename_suffix: impl AsRef<str>,
) -> PathBuf {
let now = time();
// 64 backup files per day should be enough for everyone
for i in 0..64 {
let mut path = folder.clone();
path.push(format!("{}-{}.bak", stem, i));
if !path.exists() {
return Ok(path);
let folder = PathBuf::from(folder.as_ref());
// XXX sanitize desired_filename eg using
// https://github.com/kardeiz/sanitize-filename/blob/master/src/lib.rs
let suffix = validate_filename(desired_filename_suffix.as_ref());
let file_name = PathBuf::from(suffix);
let extension = file_name.extension().map(|c| c.clone());
for i in 0..100_000 {
let ret = if i == 0 {
let mut folder = folder.clone();
folder.push(&file_name);
folder
} else {
let idx = if i < 100 { i } else { now + i };
let file_name = if let Some(stem) = file_name.file_stem() {
let mut stem = stem.to_os_string();
stem.push(format!("-{}", idx));
stem
} else {
OsString::from(idx.to_string())
};
let mut folder = folder.clone();
folder.push(file_name);
if let Some(ext) = extension {
folder.set_extension(&ext);
}
folder
};
if !dc_file_exist(context, &ret) {
// fine filename found
return ret;
}
}
bail!("could not create backup file, disk full?");
panic!("Something is really wrong, you need to clean up your disk");
}
pub(crate) fn dc_is_blobdir_path(context: &Context, path: impl AsRef<str>) -> bool {
@@ -637,10 +606,7 @@ fn dc_make_rel_path(context: &Context, path: &mut String) {
.map(|s| path.starts_with(s))
.unwrap_or_default()
{
*path = path.replace(
context.get_blobdir().to_str().unwrap_or_default(),
"$BLOBDIR",
);
*path = path.replace(context.get_blobdir().to_str().unwrap(), "$BLOBDIR");
}
}
@@ -649,10 +615,13 @@ pub(crate) fn dc_make_rel_and_copy(context: &Context, path: &mut String) -> bool
dc_make_rel_path(context, path);
return true;
}
if let Ok(blobdir_path) = context.copy_to_blobdir(&path) {
*path = blobdir_path;
let blobdir_path = dc_get_fine_path_filename(context, "$BLOBDIR", &path);
if dc_copy_file(context, &path, &blobdir_path) {
*path = blobdir_path.to_string_lossy().to_string();
dc_make_rel_path(context, path);
return true;
}
false
}
@@ -801,6 +770,22 @@ impl<T: AsRef<str>> StrExt for T {
}
}
pub fn to_string(s: *const libc::c_char) -> String {
if s.is_null() {
return "".into();
}
let cstr = unsafe { CStr::from_ptr(s) };
cstr.to_str().map(|s| s.to_string()).unwrap_or_else(|err| {
panic!(
"Non utf8 string: '{:?}' ({:?})",
cstr.to_string_lossy(),
err
);
})
}
pub fn to_string_lossy(s: *const libc::c_char) -> String {
if s.is_null() {
return "".into();
@@ -1123,21 +1108,6 @@ mod tests {
);
}
/* calls free() for each item content */
unsafe fn clist_free_content(haystack: *const clist) {
let mut iter = (*haystack).first;
while !iter.is_null() {
free((*iter).data);
(*iter).data = ptr::null_mut();
iter = if !iter.is_null() {
(*iter).next
} else {
ptr::null_mut()
}
}
}
fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char {
if s.is_null() {
return std::ptr::null_mut();
@@ -1153,35 +1123,6 @@ mod tests {
}
}
#[test]
fn test_dc_str_to_clist_1() {
unsafe {
let list = dc_str_to_clist("", " ");
assert_eq!((*list).count, 1);
clist_free_content(list);
clist_free(list);
}
}
#[test]
fn test_dc_str_to_clist_4() {
unsafe {
let list: *mut clist = dc_str_to_clist("foo bar test", " ");
assert_eq!((*list).count, 3);
let str: *mut libc::c_char =
dc_str_from_clist(list, b" \x00" as *const u8 as *const libc::c_char);
assert_eq!(
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
"foo bar test"
);
clist_free_content(list);
clist_free(list);
free(str as *mut libc::c_void);
}
}
#[test]
fn test_dc_create_id() {
let buf = dc_create_id();
@@ -1423,27 +1364,14 @@ mod tests {
unsafe {
let res = strndup(b"helloworld\x00" as *const u8 as *const libc::c_char, 4);
assert_eq!(
to_string_lossy(res),
to_string_lossy(b"hell\x00" as *const u8 as *const libc::c_char)
to_string(res),
to_string(b"hell\x00" as *const u8 as *const libc::c_char)
);
assert_eq!(strlen(res), 4);
free(res as *mut _);
}
}
#[test]
fn test_file_get_safe_basename() {
assert_eq!(get_safe_basename("12312/hello"), "hello");
assert_eq!(get_safe_basename("12312\\hello"), "hello");
assert_eq!(get_safe_basename("//12312\\hello"), "hello");
assert_eq!(get_safe_basename("//123:12\\hello"), "hello");
assert_eq!(get_safe_basename("//123:12/\\\\hello"), "hello");
assert_eq!(get_safe_basename("//123:12//hello"), "hello");
assert_eq!(get_safe_basename("//123:12//"), "nobasename");
assert_eq!(get_safe_basename("//123:12/"), "nobasename");
assert!(get_safe_basename("123\x012.hello").ends_with(".hello"));
}
#[test]
fn test_file_handling() {
let t = dummy_context();
@@ -1478,7 +1406,6 @@ mod tests {
assert!(dc_file_exist(context, &abs_path));
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
let buf = dc_read_file(context, "$BLOBDIR/dada").unwrap();
@@ -1491,12 +1418,14 @@ mod tests {
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
assert!(!dc_delete_file(context, "$BLOBDIR/foobar-folder"));
let fn0 = dc_get_fine_path_filename(context, "$BLOBDIR", "foobar.dadada");
assert_eq!(fn0, PathBuf::from("$BLOBDIR/foobar.dadada"));
let fn0 = "$BLOBDIR/data.data";
assert!(dc_write_file(context, &fn0, b"content"));
let fn1 = dc_get_fine_path_filename(context, "$BLOBDIR", "foobar.dadada");
assert_eq!(fn1, PathBuf::from("$BLOBDIR/foobar-1.dadada"));
assert!(dc_delete_file(context, &fn0));
assert!(!dc_file_exist(context, &fn0));
}
#[test]
@@ -1518,12 +1447,12 @@ mod tests {
unsafe {
let input = "foo\r\nbar".strdup();
dc_remove_cr_chars(input);
assert_eq!("foo\nbar", to_string_lossy(input));
assert_eq!("foo\nbar", to_string(input));
free(input.cast());
let input = "\rfoo\r\rbar\r".strdup();
dc_remove_cr_chars(input);
assert_eq!("foobar", to_string_lossy(input));
assert_eq!("foobar", to_string(input));
free(input.cast());
}
}

View File

@@ -5,7 +5,6 @@ use std::ptr;
use std::str::FromStr;
use libc::strlen;
use mmime::clist::*;
use mmime::mailimf::types::*;
use mmime::mailimf::types_helper::*;
use mmime::mailimf::*;
@@ -22,6 +21,7 @@ use num_traits::FromPrimitive;
use crate::aheader::*;
use crate::config::Config;
use crate::context::Context;
use crate::dc_mimeparser::*;
use crate::dc_tools::*;
use crate::error::*;
use crate::key::*;
@@ -46,9 +46,12 @@ pub struct EncryptHelper {
impl EncryptHelper {
pub fn new(context: &Context) -> Result<EncryptHelper> {
let prefer_encrypt =
EncryptPreference::from_i32(context.get_config_int(Config::E2eeEnabled))
.unwrap_or_default();
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!");
@@ -163,19 +166,16 @@ impl EncryptHelper {
}
// 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 = (*(*imffields_unprotected).fld_list).first;
while !cur.is_null() {
let field = (*cur).data as *mut mailimf_field;
(*imffields_unprotected).0.retain(|field| {
let mut move_to_encrypted = false;
if !field.is_null() {
if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
match *field {
mailimf_field::Subject(_) => {
move_to_encrypted = true;
} else if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
let opt_field = (*field).fld_data.fld_optional_field;
if !opt_field.is_null() && !(*opt_field).fld_name.is_null() {
let fld_name = to_string_lossy((*opt_field).fld_name);
}
mailimf_field::OptionalField(opt_field) => {
if !opt_field.is_null() && !(*opt_field).name.is_null() {
let fld_name = to_string_lossy((*opt_field).name);
if fld_name.starts_with("Secure-Join")
|| (fld_name.starts_with("Chat-") && fld_name != "Chat-Version")
{
@@ -183,18 +183,19 @@ impl EncryptHelper {
}
}
}
_ => {}
}
if move_to_encrypted {
mailimf_fields_add(imffields_encrypted, field);
cur = clist_delete((*imffields_unprotected).fld_list, cur);
mailimf_fields_add(imffields_encrypted, *field);
false
} else {
cur = (*cur).next;
true
}
}
});
let subject = mailimf_subject_new("...".strdup());
mailimf_fields_add(imffields_unprotected, mailimf_field_new_subject(subject));
mailimf_fields_add(imffields_unprotected, mailimf_field::Subject(subject));
wrapmime::append_ct_param(
(*part_to_encrypt).mm_content_type,
@@ -262,80 +263,98 @@ pub fn try_decrypt(
context: &Context,
in_out_message: *mut Mailmime,
) -> Result<(bool, HashSet<String>, HashSet<String>)> {
// just a pointer into mailmime structure, must not be freed
let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) };
ensure!(
!in_out_message.is_null() && !imffields.is_null(),
"corrupt invalid mime inputs"
);
let from = wrapmime::get_field_from(imffields)?;
let message_time = wrapmime::get_field_date(imffields)?;
let mut peerstate = None;
let autocryptheader = Aheader::from_imffields(&from, imffields);
if message_time > 0 {
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)?;
} 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)?;
}
} else if let Some(ref header) = autocryptheader {
let p = Peerstate::from_header(context, header, message_time);
p.save_to_db(&context.sql, true)?;
peerstate = Some(p);
}
}
/* possibly perform decryption */
let mut private_keyring = Keyring::default();
let mut public_keyring_for_validate = Keyring::default();
let mut encrypted = false;
let mut signatures = HashSet::default();
let mut gossipped_addr = HashSet::default();
let self_addr = context.get_config(Config::ConfiguredAddr);
// 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();
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);
}
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);
// XXX do wrapmime:: helper for the next block
if !(in_out_message.is_null() || imffields.is_null()) {
if let Some(field) = mailimf_find_from_field(imffields) {
let mb_list = unsafe { (*field).frm_mb_list };
from = mailimf_find_first_addr(mb_list);
}
if let Some(orig_date) = mailimf_find_orig_date_field(imffields) {
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 gossip_headers = ptr::null_mut();
encrypted = decrypt_if_autocrypt_message(
context,
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)?;
unsafe { mailimf_fields_free(gossip_headers) };
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(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,
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() {
unsafe { mailimf_fields_free(gossip_headers) };
}
Ok((encrypted, signatures, gossipped_addr))
}
@@ -433,25 +452,15 @@ fn update_gossip_peerstates(
let mut recipients: Option<HashSet<String>> = None;
let mut gossipped_addr: HashSet<String> = Default::default();
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 };
for field in unsafe { &(*gossip_headers).0 } {
if let mailimf_field::OptionalField(optional_field) = *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 = to_string_lossy(optional_field.fld_value);
let optional_field = unsafe { &*optional_field };
if !optional_field.name.is_null() && as_str(optional_field.name) == "Autocrypt-Gossip" {
let value = to_string_lossy(optional_field.value);
let gossip_header = Aheader::from_str(&value);
if let Ok(ref header) = gossip_header {
@@ -533,7 +542,7 @@ fn decrypt_if_autocrypt_message(
// 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.is_empty() {
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
let mut dummy: libc::size_t = 0;
let mut test: *mut mailimf_fields = ptr::null_mut();
if mailimf_envelope_and_optional_fields_parse(
@@ -573,16 +582,22 @@ fn decrypt_part(
mime_transfer_encoding = enc;
}
let data: Vec<u8> = wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?;
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.
let mut ret_decrypted_mime = ptr::null_mut();
if has_decrypted_pgp_armor(&data) {
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(
&data,
data,
&private_keyring,
&public_keyring_for_validate,
Some(ret_valid_signatures),
@@ -591,7 +606,10 @@ fn decrypt_part(
ensure!(!ret_valid_signatures.is_empty(), "no valid signatures");
plain
}
Err(err) => bail!("could not decrypt: {}", err),
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;
@@ -619,6 +637,7 @@ fn decrypt_part(
ret_decrypted_mime = decrypted_mime;
}
}
unsafe { mmap_string_unref(decoded_data) };
Ok(ret_decrypted_mime)
}
@@ -686,12 +705,12 @@ fn contains_report(mime: *mut Mailmime) -> bool {
/// If this succeeds you are also guaranteed that the
/// [Config::ConfiguredAddr] is configured, this address is returned.
pub fn ensure_secret_key_exists(context: &Context) -> Result<String> {
let self_addr = context.get_config(Config::ConfiguredAddr).ok_or_else(|| {
format_err!(concat!(
let self_addr = context
.get_config(Config::ConfiguredAddr)
.ok_or(format_err!(concat!(
"Failed to get self address, ",
"cannot ensure secret key if not configured."
))
})?;
)))?;
load_or_generate_self_public_key(context, &self_addr)?;
Ok(self_addr)
}

View File

@@ -42,30 +42,6 @@ pub enum Event {
#[strum(props(id = "103"))]
SmtpMessageSent(String),
/// Emitted when an IMAP message has been marked as deleted
///
/// @return 0
#[strum(props(id = "104"))]
ImapMessageDeleted(String),
/// Emitted when an IMAP message has been moved
///
/// @return 0
#[strum(props(id = "105"))]
ImapMessageMoved(String),
/// Emitted when an new file in the $BLOBDIR was created
///
/// @return 0
#[strum(props(id = "150"))]
NewBlobFile(String),
/// Emitted when an new file in the $BLOBDIR was created
///
/// @return 0
#[strum(props(id = "151"))]
DeletedBlobFile(String),
/// The library-user should write a warning string to the log.
/// Passed to the callback given to dc_context_new().
///

View File

@@ -8,14 +8,12 @@ use std::time::{Duration, SystemTime};
use crate::constants::*;
use crate::context::Context;
use crate::dc_receive_imf::dc_receive_imf;
use crate::error::Error;
use crate::events::Event;
use crate::job::{connect_to_inbox, job_add, Action};
use crate::login_param::{dc_build_tls, CertificateChecks, LoginParam};
use crate::job::{job_add, Action};
use crate::login_param::LoginParam;
use crate::message::{self, update_msg_move_state, update_server_uid};
use crate::oauth2::dc_get_oauth2_access_token;
use crate::param::Params;
use crate::wrapmime;
const DC_IMAP_SEEN: usize = 0x0001;
@@ -29,6 +27,7 @@ pub enum ImapResult {
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
const FETCH_FLAGS: &str = "(FLAGS)";
#[derive(Debug)]
pub struct Imap {
@@ -108,10 +107,14 @@ impl Client {
pub fn connect_secure<A: net::ToSocketAddrs, S: AsRef<str>>(
addr: A,
domain: S,
certificate_checks: CertificateChecks,
) -> imap::error::Result<Self> {
let stream = net::TcpStream::connect(addr)?;
let tls = dc_build_tls(certificate_checks).unwrap();
let tls = native_tls::TlsConnector::builder()
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
.danger_accept_invalid_certs(true)
.danger_accept_invalid_hostnames(true)
.build()
.unwrap();
let s = stream.try_clone().expect("cloning the stream failed");
let tls_stream = native_tls::TlsConnector::connect(&tls, domain.as_ref(), s)?;
@@ -131,14 +134,13 @@ impl Client {
Ok(Client::Insecure(client, stream))
}
pub fn secure<S: AsRef<str>>(
self,
domain: S,
certificate_checks: CertificateChecks,
) -> imap::error::Result<Client> {
pub fn secure<S: AsRef<str>>(self, domain: S) -> imap::error::Result<Client> {
match self {
Client::Insecure(client, stream) => {
let tls = dc_build_tls(certificate_checks).unwrap();
let tls = native_tls::TlsConnector::builder()
.danger_accept_invalid_hostnames(true)
.build()
.unwrap();
let client_sec = client.secure(domain, &tls)?;
@@ -318,7 +320,6 @@ struct ImapConfig {
pub imap_port: u16,
pub imap_user: String,
pub imap_pw: String,
pub certificate_checks: CertificateChecks,
pub server_flags: usize,
pub selected_folder: Option<String>,
pub selected_mailbox: Option<imap::types::Mailbox>,
@@ -331,13 +332,12 @@ struct ImapConfig {
impl Default for ImapConfig {
fn default() -> Self {
ImapConfig {
let cfg = ImapConfig {
addr: "".into(),
imap_server: "".into(),
imap_port: 0,
imap_user: "".into(),
imap_pw: "".into(),
certificate_checks: Default::default(),
server_flags: 0,
selected_folder: None,
selected_mailbox: None,
@@ -346,7 +346,9 @@ impl Default for ImapConfig {
has_xlist: false,
imap_delimiter: '.',
watch_folder: None,
}
};
cfg
}
}
@@ -394,7 +396,7 @@ impl Imap {
Client::connect_insecure((imap_server, imap_port)).and_then(|client| {
if (server_flags & DC_LP_IMAP_SOCKET_STARTTLS) != 0 {
client.secure(imap_server, config.certificate_checks)
client.secure(imap_server)
} else {
Ok(client)
}
@@ -404,11 +406,7 @@ impl Imap {
let imap_server: &str = config.imap_server.as_ref();
let imap_port = config.imap_port;
Client::connect_secure(
(imap_server, imap_port),
imap_server,
config.certificate_checks,
)
Client::connect_secure((imap_server, imap_port), imap_server)
};
let login_res = match connection_res {
@@ -535,7 +533,6 @@ impl Imap {
config.imap_port = imap_port;
config.imap_user = imap_user.to_string();
config.imap_pw = imap_pw.to_string();
config.certificate_checks = lp.imap_certificate_checks;
config.server_flags = server_flags;
}
@@ -684,7 +681,7 @@ impl Imap {
}
}
} else {
unreachable!();
return 0;
}
}
@@ -693,20 +690,12 @@ impl Imap {
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
let key = format!("imap.mailbox.{}", folder.as_ref());
if let Some(entry) = context.sql.get_raw_config(context, &key) {
if let Some(entry) = context.sql.get_config(context, &key) {
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
let mut parts = entry.split(':');
(
parts
.next()
.unwrap_or_default()
.parse()
.unwrap_or_else(|_| 0),
parts
.next()
.unwrap_or_default()
.parse()
.unwrap_or_else(|_| 0),
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
)
} else {
(0, 0)
@@ -750,7 +739,7 @@ impl Imap {
return 0;
}
if mailbox.uid_validity.unwrap_or_default() != uid_validity {
if mailbox.uid_validity.unwrap() != uid_validity {
// first time this folder is selected or UIDVALIDITY has changed, init lastseenuid and save it to config
if mailbox.exists == 0 {
@@ -760,12 +749,7 @@ impl Imap {
// id we do not do this here, we'll miss the first message
// as we will get in here again and fetch from lastseenuid+1 then
self.set_config_last_seen_uid(
context,
&folder,
mailbox.uid_validity.unwrap_or_default(),
0,
);
self.set_config_last_seen_uid(context, &folder, mailbox.uid_validity.unwrap(), 0);
return 0;
}
@@ -796,7 +780,7 @@ impl Imap {
last_seen_uid -= 1;
}
uid_validity = mailbox.uid_validity.unwrap_or_default();
uid_validity = mailbox.uid_validity.unwrap();
self.set_config_last_seen_uid(context, &folder, uid_validity, last_seen_uid);
info!(
context,
@@ -832,7 +816,11 @@ impl Imap {
if cur_uid > last_seen_uid {
read_cnt += 1;
let message_id = prefetch_get_message_id(msg).unwrap_or_default();
let message_id = msg
.envelope()
.expect("missing envelope")
.message_id
.expect("missing message id");
if !precheck_imf(context, &message_id, folder.as_ref(), cur_uid) {
// check passed, go fetch the rest
@@ -897,7 +885,7 @@ impl Imap {
let key = format!("imap.mailbox.{}", folder.as_ref());
let val = format!("{}:{}", uidvalidity, lastseenuid);
context.sql.set_raw_config(context, &key, Some(&val)).ok();
context.sql.set_config(context, &key, Some(&val)).ok();
}
fn fetch_single_msg<S: AsRef<str>>(
@@ -945,20 +933,27 @@ impl Imap {
} else {
let msg = &msgs[0];
// XXX put flags into a set and pass them to dc_receive_imf
let is_deleted = msg.flags().iter().any(|flag| match flag {
imap::types::Flag::Deleted => true,
_ => false,
});
let is_seen = msg.flags().iter().any(|flag| match flag {
imap::types::Flag::Seen => true,
_ => false,
});
let is_deleted = msg
.flags()
.iter()
.find(|flag| match flag {
imap::types::Flag::Deleted => true,
_ => false,
})
.is_some();
let is_seen = msg
.flags()
.iter()
.find(|flag| match flag {
imap::types::Flag::Seen => true,
_ => false,
})
.is_some();
let flags = if is_seen { DC_IMAP_SEEN } else { 0 };
if !is_deleted && msg.body().is_some() {
let body = msg.body().unwrap_or_default();
let body = msg.body().unwrap();
unsafe {
dc_receive_imf(context, &body, folder.as_ref(), server_uid, flags as u32);
}
@@ -1069,14 +1064,13 @@ impl Imap {
let mut do_fake_idle = true;
while do_fake_idle {
// wait a moment: every 5 seconds in the first 3 minutes after a new message, after that every 60 seconds.
let seconds_to_wait = if fake_idle_start_time.elapsed().unwrap_or_default()
< Duration::new(3 * 60, 0)
&& !wait_long
{
Duration::new(5, 0)
} else {
Duration::new(60, 0)
};
let seconds_to_wait =
if fake_idle_start_time.elapsed().unwrap() < Duration::new(3 * 60, 0) && !wait_long
{
Duration::new(5, 0)
} else {
Duration::new(60, 0)
};
let &(ref lock, ref cvar) = &*self.watch.clone();
let mut watch = lock.lock().unwrap();
@@ -1125,94 +1119,117 @@ impl Imap {
cvar.notify_one();
}
pub fn mv(
pub fn mv<S1: AsRef<str>, S2: AsRef<str>>(
&self,
context: &Context,
folder: &str,
folder: S1,
uid: u32,
dest_folder: &str,
dest_folder: S2,
dest_uid: &mut u32,
) -> ImapResult {
if folder == dest_folder {
let mut res = ImapResult::RetryLater;
let set = format!("{}", uid);
if uid == 0 {
res = ImapResult::Failed;
} else if folder.as_ref() == dest_folder.as_ref() {
info!(
context,
"Skip moving message; message {}/{} is already in {}...", folder, uid, dest_folder,
"Skip moving message; message {}/{} is already in {}...",
folder.as_ref(),
uid,
dest_folder.as_ref()
);
return ImapResult::AlreadyDone;
}
if let Some(imapresult) = self.prepare_imap_operation_on_msg(context, folder, uid) {
return imapresult;
}
// we are connected, and the folder is selected
// XXX Rust-Imap provides no target uid on mv, so just set it to 0
*dest_uid = 0;
let set = format!("{}", uid);
let display_folder_id = format!("{}/{}", folder, uid);
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_mv(&set, &dest_folder) {
Ok(_) => {
emit_event!(
context,
Event::ImapMessageMoved(format!(
"IMAP Message {} moved to {}",
display_folder_id, dest_folder
))
);
return ImapResult::Success;
}
Err(err) => {
warn!(
context,
"Cannot move message, fallback to COPY/DELETE {}/{} to {}: {}",
folder,
uid,
dest_folder,
err
);
}
}
res = ImapResult::AlreadyDone;
} else {
unreachable!();
};
info!(
context,
"Moving message {}/{} to {}...",
folder.as_ref(),
uid,
dest_folder.as_ref()
);
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_copy(&set, &dest_folder) {
Ok(_) => {
if !self.add_flag_finalized(context, uid, "\\Deleted") {
warn!(context, "Cannot mark {} as \"Deleted\" after copy.", uid);
ImapResult::Failed
if self.select_folder(context, Some(folder.as_ref())) == 0 {
warn!(
context,
"Cannot select folder {} for moving message.",
folder.as_ref()
);
} else {
let moved = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_mv(&set, &dest_folder) {
Ok(_) => {
res = ImapResult::Success;
true
}
Err(err) => {
info!(
context,
"Cannot move message, fallback to COPY/DELETE {}/{} to {}: {}",
folder.as_ref(),
uid,
dest_folder.as_ref(),
err
);
false
}
}
} else {
unreachable!();
};
if !moved {
let copied = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_copy(&set, &dest_folder) {
Ok(_) => true,
Err(err) => {
eprintln!("error copy: {:?}", err);
info!(context, "Cannot copy message.",);
false
}
}
} else {
unreachable!();
};
if copied {
if self.add_flag(context, uid, "\\Deleted") == 0 {
warn!(context, "Cannot mark message as \"Deleted\".",);
}
self.config.write().unwrap().selected_folder_needs_expunge = true;
ImapResult::Success
res = ImapResult::Success;
}
}
Err(err) => {
warn!(context, "Could not copy message: {}", err);
ImapResult::Failed
}
}
}
if res == ImapResult::Success {
// TODO: is this correct?
*dest_uid = uid;
}
if res == ImapResult::RetryLater {
if self.should_reconnect() {
ImapResult::RetryLater
} else {
ImapResult::Failed
}
} else {
unreachable!();
res
}
}
fn add_flag_finalized(&self, context: &Context, server_uid: u32, flag: &str) -> bool {
// return true if we successfully set the flag or we otherwise
// think add_flag should not be retried: Disconnection during setting
// the flag, or other imap-errors, returns true as well.
//
// returning false means that the operation can be retried.
fn add_flag<S: AsRef<str>>(&self, context: &Context, server_uid: u32, flag: S) -> usize {
if server_uid == 0 {
return true; // might be moved but we don't want to have a stuck job
}
if self.should_reconnect() {
return false;
return 0;
}
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
let set = format!("{}", server_uid);
let query = format!("+FLAGS ({})", flag);
let query = format!("+FLAGS ({})", flag.as_ref());
match session.uid_store(&set, &query) {
Ok(_) => {}
Err(err) => {
@@ -1222,126 +1239,243 @@ impl Imap {
);
}
}
true // we tried once, that's probably enough for setting flag
}
// All non-connection states are treated as success - the mail may
// already be deleted or moved away on the server.
if self.should_reconnect() {
0
} else {
unreachable!();
1
}
}
pub fn prepare_imap_operation_on_msg(
&self,
context: &Context,
folder: &str,
uid: u32,
) -> Option<ImapResult> {
pub fn set_seen<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult {
let mut res = ImapResult::RetryLater;
if uid == 0 {
return Some(ImapResult::Failed);
} else if !self.is_connected() {
connect_to_inbox(context, &self);
if !self.is_connected() {
return Some(ImapResult::RetryLater);
res = ImapResult::Failed
} else if self.is_connected() {
info!(
context,
"Marking message {}/{} as seen...",
folder.as_ref(),
uid,
);
if self.select_folder(context, Some(folder.as_ref())) == 0 {
warn!(
context,
"Cannot select folder {} for setting SEEN flag.",
folder.as_ref(),
);
} else if self.add_flag(context, uid, "\\Seen") == 0 {
warn!(context, "Cannot mark message as seen.",);
} else {
res = ImapResult::Success
}
}
if self.select_folder(context, Some(&folder)) == 0 {
warn!(
context,
"Cannot select folder {} for preparing IMAP operation", folder
);
Some(ImapResult::RetryLater)
if res == ImapResult::RetryLater {
if self.should_reconnect() {
ImapResult::RetryLater
} else {
ImapResult::Failed
}
} else {
None
res
}
}
pub fn set_seen(&self, context: &Context, folder: &str, uid: u32) -> ImapResult {
if let Some(imapresult) = self.prepare_imap_operation_on_msg(context, folder, uid) {
return imapresult;
}
// we are connected, and the folder is selected
info!(context, "Marking message {}/{} as seen...", folder, uid,);
pub fn set_mdnsent<S: AsRef<str>>(&self, context: &Context, folder: S, uid: u32) -> ImapResult {
// returns 0=job should be retried later, 1=job done, 2=job done and flag just set
let mut res = ImapResult::RetryLater;
let set = format!("{}", uid);
if self.add_flag_finalized(context, uid, "\\Seen") {
ImapResult::Success
} else {
warn!(
if uid == 0 {
res = ImapResult::Failed;
} else if self.is_connected() {
info!(
context,
"Cannot mark message {} in folder {} as seen, ignoring.", uid, folder
"Marking message {}/{} as $MDNSent...",
folder.as_ref(),
uid,
);
ImapResult::Failed
if self.select_folder(context, Some(folder.as_ref())) == 0 {
warn!(
context,
"Cannot select folder {} for setting $MDNSent flag.",
folder.as_ref()
);
} else {
// Check if the folder can handle the `$MDNSent` flag (see RFC 3503). If so, and not
// set: set the flags and return this information.
// If the folder cannot handle the `$MDNSent` flag, we risk duplicated MDNs; it's up
// to the receiving MUA to handle this then (eg. Delta Chat has no problem with this).
let can_create_flag = self
.config
.read()
.unwrap()
.selected_mailbox
.as_ref()
.map(|mbox| {
// empty means, everything can be stored
mbox.permanent_flags.is_empty()
|| mbox
.permanent_flags
.iter()
.find(|flag| match flag {
imap::types::Flag::Custom(s) => s == "$MDNSent",
_ => false,
})
.is_some()
})
.expect("just selected folder");
if can_create_flag {
let fetched_msgs =
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, FETCH_FLAGS) {
Ok(res) => Some(res),
Err(err) => {
eprintln!("fetch error: {:?}", err);
None
}
}
} else {
unreachable!();
};
if let Some(msgs) = fetched_msgs {
let flag_set = msgs
.first()
.map(|msg| {
msg.flags()
.iter()
.find(|flag| match flag {
imap::types::Flag::Custom(s) => s == "$MDNSent",
_ => false,
})
.is_some()
})
.unwrap_or_else(|| false);
res = if flag_set {
ImapResult::AlreadyDone
} else if self.add_flag(context, uid, "$MDNSent") != 0 {
ImapResult::Success
} else {
res
};
if res == ImapResult::Success {
info!(context, "$MDNSent just set and MDN will be sent.");
} else {
info!(context, "$MDNSent already set and MDN already sent.");
}
}
} else {
res = ImapResult::Success;
info!(
context,
"Cannot store $MDNSent flags, risk sending duplicate MDN.",
);
}
}
}
if res == ImapResult::RetryLater {
if self.should_reconnect() {
ImapResult::RetryLater
} else {
ImapResult::Failed
}
} else {
res
}
}
// only returns 0 on connection problems; we should try later again in this case *
pub fn delete_msg(
pub fn delete_msg<S1: AsRef<str>, S2: AsRef<str>>(
&self,
context: &Context,
message_id: &str,
folder: &str,
uid: &mut u32,
) -> ImapResult {
if let Some(imapresult) = self.prepare_imap_operation_on_msg(context, folder, *uid) {
return imapresult;
}
// we are connected, and the folder is selected
message_id: S1,
folder: S2,
server_uid: &mut u32,
) -> usize {
let mut success = false;
if *server_uid == 0 {
success = true
} else {
info!(
context,
"Marking message \"{}\", {}/{} for deletion...",
message_id.as_ref(),
folder.as_ref(),
server_uid,
);
let set = format!("{}", uid);
let display_imap_id = format!("{}/{}", folder, uid);
if self.select_folder(context, Some(&folder)) == 0 {
warn!(
context,
"Cannot select folder {} for deleting message.",
folder.as_ref()
);
} else {
let set = format!("{}", server_uid);
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, PREFETCH_FLAGS) {
Ok(msgs) => {
if msgs.is_empty()
|| msgs
.first()
.unwrap()
.envelope()
.expect("missing envelope")
.message_id
.expect("missing message id")
!= message_id.as_ref()
{
warn!(
context,
"Cannot delete on IMAP, {}/{} does not match {}.",
folder.as_ref(),
server_uid,
message_id.as_ref(),
);
*server_uid = 0;
}
}
Err(err) => {
eprintln!("fetch error: {:?}", err);
// double-check that we are deleting the correct message-id
// this comes at the expense of another imap query
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
match session.uid_fetch(set, PREFETCH_FLAGS) {
Ok(msgs) => {
if msgs.is_empty() {
warn!(
context,
"Cannot delete on IMAP, {}: imap entry gone '{}'",
display_imap_id,
message_id,
);
return ImapResult::Failed;
warn!(
context,
"Cannot delete on IMAP, {}/{} not found.",
folder.as_ref(),
server_uid,
);
*server_uid = 0;
}
}
let remote_message_id =
prefetch_get_message_id(msgs.first().unwrap()).unwrap_or_default();
if remote_message_id != message_id {
warn!(
context,
"Cannot delete on IMAP, {}: remote message-id '{}' != '{}'",
display_imap_id,
remote_message_id,
message_id,
);
}
*uid = 0;
}
Err(err) => {
warn!(
context,
"Cannot delete {} on IMAP: {}", display_imap_id, err
);
*uid = 0;
// mark the message for deletion
if self.add_flag(context, *server_uid, "\\Deleted") == 0 {
warn!(context, "Cannot mark message as \"Deleted\".");
} else {
self.config.write().unwrap().selected_folder_needs_expunge = true;
success = true
}
}
}
// mark the message for deletion
if !self.add_flag_finalized(context, *uid, "\\Deleted") {
warn!(
context,
"Cannot mark message {} as \"Deleted\".", display_imap_id
);
ImapResult::Failed
if success {
1
} else {
emit_event!(
context,
Event::ImapMessageDeleted(format!(
"IMAP Message {} marked as deleted [{}]",
display_imap_id, message_id
))
);
self.config.write().unwrap().selected_folder_needs_expunge = true;
ImapResult::Success
self.is_connected() as usize
}
}
@@ -1410,18 +1544,18 @@ impl Imap {
context
.sql
.set_raw_config_int(context, "folders_configured", 3)
.set_config_int(context, "folders_configured", 3)
.ok();
if let Some(ref mvbox_folder) = mvbox_folder {
context
.sql
.set_raw_config(context, "configured_mvbox_folder", Some(mvbox_folder))
.set_config(context, "configured_mvbox_folder", Some(mvbox_folder))
.ok();
}
if let Some(ref sentbox_folder) = sentbox_folder {
context
.sql
.set_raw_config(
.set_config(
context,
"configured_sentbox_folder",
Some(sentbox_folder.name()),
@@ -1501,11 +1635,27 @@ fn get_folder_meaning(folder_name: &imap::types::Name) -> FolderMeaning {
}
fn precheck_imf(context: &Context, rfc724_mid: &str, server_folder: &str, server_uid: u32) -> bool {
let mut rfc724_mid_exists = false;
let mut mark_seen = false;
if let Ok((old_server_folder, old_server_uid, msg_id)) =
message::rfc724_mid_exists(context, &rfc724_mid)
{
rfc724_mid_exists = true;
if old_server_folder.is_empty() && old_server_uid == 0 {
info!(context, "[move] detected bbc-self {}", rfc724_mid,);
mark_seen = true;
} else if old_server_folder != server_folder {
info!(context, "[move] detected moved message {}", rfc724_mid,);
update_msg_move_state(context, &rfc724_mid, MoveState::Stay);
}
if old_server_folder != server_folder || old_server_uid != server_uid {
update_server_uid(context, &rfc724_mid, server_folder, server_uid);
}
context.do_heuristics_moves(server_folder, msg_id);
if mark_seen {
job_add(
context,
Action::MarkseenMsgOnImap,
@@ -1513,21 +1663,8 @@ fn precheck_imf(context: &Context, rfc724_mid: &str, server_folder: &str, server
Params::new(),
0,
);
} else if old_server_folder != server_folder {
info!(context, "[move] detected moved message {}", rfc724_mid,);
update_msg_move_state(context, &rfc724_mid, MoveState::Stay);
}
if old_server_folder != server_folder || old_server_uid != server_uid {
update_server_uid(context, &rfc724_mid, server_folder, server_uid);
}
true
} else {
false
}
}
fn prefetch_get_message_id(prefetch_msg: &imap::types::Fetch) -> Result<String, Error> {
let message_id = prefetch_msg.envelope().unwrap().message_id.unwrap();
wrapmime::parse_message_id(&message_id)
rfc724_mid_exists
}

View File

@@ -90,7 +90,7 @@ pub fn has_backup(context: &Context, dir_name: impl AsRef<Path>) -> Result<Strin
let sql = Sql::new();
if sql.open(context, &path, 0x1) {
let curr_backup_time =
sql.get_raw_config_int(context, "backup_time")
sql.get_config_int(context, "backup_time")
.unwrap_or_default() as u64;
if curr_backup_time > newest_backup_time {
newest_backup_path = Some(path);
@@ -129,15 +129,14 @@ pub fn initiate_key_transfer(context: &Context) -> Result<String> {
.unwrap()
.shall_stop_ongoing
{
let setup_file_name = context.new_blob_file(
"autocrypt-setup-message.html",
setup_file_content.as_bytes(),
)?;
{
let setup_file_name =
dc_get_fine_path_filename(context, "$BLOBDIR", "autocrypt-setup-message.html");
if dc_write_file(context, &setup_file_name, setup_file_content.as_bytes()) {
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
msg = Message::default();
msg.type_0 = Viewtype::File;
msg.param.set(Param::File, setup_file_name);
msg.param
.set(Param::File, setup_file_name.to_string_lossy());
msg.param
.set(Param::MimeType, "application/autocrypt-setup");
@@ -195,9 +194,13 @@ pub fn render_setup_file(context: &Context, passphrase: &str) -> Result<String>
let self_addr = e2ee::ensure_secret_key_exists(context)?;
let private_key = Key::from_self_private(context, self_addr, &context.sql)
.ok_or(format_err!("Failed to get private key."))?;
let ac_headers = match context.get_config_bool(Config::E2eeEnabled) {
false => None,
true => Some(("Autocrypt-Prefer-Encrypt", "mutual")),
let ac_headers = match context
.sql
.get_config_int(context, Config::E2eeEnabled)
.unwrap_or(1)
{
0 => None,
_ => Some(("Autocrypt-Prefer-Encrypt", "mutual")),
};
let private_key_asc = private_key.to_asc(ac_headers);
let encr = dc_pgp_symm_encrypt(&passphrase, private_key_asc.as_bytes())?;
@@ -263,7 +266,7 @@ pub fn continue_key_transfer(context: &Context, msg_id: u32, setup_code: &str) -
if msg.is_err() {
bail!("Message is no Autocrypt Setup Message.");
}
let msg = msg.unwrap_or_default();
let msg = msg.unwrap();
ensure!(
msg.is_setupmessage(),
"Message is no Autocrypt Setup Message."
@@ -313,7 +316,7 @@ fn set_self_key(
};
context
.sql
.set_raw_config_int(context, "e2ee_enabled", e2ee_enabled)?;
.set_config_int(context, "e2ee_enabled", e2ee_enabled)?;
}
None => {
if prefer_encrypt_required {
@@ -346,7 +349,7 @@ fn set_self_key(
context,
&public_key,
&private_key,
self_addr.unwrap_or_default(),
self_addr.unwrap(),
set_default,
&context.sql,
) {
@@ -474,7 +477,7 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
!dc_is_configured(context),
"Cannot import backups to accounts in use."
);
context.sql.close(&context);
&context.sql.close(&context);
dc_delete_file(context, context.get_dbfile());
ensure!(
!dc_file_exist(context, context.get_dbfile()),
@@ -511,7 +514,9 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
Ok((name, blob))
},
|files| {
for (processed_files_cnt, file) in files.enumerate() {
let mut processed_files_cnt = 0;
for file in files {
let (file_name, file_blob) = file?;
if context
.running_state
@@ -522,6 +527,7 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
{
bail!("received stop signal");
}
processed_files_cnt += 1;
let mut permille = processed_files_cnt * 1000 / total_files_cnt;
if permille < 10 {
permille = 10
@@ -567,10 +573,13 @@ fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
let mut delete_dest_file: libc::c_int = 0;
// get a fine backup file name (the name includes the date so that multiple backup instances are possible)
// FIXME: we should write to a temporary file first and rename it on success. this would guarantee the backup is complete.
// let dest_path_filename = dc_get_next_backup_file(context, dir, res);
// FIXME: we should write to a temporary file first and rename it on success. this would guarantee the backup is complete. however, currently it is not clear it the import exists in the long run (may be replaced by a restore-from-imap)
let now = time();
let dest_path_filename = dc_get_next_backup_path(dir, now)?;
let res = chrono::NaiveDateTime::from_timestamp(now as i64, 0)
.format("delta-chat-%Y-%m-%d.bak")
.to_string();
let dest_path_filename = dc_get_fine_path_filename(context, dir, res);
sql::housekeeping(context);
@@ -682,7 +691,7 @@ fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
}
Ok(())
}
).unwrap_or_default();
).unwrap();
} else {
error!(
context,
@@ -695,7 +704,7 @@ fn export_backup(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
}
if ok_to_continue {
if sql
.set_raw_config_int(context, "backup_time", now as i32)
.set_config_int(context, "backup_time", now as i32)
.is_ok()
{
context.call_cb(Event::ImexFileWritten(dest_path_filename.clone()));
@@ -763,7 +772,7 @@ fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
}
let ccontent = if let Ok(content) = dc_read_file(context, &path_plus_name) {
key = String::from_utf8_lossy(&content).to_string();
CString::new(content).unwrap_or_default()
CString::new(content).unwrap()
} else {
continue;
};
@@ -801,7 +810,7 @@ fn import_self_keys(context: &Context, dir: impl AsRef<Path>) -> Result<()> {
"No private keys found in \"{}\".",
dir_name
);
Ok(())
return Ok(());
} else {
bail!("Import: Cannot open directory \"{}\".", dir_name);
}
@@ -978,17 +987,16 @@ mod tests {
let mut setupcodebegin = ptr::null();
let mut preferencrypt = ptr::null();
let mut buf_1 = S_EM_SETUPFILE.to_string();
unsafe {
let buf_1 = S_EM_SETUPFILE.strdup();
let res = dc_split_armored_data(
buf_1,
assert!(dc_split_armored_data(
buf_1.as_mut_ptr().cast(),
&mut headerline,
&mut setupcodebegin,
&mut preferencrypt,
ptr::null_mut(),
);
libc::free(buf_1 as *mut libc::c_void);
assert!(res);
));
}
assert_eq!(headerline, "-----BEGIN PGP MESSAGE-----");
assert!(!setupcodebegin.is_null());
@@ -999,20 +1007,18 @@ mod tests {
assert!(preferencrypt.is_null());
let mut setup_file = S_EM_SETUPFILE.to_string();
let decrypted = unsafe {
let mut decrypted = unsafe {
decrypt_setup_file(context, S_EM_SETUPCODE, setup_file.as_bytes_mut()).unwrap()
};
unsafe {
let buf1 = decrypted.strdup();
assert!(dc_split_armored_data(
buf1,
decrypted.as_mut_ptr().cast(),
&mut headerline,
&mut setupcodebegin,
&mut preferencrypt,
ptr::null_mut(),
));
libc::free(buf1 as *mut libc::c_void);
}
assert_eq!(headerline, "-----BEGIN PGP PRIVATE KEY BLOCK-----");

View File

@@ -4,15 +4,13 @@ use deltachat_derive::{FromSql, ToSql};
use rand::{thread_rng, Rng};
use crate::chat;
use crate::config::Config;
use crate::configure::*;
use crate::constants::*;
use crate::context::Context;
use crate::dc_tools::*;
use crate::error::Error;
use crate::events::Event;
use crate::imap::*;
use crate::imex::job_do_DC_JOB_IMEX_IMAP;
use crate::imex::*;
use crate::location;
use crate::login_param::LoginParam;
use crate::message::{self, Message, MessageState};
@@ -142,11 +140,11 @@ impl Job {
if let Ok(body) = dc_read_file(context, filename) {
if let Some(recipients) = self.param.get(Param::Recipients) {
let recipients_list = recipients
.split('\x1e')
.split("\x1e")
.filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) {
Ok(addr) => Some(addr),
Err(err) => {
warn!(context, "invalid recipient: {} {:?}", addr, err);
eprintln!("WARNING: invalid recipient: {} {:?}", addr, err);
None
}
})
@@ -158,45 +156,39 @@ impl Job {
if 0 != self.foreign_id && !message::exists(context, self.foreign_id) {
warn!(
context,
"Not sending Message {} as it was deleted", self.foreign_id
"Message {} for job {} does not exist", self.foreign_id, self.job_id,
);
return;
};
// hold the smtp lock during sending of a job and
// its ok/error response processing. Note that if a message
// was sent we need to mark it in the database ASAP as we
// was sent we need to mark it in the database as we
// otherwise might send it twice.
let mut sock = context.smtp.lock().unwrap();
match sock.send(context, recipients_list, body) {
Err(err) => {
sock.disconnect();
warn!(context, "smtp failed: {}", err);
self.try_again_later(-1i32, Some(err.to_string()));
}
Ok(()) => {
// smtp success, update db ASAP, then delete smtp file
if 0 != self.foreign_id {
message::update_msg_state(
if 0 == sock.send(context, recipients_list, body) {
sock.disconnect();
self.try_again_later(-1i32, sock.error.clone());
} else {
dc_delete_file(context, filename);
if 0 != self.foreign_id {
message::update_msg_state(
context,
self.foreign_id,
MessageState::OutDelivered,
);
let chat_id: i32 = context
.sql
.query_get_value(
context,
self.foreign_id,
MessageState::OutDelivered,
);
let chat_id: i32 = context
.sql
.query_get_value(
context,
"SELECT chat_id FROM msgs WHERE id=?",
params![self.foreign_id as i32],
)
.unwrap_or_default();
context.call_cb(Event::MsgDelivered {
chat_id: chat_id as u32,
msg_id: self.foreign_id,
});
}
// now also delete the generated file
dc_delete_file(context, filename);
"SELECT chat_id FROM msgs WHERE id=?",
params![self.foreign_id as i32],
)
.unwrap_or_default();
context.call_cb(Event::MsgDelivered {
chat_id: chat_id as u32,
msg_id: self.foreign_id,
});
}
}
} else {
@@ -216,18 +208,23 @@ impl Job {
fn do_DC_JOB_MOVE_MSG(&mut self, context: &Context) {
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3, None);
return;
}
}
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
if context
.sql
.get_raw_config_int(context, "folders_configured")
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
inbox.configure_folders(context, 0x1i32);
}
let dest_folder = context
.sql
.get_raw_config(context, "configured_mvbox_folder");
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
if let Some(dest_folder) = dest_folder {
let server_folder = msg.server_folder.as_ref().unwrap();
@@ -264,18 +261,26 @@ impl Job {
if let Ok(mut msg) = Message::load_from_db(context, self.foreign_id) {
if !msg.rfc724_mid.is_empty() {
/* eg. device messages have no Message-ID */
if message::rfc724_mid_cnt(context, &msg.rfc724_mid) > 1 {
let mut delete_from_server = true;
if message::rfc724_mid_cnt(context, &msg.rfc724_mid) != 1 {
info!(
context,
"The message is deleted from the server when all parts are deleted.",
);
} else {
/* if this is the last existing part of the message,
we delete the message from the server */
delete_from_server = false;
}
/* if this is the last existing part of the message, we delete the message from the server */
if delete_from_server {
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3i32, None);
return;
}
}
let mid = msg.rfc724_mid;
let server_folder = msg.server_folder.as_ref().unwrap();
let res = inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid);
if res == ImapResult::RetryLater {
if 0 == inbox.delete_msg(context, &mid, server_folder, &mut msg.server_uid) {
self.try_again_later(-1i32, None);
return;
}
@@ -289,23 +294,37 @@ impl Job {
fn do_DC_JOB_MARKSEEN_MSG_ON_IMAP(&mut self, context: &Context) {
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3i32, None);
return;
}
}
if let Ok(msg) = Message::load_from_db(context, self.foreign_id) {
let folder = msg.server_folder.as_ref().unwrap();
match inbox.set_seen(context, folder, msg.server_uid) {
let server_folder = msg.server_folder.as_ref().unwrap();
match inbox.set_seen(context, server_folder, msg.server_uid) {
ImapResult::Failed => {}
ImapResult::RetryLater => {
self.try_again_later(3i32, None);
}
ImapResult::AlreadyDone => {}
ImapResult::Success | ImapResult::Failed => {
// XXX the message might just have been moved
// we want to send out an MDN anyway
// The job will not be retried so locally
// there is no risk of double-sending MDNs.
_ => {
if 0 != msg.param.get_int(Param::WantsMdn).unwrap_or_default()
&& context.get_config_bool(Config::MdnsEnabled)
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
if let Err(err) = send_mdn(context, msg.id) {
warn!(context, "could not send out mdn for {}: {}", msg.id, err);
let folder = msg.server_folder.as_ref().unwrap();
match inbox.set_mdnsent(context, folder, msg.server_uid) {
ImapResult::RetryLater => {
self.try_again_later(3i32, None);
}
ImapResult::Success => {
send_mdn(context, msg.id);
}
ImapResult::Failed | ImapResult::AlreadyDone => {}
}
}
}
@@ -322,26 +341,31 @@ impl Job {
.to_string();
let uid = self.param.get_int(Param::ServerUid).unwrap_or_default() as u32;
let inbox = context.inbox.read().unwrap();
if inbox.set_seen(context, &folder, uid) == ImapResult::RetryLater {
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
self.try_again_later(3, None);
return;
}
}
if inbox.set_seen(context, &folder, uid) == ImapResult::Failed {
self.try_again_later(3i32, None);
return;
}
if 0 != self.param.get_int(Param::AlsoMove).unwrap_or_default() {
if context
.sql
.get_raw_config_int(context, "folders_configured")
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
inbox.configure_folders(context, 0x1i32);
}
let dest_folder = context
.sql
.get_raw_config(context, "configured_mvbox_folder");
let dest_folder = context.sql.get_config(context, "configured_mvbox_folder");
if let Some(dest_folder) = dest_folder {
let mut dest_uid = 0;
if ImapResult::RetryLater
== inbox.mv(context, &folder, uid, &dest_folder, &mut dest_uid)
== inbox.mv(context, folder, uid, dest_folder, &mut dest_uid)
{
self.try_again_later(3, None);
}
@@ -368,7 +392,12 @@ pub fn perform_imap_fetch(context: &Context) {
if 0 == connect_to_inbox(context, &inbox) {
return;
}
if !context.get_config_bool(Config::InboxWatch) {
if context
.sql
.get_config_int(context, "inbox_watch")
.unwrap_or_else(|| 1)
== 0
{
info!(context, "INBOX-watch disabled.",);
return;
}
@@ -403,23 +432,29 @@ pub fn perform_imap_idle(context: &Context) {
}
pub fn perform_mvbox_fetch(context: &Context) {
let use_network = context.get_config_bool(Config::MvboxWatch);
let use_network = context
.sql
.get_config_int(context, "mvbox_watch")
.unwrap_or_else(|| 1);
context
.mvbox_thread
.write()
.unwrap()
.fetch(context, use_network);
.fetch(context, use_network == 1);
}
pub fn perform_mvbox_idle(context: &Context) {
let use_network = context.get_config_bool(Config::MvboxWatch);
let use_network = context
.sql
.get_config_int(context, "mvbox_watch")
.unwrap_or_else(|| 1);
context
.mvbox_thread
.read()
.unwrap()
.idle(context, use_network);
.idle(context, use_network == 1);
}
pub fn interrupt_mvbox_idle(context: &Context) {
@@ -427,23 +462,29 @@ pub fn interrupt_mvbox_idle(context: &Context) {
}
pub fn perform_sentbox_fetch(context: &Context) {
let use_network = context.get_config_bool(Config::SentboxWatch);
let use_network = context
.sql
.get_config_int(context, "sentbox_watch")
.unwrap_or_else(|| 1);
context
.sentbox_thread
.write()
.unwrap()
.fetch(context, use_network);
.fetch(context, use_network == 1);
}
pub fn perform_sentbox_idle(context: &Context) {
let use_network = context.get_config_bool(Config::SentboxWatch);
let use_network = context
.sql
.get_config_int(context, "sentbox_watch")
.unwrap_or_else(|| 1);
context
.sentbox_thread
.read()
.unwrap()
.idle(context, use_network);
.idle(context, use_network == 1);
}
pub fn interrupt_sentbox_idle(context: &Context) {
@@ -560,108 +601,109 @@ pub fn job_action_exists(context: &Context, action: Action) -> bool {
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
#[allow(non_snake_case)]
pub fn job_send_msg(context: &Context, msg_id: u32) -> Result<(), Error> {
let mut mimefactory = MimeFactory::load_msg(context, msg_id)?;
pub fn job_send_msg(context: &Context, msg_id: u32) -> libc::c_int {
let mut success = 0;
if chat::msgtype_has_file(mimefactory.msg.type_0) {
let file_param = mimefactory
.msg
.param
.get(Param::File)
.map(|s| s.to_string());
if let Some(pathNfilename) = file_param {
if (mimefactory.msg.type_0 == Viewtype::Image
|| mimefactory.msg.type_0 == Viewtype::Gif)
&& !mimefactory.msg.param.exists(Param::Width)
{
mimefactory.msg.param.set_int(Param::Width, 0);
mimefactory.msg.param.set_int(Param::Height, 0);
if let Ok(buf) = dc_read_file(context, pathNfilename) {
if let Ok((width, height)) = dc_get_filemeta(&buf) {
mimefactory.msg.param.set_int(Param::Width, width as i32);
mimefactory.msg.param.set_int(Param::Height, height as i32);
}
}
mimefactory.msg.save_param_to_disk(context);
}
}
}
/* create message */
if let Err(msg) = unsafe { mimefactory.render() } {
let e = msg.to_string();
message::set_msg_failed(context, msg_id, Some(e));
return Err(msg);
}
if 0 != mimefactory
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
&& !mimefactory.out_encrypted
{
/* unrecoverable */
message::set_msg_failed(
context,
msg_id,
Some("End-to-end-encryption unavailable unexpectedly."),
);
bail!(
"e2e encryption unavailable {} - {:?}",
msg_id,
mimefactory.msg.param.get_int(Param::GuranteeE2ee),
);
}
if context.get_config_bool(Config::BccSelf)
&& !vec_contains_lowercase(&mimefactory.recipients_addr, &mimefactory.from_addr)
{
mimefactory.recipients_names.push("".to_string());
mimefactory
.recipients_addr
.push(mimefactory.from_addr.to_string());
}
if mimefactory.recipients_addr.is_empty() {
/* load message data */
let mimefactory = MimeFactory::load_msg(context, msg_id);
if mimefactory.is_err() {
warn!(
context,
"message {} has no recipient, skipping smtp-send", msg_id
"Cannot load data to send, maybe the message is deleted in between.",
);
return Ok(());
}
} else {
let mut mimefactory = mimefactory.unwrap();
// no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed()
if chat::msgtype_has_file(mimefactory.msg.type_0) {
let file_param = mimefactory
.msg
.param
.get(Param::File)
.map(|s| s.to_string());
if let Some(pathNfilename) = file_param {
if (mimefactory.msg.type_0 == Viewtype::Image
|| mimefactory.msg.type_0 == Viewtype::Gif)
&& !mimefactory.msg.param.exists(Param::Width)
{
mimefactory.msg.param.set_int(Param::Width, 0);
mimefactory.msg.param.set_int(Param::Height, 0);
if mimefactory.out_gossiped {
chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time());
}
if 0 != mimefactory.out_last_added_location_id {
if let Err(err) = location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time())
{
error!(context, "Failed to set kml sent_timestamp: {:?}", err);
}
if !mimefactory.msg.hidden {
if let Err(err) = location::set_msg_location_id(
context,
mimefactory.msg.id,
mimefactory.out_last_added_location_id,
) {
error!(context, "Failed to set msg_location_id: {:?}", err);
if let Ok(buf) = dc_read_file(context, pathNfilename) {
if let Ok((width, height)) = dc_get_filemeta(&buf) {
mimefactory.msg.param.set_int(Param::Width, width as i32);
mimefactory.msg.param.set_int(Param::Height, height as i32);
}
}
mimefactory.msg.save_param_to_disk(context);
}
}
}
/* create message */
if let Err(msg) = unsafe { mimefactory.render() } {
let e = msg.to_string();
message::set_msg_failed(context, msg_id, Some(e));
} else if 0
!= mimefactory
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
&& !mimefactory.out_encrypted
{
/* unrecoverable */
warn!(
context,
"e2e encryption unavailable {} - {:?}",
msg_id,
mimefactory.msg.param.get_int(Param::GuranteeE2ee),
);
message::set_msg_failed(
context,
msg_id,
Some("End-to-end-encryption unavailable unexpectedly."),
);
} else {
if !vec_contains_lowercase(&mimefactory.recipients_addr, &mimefactory.from_addr) {
mimefactory.recipients_names.push("".to_string());
mimefactory
.recipients_addr
.push(mimefactory.from_addr.to_string());
}
if mimefactory.out_gossiped {
chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time());
}
if 0 != mimefactory.out_last_added_location_id {
if let Err(err) =
location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time())
{
error!(context, "Failed to set kml sent_timestamp: {:?}", err);
}
if !mimefactory.msg.hidden {
if let Err(err) = location::set_msg_location_id(
context,
mimefactory.msg.id,
mimefactory.out_last_added_location_id,
) {
error!(context, "Failed to set msg_location_id: {:?}", err);
}
}
}
if mimefactory.out_encrypted
&& mimefactory
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
== 0
{
mimefactory.msg.param.set_int(Param::GuranteeE2ee, 1);
mimefactory.msg.save_param_to_disk(context);
}
success = add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory);
}
}
if mimefactory.out_encrypted
&& mimefactory
.msg
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
== 0
{
mimefactory.msg.param.set_int(Param::GuranteeE2ee, 1);
mimefactory.msg.save_param_to_disk(context);
}
add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory)?;
Ok(())
success
}
pub fn perform_imap_jobs(context: &Context) {
@@ -751,13 +793,13 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
// - they can be re-executed one time AT_ONCE, but they are not save in the database for later execution
if Action::ConfigureImap == job.action || Action::ImexImap == job.action {
job_kill_action(context, job.action);
context
&context
.sentbox_thread
.clone()
.read()
.unwrap()
.suspend(context);
context
&context
.mvbox_thread
.clone()
.read()
@@ -781,8 +823,13 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
Action::MarkseenMdnOnImap => job.do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context),
Action::MoveMsg => job.do_DC_JOB_MOVE_MSG(context),
Action::SendMdn => job.do_DC_JOB_SEND(context),
Action::ImexImap => job_do_DC_JOB_IMEX_IMAP(context, &job),
Action::ConfigureImap => dc_job_do_DC_JOB_CONFIGURE_IMAP(context),
Action::ConfigureImap => unsafe { dc_job_do_DC_JOB_CONFIGURE_IMAP(context) },
Action::ImexImap => match job_do_DC_JOB_IMEX_IMAP(context, &job) {
Ok(()) => {}
Err(err) => {
error!(context, "{}", err);
}
},
Action::MaybeSendLocations => {
location::job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context, &job)
}
@@ -877,7 +924,8 @@ fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
#[allow(non_snake_case)]
fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 {
// results in ~3 weeks for the last backoff timespan
let N = 2_i32.pow((c_tries - 1) as u32) * 60;
let mut N = 2_i32.pow((c_tries - 1) as u32);
N = N * 60;
let mut rng = thread_rng();
let n: i32 = rng.gen();
let mut seconds = n % (N + 1);
@@ -899,7 +947,7 @@ fn suspend_smtp_thread(context: &Context, suspend: bool) {
}
}
pub fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
let ret_connected = dc_connect_to_configured_imap(context, inbox);
if 0 != ret_connected {
inbox.set_watch_folder("INBOX".into());
@@ -907,44 +955,51 @@ pub fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
ret_connected
}
fn send_mdn(context: &Context, msg_id: u32) -> Result<(), Error> {
let mut mimefactory = MimeFactory::load_mdn(context, msg_id)?;
unsafe { mimefactory.render()? };
add_smtp_job(context, Action::SendMdn, &mut mimefactory)?;
Ok(())
fn send_mdn(context: &Context, msg_id: u32) {
if let Ok(mut mimefactory) = MimeFactory::load_mdn(context, msg_id) {
if unsafe { mimefactory.render() }.is_ok() {
add_smtp_job(context, Action::SendMdn, &mut mimefactory);
}
}
}
#[allow(non_snake_case)]
fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) -> Result<(), Error> {
ensure!(
!mimefactory.recipients_addr.is_empty(),
"no recipients for smtp job set"
);
fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut param = Params::new();
let path_filename = dc_get_fine_path_filename(context, "$BLOBDIR", &mimefactory.rfc724_mid);
let bytes = unsafe {
std::slice::from_raw_parts(
(*mimefactory.out).str_0 as *const u8,
(*mimefactory.out).len,
)
};
let bpath = context.new_blob_file(&mimefactory.rfc724_mid, bytes)?;
let recipients = mimefactory.recipients_addr.join("\x1e");
param.set(Param::File, &bpath);
param.set(Param::Recipients, &recipients);
job_add(
context,
action,
(if mimefactory.loaded == Loaded::Message {
mimefactory.msg.id
} else {
0
}) as libc::c_int,
param,
0,
);
Ok(())
if !dc_write_file(context, &path_filename, bytes) {
error!(
context,
"Could not write message <{}> to \"{}\".",
mimefactory.rfc724_mid,
path_filename.display(),
);
} else {
info!(context, "add_smtp_job file written: {:?}", path_filename);
let recipients = mimefactory.recipients_addr.join("\x1e");
param.set(Param::File, path_filename.to_string_lossy());
param.set(Param::Recipients, &recipients);
job_add(
context,
action,
(if mimefactory.loaded == Loaded::Message {
mimefactory.msg.id
} else {
0
}) as libc::c_int,
param,
0,
);
success = 1;
}
success
}
pub fn job_add(

View File

@@ -116,14 +116,14 @@ impl JobThread {
if ret_connected {
if context
.sql
.get_raw_config_int(context, "folders_configured")
.get_config_int(context, "folders_configured")
.unwrap_or_default()
< 3
{
self.imap.configure_folders(context, 0x1);
}
if let Some(mvbox_name) = context.sql.get_raw_config(context, self.folder_config_name) {
if let Some(mvbox_name) = context.sql.get_config(context, self.folder_config_name) {
self.imap.set_watch_folder(mvbox_name);
} else {
self.imap.disconnect(context);

View File

@@ -163,8 +163,8 @@ impl Key {
pub fn to_bytes(&self) -> Vec<u8> {
match self {
Key::Public(k) => k.to_bytes().unwrap_or_default(),
Key::Secret(k) => k.to_bytes().unwrap_or_default(),
Key::Public(k) => k.to_bytes().unwrap(),
Key::Secret(k) => k.to_bytes().unwrap(),
}
}
@@ -218,10 +218,10 @@ impl Key {
let file_content = self.to_asc(None).into_bytes();
if dc_write_file(context, &file, &file_content) {
true
return true;
} else {
error!(context, "Cannot write key to {}", file.as_ref().display());
false
return false;
}
}

View File

@@ -3,7 +3,6 @@ use quick_xml;
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
use crate::chat;
use crate::config::Config;
use crate::constants::*;
use crate::context::*;
use crate::dc_tools::*;
@@ -63,7 +62,7 @@ impl Kml {
pub fn parse(context: &Context, content: impl AsRef<str>) -> Result<Self, Error> {
ensure!(
content.as_ref().len() <= (1024 * 1024),
content.as_ref().len() <= (1 * 1024 * 1024),
"A kml-files with {} bytes is larger than reasonably expected.",
content.as_ref().len()
);
@@ -219,7 +218,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
msg.text =
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
msg.param.set_int(Param::Cmd, 8);
chat::send_msg(context, chat_id, &mut msg).unwrap_or_default();
chat::send_msg(context, chat_id, &mut msg).unwrap();
} else if 0 == seconds && is_sending_locations_before {
let stock_str =
context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
@@ -360,7 +359,7 @@ pub fn get_range(
}
fn is_marker(txt: &str) -> bool {
txt.len() == 1 && !txt.starts_with(' ')
txt.len() == 1 && txt.chars().next().unwrap() != ' '
}
pub fn delete_all(context: &Context) -> Result<(), Error> {
@@ -376,7 +375,8 @@ pub fn get_kml(context: &Context, chat_id: u32) -> Result<(String, u32), Error>
let mut last_added_location_id = 0;
let self_addr = context
.get_config(Config::ConfiguredAddr)
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
let (locations_send_begin, locations_send_until, locations_last_sent) = context.sql.query_row(
@@ -615,7 +615,7 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
for (chat_id, mut msg) in msgs.into_iter() {
// TODO: better error handling
chat::send_msg(context, chat_id as u32, &mut msg).unwrap_or_default();
chat::send_msg(context, chat_id as u32, &mut msg).unwrap();
}
}
if 0 != continue_streaming {

View File

@@ -4,22 +4,6 @@ use std::fmt;
use crate::context::Context;
use crate::error::Error;
#[derive(Copy, Clone, Debug, Display, FromPrimitive)]
#[repr(i32)]
#[strum(serialize_all = "snake_case")]
pub enum CertificateChecks {
Automatic = 0,
Strict = 1,
AcceptInvalidHostnames = 2,
AcceptInvalidCertificates = 3,
}
impl Default for CertificateChecks {
fn default() -> Self {
Self::Automatic
}
}
#[derive(Default, Debug)]
pub struct LoginParam {
pub addr: String,
@@ -27,14 +11,10 @@ pub struct LoginParam {
pub mail_user: String,
pub mail_pw: String,
pub mail_port: i32,
/// IMAP TLS options: whether to allow invalid certificates and/or invalid hostnames
pub imap_certificate_checks: CertificateChecks,
pub send_server: String,
pub send_user: String,
pub send_pw: String,
pub send_port: i32,
/// SMTP TLS options: whether to allow invalid certificates and/or invalid hostnames
pub smtp_certificate_checks: CertificateChecks,
pub server_flags: i32,
}
@@ -51,53 +31,37 @@ impl LoginParam {
let key = format!("{}addr", prefix);
let addr = sql
.get_raw_config(context, key)
.get_config(context, key)
.unwrap_or_default()
.trim()
.to_string();
let key = format!("{}mail_server", prefix);
let mail_server = sql.get_raw_config(context, key).unwrap_or_default();
let mail_server = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}mail_port", prefix);
let mail_port = sql.get_raw_config_int(context, key).unwrap_or_default();
let mail_port = sql.get_config_int(context, key).unwrap_or_default();
let key = format!("{}mail_user", prefix);
let mail_user = sql.get_raw_config(context, key).unwrap_or_default();
let mail_user = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}mail_pw", prefix);
let mail_pw = sql.get_raw_config(context, key).unwrap_or_default();
let key = format!("{}imap_certificate_checks", prefix);
let imap_certificate_checks =
if let Some(certificate_checks) = sql.get_raw_config_int(context, key) {
num_traits::FromPrimitive::from_i32(certificate_checks).unwrap()
} else {
Default::default()
};
let mail_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_server", prefix);
let send_server = sql.get_raw_config(context, key).unwrap_or_default();
let send_server = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_port", prefix);
let send_port = sql.get_raw_config_int(context, key).unwrap_or_default();
let send_port = sql.get_config_int(context, key).unwrap_or_default();
let key = format!("{}send_user", prefix);
let send_user = sql.get_raw_config(context, key).unwrap_or_default();
let send_user = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}send_pw", prefix);
let send_pw = sql.get_raw_config(context, key).unwrap_or_default();
let key = format!("{}smtp_certificate_checks", prefix);
let smtp_certificate_checks =
if let Some(certificate_checks) = sql.get_raw_config_int(context, key) {
num_traits::FromPrimitive::from_i32(certificate_checks).unwrap()
} else {
Default::default()
};
let send_pw = sql.get_config(context, key).unwrap_or_default();
let key = format!("{}server_flags", prefix);
let server_flags = sql.get_raw_config_int(context, key).unwrap_or_default();
let server_flags = sql.get_config_int(context, key).unwrap_or_default();
LoginParam {
addr: addr.to_string(),
@@ -105,12 +69,10 @@ impl LoginParam {
mail_user,
mail_pw,
mail_port,
imap_certificate_checks,
send_server,
send_user,
send_pw,
send_port,
smtp_certificate_checks,
server_flags,
}
}
@@ -129,40 +91,34 @@ impl LoginParam {
let sql = &context.sql;
let key = format!("{}addr", prefix);
sql.set_raw_config(context, key, Some(&self.addr))?;
sql.set_config(context, key, Some(&self.addr))?;
let key = format!("{}mail_server", prefix);
sql.set_raw_config(context, key, Some(&self.mail_server))?;
sql.set_config(context, key, Some(&self.mail_server))?;
let key = format!("{}mail_port", prefix);
sql.set_raw_config_int(context, key, self.mail_port)?;
sql.set_config_int(context, key, self.mail_port)?;
let key = format!("{}mail_user", prefix);
sql.set_raw_config(context, key, Some(&self.mail_user))?;
sql.set_config(context, key, Some(&self.mail_user))?;
let key = format!("{}mail_pw", prefix);
sql.set_raw_config(context, key, Some(&self.mail_pw))?;
let key = format!("{}imap_certificate_checks", prefix);
sql.set_raw_config_int(context, key, self.imap_certificate_checks as i32)?;
sql.set_config(context, key, Some(&self.mail_pw))?;
let key = format!("{}send_server", prefix);
sql.set_raw_config(context, key, Some(&self.send_server))?;
sql.set_config(context, key, Some(&self.send_server))?;
let key = format!("{}send_port", prefix);
sql.set_raw_config_int(context, key, self.send_port)?;
sql.set_config_int(context, key, self.send_port)?;
let key = format!("{}send_user", prefix);
sql.set_raw_config(context, key, Some(&self.send_user))?;
sql.set_config(context, key, Some(&self.send_user))?;
let key = format!("{}send_pw", prefix);
sql.set_raw_config(context, key, Some(&self.send_pw))?;
let key = format!("{}smtp_certificate_checks", prefix);
sql.set_raw_config_int(context, key, self.smtp_certificate_checks as i32)?;
sql.set_config(context, key, Some(&self.send_pw))?;
let key = format!("{}server_flags", prefix);
sql.set_raw_config_int(context, key, self.server_flags)?;
sql.set_config_int(context, key, self.server_flags)?;
Ok(())
}
@@ -177,18 +133,16 @@ impl fmt::Display for LoginParam {
write!(
f,
"{} imap:{}:{}:{}:{}:cert_{} smtp:{}:{}:{}:{}:cert_{} {}",
"{} {}:{}:{}:{} {}:{}:{}:{} {}",
unset_empty(&self.addr),
unset_empty(&self.mail_user),
if !self.mail_pw.is_empty() { pw } else { unset },
unset_empty(&self.mail_server),
self.mail_port,
self.imap_certificate_checks,
unset_empty(&self.send_user),
if !self.send_pw.is_empty() { pw } else { unset },
unset_empty(&self.send_server),
self.send_port,
self.smtp_certificate_checks,
flags_readable,
)
}
@@ -250,41 +204,3 @@ fn get_readable_flags(flags: i32) -> String {
res
}
pub fn dc_build_tls(
certificate_checks: CertificateChecks,
) -> Result<native_tls::TlsConnector, native_tls::Error> {
let mut tls_builder = native_tls::TlsConnector::builder();
match certificate_checks {
CertificateChecks::Automatic => {
// Same as AcceptInvalidCertificates for now.
// TODO: use provider database when it becomes available
tls_builder
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true)
}
CertificateChecks::Strict => &mut tls_builder,
CertificateChecks::AcceptInvalidHostnames => {
tls_builder.danger_accept_invalid_hostnames(true)
}
CertificateChecks::AcceptInvalidCertificates => tls_builder
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true),
}
.build()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_certificate_checks_display() {
use std::string::ToString;
assert_eq!(
"accept_invalid_hostnames".to_string(),
CertificateChecks::AcceptInvalidHostnames.to_string()
);
}
}

View File

@@ -355,7 +355,7 @@ impl Message {
) && buf_headerline == "-----BEGIN PGP MESSAGE-----"
&& !buf_setupcodebegin.is_null()
{
return Some(to_string_lossy(buf_setupcodebegin));
return Some(to_string(buf_setupcodebegin));
}
}
}
@@ -494,10 +494,12 @@ impl Lot {
} else {
self.text1 = None;
}
} else if let Some(contact) = contact {
self.text1 = Some(contact.get_first_name().into());
} else {
self.text1 = None;
if let Some(contact) = contact {
self.text1 = Some(contact.get_first_name().into());
} else {
self.text1 = None;
}
}
self.text1_meaning = Meaning::Text1Username;
}
@@ -524,7 +526,7 @@ pub fn get_msg_info(context: &Context, msg_id: u32) -> String {
return ret;
}
let msg = msg.unwrap_or_default();
let msg = msg.unwrap();
let rawtxt: Option<String> = context.sql.query_get_value(
context,
@@ -536,7 +538,7 @@ pub fn get_msg_info(context: &Context, msg_id: u32) -> String {
ret += &format!("Cannot load message #{}.", msg_id as usize);
return ret;
}
let rawtxt = rawtxt.unwrap_or_default();
let rawtxt = rawtxt.unwrap();
let rawtxt = dc_truncate(rawtxt.trim(), 100000, false);
let fts = dc_timestamp_to_str(msg.get_timestamp());
@@ -616,8 +618,9 @@ pub fn get_msg_info(context: &Context, msg_id: u32) -> String {
}
ret += "\n";
if let Some(err) = msg.param.get(Param::Error) {
ret += &format!("Error: {}", err)
match msg.param.get(Param::Error) {
Some(err) => ret += &format!("Error: {}", err),
_ => {}
}
if let Some(path) = msg.get_file(context) {
@@ -685,7 +688,7 @@ pub fn get_mime_headers(context: &Context, msg_id: u32) -> Option<String> {
}
pub fn delete_msgs(context: &Context, msg_ids: &[u32]) {
for msg_id in msg_ids.iter() {
for msg_id in msg_ids.into_iter() {
update_msg_chat_id(context, *msg_id, DC_CHAT_ID_TRASH);
job_add(
context,
@@ -725,7 +728,7 @@ pub fn markseen_msgs(context: &Context, msg_ids: &[u32]) -> bool {
"SELECT m.state, c.blocked FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.id=? AND m.chat_id>9",
|mut stmt, _| {
let mut res = Vec::with_capacity(msg_ids.len());
for id in msg_ids.iter() {
for id in msg_ids.into_iter() {
let query_res = stmt.query_row(params![*id as i32], |row| {
Ok((row.get::<_, MessageState>(0)?, row.get::<_, Option<Blocked>>(1)?.unwrap_or_default()))
});
@@ -745,7 +748,7 @@ pub fn markseen_msgs(context: &Context, msg_ids: &[u32]) -> bool {
return false;
}
let mut send_event = false;
let msgs = msgs.unwrap_or_default();
let msgs = msgs.unwrap();
for (id, curr_state, curr_blocked) in msgs.into_iter() {
if curr_blocked == Blocked::Not {
@@ -795,7 +798,7 @@ pub fn star_msgs(context: &Context, msg_ids: &[u32], star: bool) -> bool {
context
.sql
.prepare("UPDATE msgs SET starred=? WHERE id=?;", |mut stmt, _| {
for msg_id in msg_ids.iter() {
for msg_id in msg_ids.into_iter() {
stmt.execute(params![star as i32, *msg_id as i32])?;
}
Ok(())
@@ -815,7 +818,6 @@ pub fn get_summarytext_by_raw(
let prefix = match viewtype {
Viewtype::Image => context.stock_str(StockMessage::Image).into_owned(),
Viewtype::Gif => context.stock_str(StockMessage::Gif).into_owned(),
Viewtype::Sticker => context.stock_str(StockMessage::Sticker).into_owned(),
Viewtype::Video => context.stock_str(StockMessage::Video).into_owned(),
Viewtype::Voice => context.stock_str(StockMessage::VoiceMessage).into_owned(),
Viewtype::Audio | Viewtype::File => {
@@ -834,7 +836,7 @@ pub fn get_summarytext_by_raw(
} else {
None
}
.unwrap_or_else(|| "ErrFileName".to_string());
.unwrap_or("ErrFileName".to_string());
let label = context.stock_str(if viewtype == Viewtype::Audio {
StockMessage::Audio
@@ -982,7 +984,7 @@ pub fn mdn_from_ext(
context.sql.execute(
"INSERT INTO msgs_mdns (msg_id, contact_id, timestamp_sent) VALUES (?, ?, ?);",
params![*ret_msg_id as i32, from_id as i32, timestamp_sent],
).unwrap_or_default(); // TODO: better error handling
).unwrap(); // TODO: better error handling
}
// Normal chat? that's quite easy.

View File

@@ -13,11 +13,10 @@ use mmime::mmapstring::*;
use mmime::other::*;
use crate::chat::{self, Chat};
use crate::config::Config;
use crate::constants::*;
use crate::contact::*;
use crate::context::{get_version_str, Context};
use crate::dc_mimeparser::SystemMessage;
use crate::dc_mimeparser::{mailmime_find_mailimf_fields, SystemMessage};
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::e2ee::*;
@@ -60,13 +59,11 @@ pub struct MimeFactory<'a> {
impl<'a> MimeFactory<'a> {
fn new(context: &'a Context, msg: Message) -> Self {
let cget = |context: &Context, name: &str| context.sql.get_config(context, name);
MimeFactory {
from_addr: context
.get_config(Config::ConfiguredAddr)
.unwrap_or_default(),
from_displayname: context.get_config(Config::Displayname).unwrap_or_default(),
selfstatus: context
.get_config(Config::Selfstatus)
from_addr: cget(&context, "configured_addr").unwrap_or_default(),
from_displayname: cget(&context, "displayname").unwrap_or_default(),
selfstatus: cget(&context, "selfstatus")
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
recipients_names: Vec::with_capacity(5),
recipients_addr: Vec::with_capacity(5),
@@ -109,10 +106,15 @@ impl<'a> MimeFactory<'a> {
}
pub fn load_mdn(context: &'a Context, msg_id: u32) -> Result<MimeFactory, Error> {
if !context.get_config_bool(Config::MdnsEnabled) {
// MDNs not enabled - check this is late, in the job. the
// user may have changed its choice while offline ...
bail!("MDNs meanwhile disabled")
if 0 == context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
// MDNs not enabled - check this is late, in the job. the use may have changed its
// choice while offline ...
bail!("MDNs disabled ")
}
let msg = Message::load_from_db(context, msg_id)?;
@@ -139,46 +141,52 @@ impl<'a> MimeFactory<'a> {
}
/*******************************************************************************
* Render a basic email
* Render
******************************************************************************/
// XXX restrict unsafe to parts, introduce wrapmime helpers where appropriate
// restrict unsafe to parts, introduce wrapmime helpers where appropriate
pub unsafe fn render(&mut self) -> Result<(), Error> {
if self.loaded == Loaded::Nothing || !self.out.is_null() {
bail!("Invalid use of mimefactory-object.");
}
let context = &self.context;
let from = wrapmime::new_mailbox_list(&self.from_displayname, &self.from_addr);
let to = mailimf_address_list_new_empty();
let name_iter = self.recipients_names.iter();
let addr_iter = self.recipients_addr.iter();
for (name, addr) in name_iter.zip(addr_iter) {
mailimf_address_list_add(
to,
mailimf_address_new(
MAILIMF_ADDRESS_MAILBOX as libc::c_int,
mailimf_mailbox_new(
/* create basic mail
*************************************************************************/
let from = mailimf_mailbox_list_new_empty();
mailimf_mailbox_list_add(
from,
mailimf_mailbox_new(
if !self.from_displayname.is_empty() {
dc_encode_header_words(&self.from_displayname).strdup()
} else {
ptr::null_mut()
},
self.from_addr.strdup(),
),
);
let mut to = ptr::null_mut();
if !self.recipients_names.is_empty() && !self.recipients_addr.is_empty() {
to = mailimf_address_list_new_empty();
let name_iter = self.recipients_names.iter();
let addr_iter = self.recipients_addr.iter();
for (name, addr) in name_iter.zip(addr_iter) {
mailimf_address_list_add(
to,
mailimf_address_new_mailbox(mailimf_mailbox_new(
if !name.is_empty() {
dc_encode_header_words(&name).strdup()
} else {
ptr::null_mut()
},
addr.strdup(),
),
ptr::null_mut(),
),
);
)),
);
}
}
let references_list = if !self.references.is_empty() {
dc_str_to_clist(&self.references, " ")
} else {
ptr::null_mut()
};
let in_reply_to_list = if !self.in_reply_to.is_empty() {
dc_str_to_clist(&self.in_reply_to, " ")
} else {
ptr::null_mut()
};
let references_list = dc_str_to_vec(&self.references, " ");
let in_reply_to_list = dc_str_to_vec(&self.in_reply_to, " ");
let imf_fields = mailimf_fields_new_with_data_all(
mailimf_get_date(self.timestamp as i64),
@@ -384,8 +392,15 @@ impl<'a> MimeFactory<'a> {
&fingerprint,
);
}
if let Some(id) = msg.param.get(Param::Arg4) {
wrapmime::new_custom_field(imf_fields, "Secure-Join-Group", &id);
match msg.param.get(Param::Arg4) {
Some(id) => {
wrapmime::new_custom_field(
imf_fields,
"Secure-Join-Group",
&id,
);
}
None => {}
};
}
}
@@ -410,10 +425,6 @@ impl<'a> MimeFactory<'a> {
}
}
if self.msg.type_0 == Viewtype::Sticker {
wrapmime::new_custom_field(imf_fields, "Chat-Content", "sticker");
}
if self.msg.type_0 == Viewtype::Voice
|| self.msg.type_0 == Viewtype::Audio
|| self.msg.type_0 == Viewtype::Video
@@ -573,6 +584,7 @@ impl<'a> MimeFactory<'a> {
wrapmime::set_body_text(mach_mime_part, &message_text2)?;
mailmime_add_part(multipart, mach_mime_part);
force_plaintext = DC_FP_NO_AUTOCRYPT_HEADER;
info!(context, "sending MDM {:?}", message_text2);
/* currently, we do not send MDNs encrypted:
- in a multi-device-setup that is not set up properly, MDNs would disturb the communication as they
are send automatically which may lead to spreading outdated Autocrypt headers.
@@ -595,35 +607,13 @@ impl<'a> MimeFactory<'a> {
mailimf_fields_add(
imf_fields,
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(),
mailimf_subject_new(dc_encode_header_words(subject_str).strdup()),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
),
mailimf_field::Subject(mailimf_subject_new(
dc_encode_header_words(subject_str).strdup(),
)),
);
/*just a pointer into mailmime structure, must not be freed*/
let imffields_unprotected = wrapmime::mailmime_find_mailimf_fields(message);
let imffields_unprotected = mailmime_find_mailimf_fields(message);
ensure!(
!imffields_unprotected.is_null(),
"could not find mime fields"
@@ -635,18 +625,17 @@ impl<'a> MimeFactory<'a> {
let aheader = encrypt_helper.get_aheader().to_string();
wrapmime::new_custom_field(imffields_unprotected, "Autocrypt", &aheader);
}
let finalized = if force_plaintext == 0 {
encrypt_helper.try_encrypt(
let mut finalized = false;
if force_plaintext == 0 {
finalized = encrypt_helper.try_encrypt(
self,
e2ee_guaranteed,
min_verified,
do_gossip,
message,
imffields_unprotected,
)?
} else {
false
};
)?;
}
if !finalized {
self.finalize_mime_message(message, false, false)?;
}
@@ -695,7 +684,7 @@ impl<'a> MimeFactory<'a> {
Ok(())
},
)
.unwrap_or_default();
.unwrap();
let command = factory.msg.param.get_cmd();
let msg = &factory.msg;
@@ -705,7 +694,8 @@ impl<'a> MimeFactory<'a> {
let email_to_remove = msg.param.get(Param::Arg).unwrap_or_default();
let self_addr = context
.get_config(Config::ConfiguredAddr)
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
if !email_to_remove.is_empty() && email_to_remove != self_addr {
@@ -717,7 +707,10 @@ impl<'a> MimeFactory<'a> {
}
if command != SystemMessage::AutocryptSetupMessage
&& command != SystemMessage::SecurejoinMessage
&& context.get_config_bool(Config::MdnsEnabled)
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
{
factory.req_mdn = true;
}
@@ -900,13 +893,13 @@ fn build_body_file(
let mime_sub = mailmime_new_empty(content, mime_fields);
let abs_path = dc_get_abs_path(context, path_filename)
.to_c_string()
.unwrap_or_default();
.unwrap();
mailmime_set_body_file(mime_sub, dc_strdup(abs_path.as_ptr()));
Ok((mime_sub, filename_to_send))
}
}
pub(crate) fn vec_contains_lowercase(vec: &[String], part: &str) -> bool {
pub(crate) fn vec_contains_lowercase(vec: &Vec<String>, part: &str) -> bool {
let partlc = part.to_lowercase();
for cur in vec.iter() {
if cur.to_lowercase() == partlc {

View File

@@ -50,7 +50,7 @@ pub fn dc_get_oauth2_url(
if let Some(oauth2) = Oauth2::from_address(addr) {
if context
.sql
.set_raw_config(
.set_config(
context,
"oauth2_pending_redirect_uri",
Some(redirect_uri.as_ref()),
@@ -82,17 +82,17 @@ pub fn dc_get_oauth2_access_token(
// read generated token
if !regenerate && !is_expired(context) {
let access_token = context.sql.get_raw_config(context, "oauth2_access_token");
let access_token = context.sql.get_config(context, "oauth2_access_token");
if access_token.is_some() {
// success
return access_token;
}
}
let refresh_token = context.sql.get_raw_config(context, "oauth2_refresh_token");
let refresh_token = context.sql.get_config(context, "oauth2_refresh_token");
let refresh_token_for = context
.sql
.get_raw_config(context, "oauth2_refresh_token_for")
.get_config(context, "oauth2_refresh_token_for")
.unwrap_or_else(|| "unset".into());
let (redirect_uri, token_url, update_redirect_uri_on_success) =
@@ -101,7 +101,7 @@ pub fn dc_get_oauth2_access_token(
(
context
.sql
.get_raw_config(context, "oauth2_pending_redirect_uri")
.get_config(context, "oauth2_pending_redirect_uri")
.unwrap_or_else(|| "unset".into()),
oauth2.init_token,
true,
@@ -114,7 +114,7 @@ pub fn dc_get_oauth2_access_token(
(
context
.sql
.get_raw_config(context, "oauth2_redirect_uri")
.get_config(context, "oauth2_redirect_uri")
.unwrap_or_else(|| "unset".into()),
oauth2.refresh_token,
false,
@@ -159,11 +159,11 @@ pub fn dc_get_oauth2_access_token(
if let Some(ref token) = response.refresh_token {
context
.sql
.set_raw_config(context, "oauth2_refresh_token", Some(token))
.set_config(context, "oauth2_refresh_token", Some(token))
.ok();
context
.sql
.set_raw_config(context, "oauth2_refresh_token_for", Some(code.as_ref()))
.set_config(context, "oauth2_refresh_token_for", Some(code.as_ref()))
.ok();
}
@@ -172,7 +172,7 @@ pub fn dc_get_oauth2_access_token(
if let Some(ref token) = response.access_token {
context
.sql
.set_raw_config(context, "oauth2_access_token", Some(token))
.set_config(context, "oauth2_access_token", Some(token))
.ok();
let expires_in = response
.expires_in
@@ -181,13 +181,13 @@ pub fn dc_get_oauth2_access_token(
.unwrap_or_else(|| 0);
context
.sql
.set_raw_config_int64(context, "oauth2_timestamp_expires", expires_in)
.set_config_int64(context, "oauth2_timestamp_expires", expires_in)
.ok();
if update_redirect_uri_on_success {
context
.sql
.set_raw_config(context, "oauth2_redirect_uri", Some(redirect_uri.as_ref()))
.set_config(context, "oauth2_redirect_uri", Some(redirect_uri.as_ref()))
.ok();
}
} else {
@@ -207,8 +207,14 @@ pub fn dc_get_oauth2_addr(
addr: impl AsRef<str>,
code: impl AsRef<str>,
) -> Option<String> {
let oauth2 = Oauth2::from_address(addr.as_ref())?;
oauth2.get_userinfo?;
let oauth2 = Oauth2::from_address(addr.as_ref());
if oauth2.is_none() {
return None;
}
let oauth2 = oauth2.unwrap();
if oauth2.get_userinfo.is_none() {
return None;
}
if let Some(access_token) =
dc_get_oauth2_access_token(context, addr.as_ref(), code.as_ref(), false)
@@ -293,7 +299,7 @@ impl Oauth2 {
fn is_expired(context: &Context) -> bool {
let expire_timestamp = context
.sql
.get_raw_config_int64(context, "oauth2_timestamp_expires")
.get_config_int64(context, "oauth2_timestamp_expires")
.unwrap_or_default();
if expire_timestamp <= 0 {

View File

@@ -12,65 +12,65 @@ use crate::error;
#[repr(u8)]
pub enum Param {
/// For messages and jobs
File = b'f',
File = 'f' as u8,
/// For Messages
Width = b'w',
Width = 'w' as u8,
/// For Messages
Height = b'h',
Height = 'h' as u8,
/// For Messages
Duration = b'd',
Duration = 'd' as u8,
/// For Messages
MimeType = b'm',
MimeType = 'm' as u8,
/// For Messages: message is encryoted, outgoing: guarantee E2EE or the message is not send
GuranteeE2ee = b'c',
GuranteeE2ee = 'c' as u8,
/// For Messages: decrypted with validation errors or without mutual set, if neither
/// 'c' nor 'e' are preset, the messages is only transport encrypted.
ErroneousE2ee = b'e',
ErroneousE2ee = 'e' as u8,
/// For Messages: force unencrypted message, either `ForcePlaintext::AddAutocryptHeader` (1),
/// `ForcePlaintext::NoAutocryptHeader` (2) or 0.
ForcePlaintext = b'u',
ForcePlaintext = 'u' as u8,
/// For Messages
WantsMdn = b'r',
WantsMdn = 'r' as u8,
/// For Messages
Forwarded = b'a',
Forwarded = 'a' as u8,
/// For Messages
Cmd = b'S',
Cmd = 'S' as u8,
/// For Messages
Arg = b'E',
Arg = 'E' as u8,
/// For Messages
Arg2 = b'F',
Arg2 = 'F' as u8,
/// For Messages
Arg3 = b'G',
Arg3 = 'G' as u8,
/// For Messages
Arg4 = b'H',
Arg4 = 'H' as u8,
/// For Messages
Error = b'L',
Error = 'L' as u8,
/// For Messages: space-separated list of messaged IDs of forwarded copies.
PrepForwards = b'P',
PrepForwards = 'P' as u8,
/// For Jobs
SetLatitude = b'l',
SetLatitude = 'l' as u8,
/// For Jobs
SetLongitude = b'n',
SetLongitude = 'n' as u8,
/// For Jobs
ServerFolder = b'Z',
ServerFolder = 'Z' as u8,
/// For Jobs
ServerUid = b'z',
ServerUid = 'z' as u8,
/// For Jobs
AlsoMove = b'M',
AlsoMove = 'M' as u8,
/// For Jobs: space-separated list of message recipients
Recipients = b'R',
Recipients = 'R' as u8,
// For Groups
Unpromoted = b'U',
Unpromoted = 'U' as u8,
// For Groups and Contacts
ProfileImage = b'i',
ProfileImage = 'i' as u8,
// For Chats
Selftalk = b'K',
Selftalk = 'K' as u8,
// For QR
Auth = b's',
Auth = 's' as u8,
// For QR
GroupId = b'x',
GroupId = 'x' as u8,
// For QR
GroupName = b'g',
GroupName = 'g' as u8,
}
/// Possible values for `Param::ForcePlaintext`.
@@ -122,8 +122,8 @@ impl str::FromStr for Params {
ensure!(key.is_some(), "Missing key");
ensure!(value.is_some(), "Missing value");
let key = key.unwrap_or_default().trim();
let value = value.unwrap_or_default().trim();
let key = key.unwrap().trim();
let value = value.unwrap().trim();
if let Some(key) = Param::from_u8(key.as_bytes()[0]) {
inner.insert(key, value.to_string());

View File

@@ -400,8 +400,7 @@ impl<'a> Peerstate<'a> {
&self.verified_key_fingerprint,
&self.addr,
],
)?;
reset_gossiped_timestamp(self.context, 0);
)?
} else if self.to_save == Some(ToSave::Timestamps) {
sql::execute(
self.context,
@@ -417,6 +416,10 @@ impl<'a> Peerstate<'a> {
)?;
}
if self.to_save == Some(ToSave::All) || create {
reset_gossiped_timestamp(self.context, 0);
}
Ok(())
}
@@ -472,7 +475,7 @@ mod tests {
"failed to save to db"
);
let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr)
let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into())
.expect("failed to load peerstate from db");
// clear to_save, as that is not persissted
@@ -484,44 +487,6 @@ mod tests {
assert_eq!(peerstate, peerstate_new2);
}
#[test]
fn test_peerstate_double_create() {
let ctx = crate::test_utils::dummy_context();
let addr = "hello@mail.com";
let pub_key = crate::key::Key::from_base64(
include_str!("../test-data/key/public.asc"),
KeyType::Public,
)
.unwrap();
let peerstate = Peerstate {
context: &ctx.ctx,
addr: Some(addr.into()),
last_seen: 10,
last_seen_autocrypt: 11,
prefer_encrypt: EncryptPreference::Mutual,
public_key: Some(pub_key.clone()),
public_key_fingerprint: Some(pub_key.fingerprint()),
gossip_key: None,
gossip_timestamp: 12,
gossip_key_fingerprint: None,
verified_key: None,
verified_key_fingerprint: None,
to_save: Some(ToSave::All),
degrade_event: None,
};
assert!(
peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(),
"failed to save"
);
assert!(
peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(),
"double-call with create failed"
);
}
#[test]
fn test_peerstate_with_empty_gossip_key_save_to_db() {
let ctx = crate::test_utils::dummy_context();
@@ -555,7 +520,7 @@ mod tests {
"failed to save"
);
let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr)
let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into())
.expect("failed to load peerstate from db");
// clear to_save, as that is not persissted

View File

@@ -43,7 +43,7 @@ pub unsafe fn dc_split_armored_data(
if !buf.is_null() {
dc_remove_cr_chars(buf);
while 0 != *p1 {
if i32::from(*p1) == '\n' as i32 {
if *p1 as libc::c_int == '\n' as i32 {
*line.offset(line_chars as isize) = 0i32 as libc::c_char;
if headerline.is_null() {
dc_trim(line);
@@ -69,7 +69,7 @@ pub unsafe fn dc_split_armored_data(
} else {
p2 = strchr(line, ':' as i32);
if p2.is_null() {
*line.add(line_chars) = '\n' as i32 as libc::c_char;
*line.offset(line_chars as isize) = '\n' as i32 as libc::c_char;
base64 = line;
break;
} else {
@@ -189,7 +189,7 @@ pub fn dc_pgp_pk_encrypt(
let lit_msg = Message::new_literal_bytes("", plain);
let pkeys: Vec<&SignedPublicKey> = public_keys_for_encryption
.keys()
.iter()
.into_iter()
.filter_map(|key| {
let k: &Key = &key;
k.try_into().ok()

View File

@@ -82,7 +82,10 @@ pub fn dc_get_securejoin_qr(context: &Context, group_chat_id: u32) -> Option<Str
}
};
let self_name = context.get_config(Config::Displayname).unwrap_or_default();
let self_name = context
.sql
.get_config(context, "displayname")
.unwrap_or_default();
fingerprint = match get_self_fingerprint(context) {
Some(fp) => fp,
@@ -210,7 +213,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 {
info!(context, "Taking protocol shortcut.");
context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM;
joiner_progress!(context, chat_id_2_contact_id(context, contact_chat_id), 400);
let own_fingerprint = get_self_fingerprint(context).unwrap_or_default();
let own_fingerprint = get_self_fingerprint(context).unwrap();
send_handshake_msg(
context,
contact_chat_id,
@@ -291,7 +294,7 @@ fn send_handshake_msg(
msg.param.set_int(Param::GuranteeE2ee, 1);
}
// TODO. handle cleanup on error
chat::send_msg(context, contact_chat_id, &mut msg).unwrap_or_default();
chat::send_msg(context, contact_chat_id, &mut msg).unwrap();
}
fn chat_id_2_contact_id(context: &Context, contact_chat_id: u32) -> u32 {
@@ -675,9 +678,7 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> Res
if peerstate.set_verified(1, fingerprint.as_ref(), 2) {
peerstate.prefer_encrypt = EncryptPreference::Mutual;
peerstate.to_save = Some(ToSave::All);
peerstate
.save_to_db(&context.sql, false)
.unwrap_or_default();
peerstate.save_to_db(&context.sql, false).unwrap();
return Ok(());
}
}
@@ -695,7 +696,7 @@ fn encrypted_and_signed(mimeparser: &MimeParser, expected_fingerprint: impl AsRe
if !mimeparser.encrypted {
warn!(mimeparser.context, "Message not encrypted.",);
false
} else if mimeparser.signatures.is_empty() {
} else if mimeparser.signatures.len() <= 0 {
warn!(mimeparser.context, "Message not signed.",);
false
} else if expected_fingerprint.as_ref().is_empty() {

View File

@@ -3,9 +3,8 @@ use lettre::*;
use crate::constants::*;
use crate::context::Context;
use crate::error::Error;
use crate::events::Event;
use crate::login_param::{dc_build_tls, LoginParam};
use crate::login_param::LoginParam;
use crate::oauth2::*;
#[derive(DebugStub)]
@@ -15,6 +14,7 @@ pub struct Smtp {
transport_connected: bool,
/// Email address we are sending from.
from: Option<EmailAddress>,
pub error: Option<String>,
}
impl Smtp {
@@ -24,6 +24,7 @@ impl Smtp {
transport: None,
transport_connected: false,
from: None,
error: None,
}
}
@@ -68,7 +69,14 @@ impl Smtp {
let domain = &lp.send_server;
let port = lp.send_port as u16;
let tls = dc_build_tls(lp.smtp_certificate_checks).unwrap();
let tls = native_tls::TlsConnector::builder()
// see also: https://github.com/deltachat/deltachat-core-rust/issues/203
.danger_accept_invalid_hostnames(true)
.danger_accept_invalid_certs(true)
.min_protocol_version(Some(DEFAULT_TLS_PROTOCOLS[0]))
.build()
.unwrap();
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls);
let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
@@ -81,10 +89,7 @@ impl Smtp {
}
let user = &lp.send_user;
lettre::smtp::authentication::Credentials::new(
user.to_string(),
access_token.unwrap_or_default(),
)
lettre::smtp::authentication::Credentials::new(user.to_string(), access_token.unwrap())
} else {
// plain
let user = lp.send_user.clone();
@@ -120,50 +125,37 @@ impl Smtp {
}
}
/// SMTP-Send a prepared mail to recipients.
/// returns boolean whether send was successful.
pub fn send<'a>(
&mut self,
context: &Context,
recipients: Vec<EmailAddress>,
message: Vec<u8>,
) -> Result<(), Error> {
let message_len = message.len();
let recipients_display = recipients
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<String>>()
.join(",");
body: Vec<u8>,
) -> usize {
if let Some(ref mut transport) = self.transport {
let envelope = Envelope::new(self.from.clone(), recipients);
ensure!(envelope.is_ok(), "internal smtp-message construction fail");
let envelope = envelope.unwrap();
let envelope = Envelope::new(self.from.clone(), recipients).expect("invalid envelope");
let mail = SendableEmail::new(
envelope,
"mail-id".into(), // TODO: random id
message,
body,
);
match transport.send(mail) {
Ok(_) => {
context.call_cb(Event::SmtpMessageSent(format!(
"Message len={} was smtp-sent to {}",
message_len, recipients_display
)));
context.call_cb(Event::SmtpMessageSent(
"Message was sent to SMTP server".into(),
));
self.transport_connected = true;
Ok(())
1
}
Err(err) => {
bail!("SMTP failed len={}: error: {}", message_len, err);
warn!(context, "SMTP failed to send message: {}", err);
self.error = Some(format!("{}", err));
0
}
}
} else {
bail!(
"uh? SMTP has no transport, failed to send to {:?}",
recipients_display
);
// TODO: log error
0
}
}
}

View File

@@ -1,6 +1,5 @@
use std::collections::HashSet;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS};
use thread_local_object::ThreadLocal;
@@ -69,16 +68,6 @@ impl Sql {
let res = match &*self.pool.read().unwrap() {
Some(pool) => {
let conn = pool.get()?;
// Only one process can make changes to the database at one time.
// busy_timeout defines, that if a seconds process wants write access,
// this second process will wait some milliseconds
// and try over until it gets write access or the given timeout is elapsed.
// If the second process does not get write access within the given timeout,
// sqlite3_step() will return the error SQLITE_BUSY.
// (without a busy_timeout, sqlite3_step() would return SQLITE_BUSY _at once_)
conn.busy_timeout(Duration::from_secs(10))?;
g(&conn)
}
None => Err(Error::SqlNoConnection),
@@ -193,14 +182,14 @@ impl Sql {
///
/// Setting `None` deletes the value. On failure an error message
/// will already have been logged.
pub fn set_raw_config(
pub fn set_config(
&self,
context: &Context,
key: impl AsRef<str>,
value: Option<&str>,
) -> Result<()> {
if !self.is_open() {
error!(context, "set_raw_config(): Database not ready.");
error!(context, "set_config(): Database not ready.");
return Err(Error::SqlNoConnection);
}
@@ -234,14 +223,14 @@ impl Sql {
match res {
Ok(_) => Ok(()),
Err(err) => {
error!(context, "set_raw_config(): Cannot change value. {:?}", &err);
Err(err)
error!(context, "set_config(): Cannot change value. {:?}", &err);
Err(err.into())
}
}
}
/// Get configuration options from the database.
pub fn get_raw_config(&self, context: &Context, key: impl AsRef<str>) -> Option<String> {
pub fn get_config(&self, context: &Context, key: impl AsRef<str>) -> Option<String> {
if !self.is_open() || key.as_ref().is_empty() {
return None;
}
@@ -252,46 +241,44 @@ impl Sql {
)
}
pub fn set_raw_config_int(
pub fn set_config_int(
&self,
context: &Context,
key: impl AsRef<str>,
value: i32,
) -> Result<()> {
self.set_raw_config(context, key, Some(&format!("{}", value)))
self.set_config(context, key, Some(&format!("{}", value)))
}
pub fn get_raw_config_int(&self, context: &Context, key: impl AsRef<str>) -> Option<i32> {
self.get_raw_config(context, key)
.and_then(|s| s.parse().ok())
pub fn get_config_int(&self, context: &Context, key: impl AsRef<str>) -> Option<i32> {
self.get_config(context, key).and_then(|s| s.parse().ok())
}
pub fn get_raw_config_bool(&self, context: &Context, key: impl AsRef<str>) -> bool {
pub fn get_config_bool(&self, context: &Context, key: impl AsRef<str>) -> bool {
// Not the most obvious way to encode bool as string, but it is matter
// of backward compatibility.
self.get_raw_config_int(context, key).unwrap_or_default() > 0
self.get_config_int(context, key).unwrap_or_default() > 0
}
pub fn set_raw_config_bool<T>(&self, context: &Context, key: T, value: bool) -> Result<()>
pub fn set_config_bool<T>(&self, context: &Context, key: T, value: bool) -> Result<()>
where
T: AsRef<str>,
{
let value = if value { Some("1") } else { None };
self.set_raw_config(context, key, value)
self.set_config(context, key, value)
}
pub fn set_raw_config_int64(
pub fn set_config_int64(
&self,
context: &Context,
key: impl AsRef<str>,
value: i64,
) -> Result<()> {
self.set_raw_config(context, key, Some(&format!("{}", value)))
self.set_config(context, key, Some(&format!("{}", value)))
}
pub fn get_raw_config_int64(&self, context: &Context, key: impl AsRef<str>) -> Option<i64> {
self.get_raw_config(context, key)
.and_then(|r| r.parse().ok())
pub fn get_config_int64(&self, context: &Context, key: impl AsRef<str>) -> Option<i64> {
self.get_config(context, key).and_then(|r| r.parse().ok())
}
fn start_stmt(&self, stmt: impl AsRef<str>) {
@@ -343,7 +330,7 @@ fn open(
.with_init(|c| c.execute_batch("PRAGMA secure_delete=on;"));
let pool = r2d2::Pool::builder()
.min_idle(Some(2))
.max_size(10)
.max_size(4)
.connection_timeout(std::time::Duration::new(60, 0))
.build(mgr)?;
@@ -479,13 +466,11 @@ fn open(
// cannot create the tables - maybe we cannot write?
return Err(Error::SqlFailedToOpen);
} else {
sql.set_raw_config_int(context, "dbversion", 0)?;
sql.set_config_int(context, "dbversion", 0)?;
}
} else {
exists_before_update = 1;
dbversion_before_update = sql
.get_raw_config_int(context, "dbversion")
.unwrap_or_default();
dbversion_before_update = sql.get_config_int(context, "dbversion").unwrap_or_default();
}
// (1) update low-level database structure.
@@ -497,7 +482,6 @@ fn open(
let mut update_file_paths = 0;
if dbversion < 1 {
info!(context, "[migration] v1");
sql.execute(
"CREATE TABLE leftgrps ( id INTEGER PRIMARY KEY, grpid TEXT DEFAULT '');",
params![],
@@ -507,19 +491,17 @@ fn open(
params![],
)?;
dbversion = 1;
sql.set_raw_config_int(context, "dbversion", 1)?;
sql.set_config_int(context, "dbversion", 1)?;
}
if dbversion < 2 {
info!(context, "[migration] v2");
sql.execute(
"ALTER TABLE contacts ADD COLUMN authname TEXT DEFAULT '';",
params![],
)?;
dbversion = 2;
sql.set_raw_config_int(context, "dbversion", 2)?;
sql.set_config_int(context, "dbversion", 2)?;
}
if dbversion < 7 {
info!(context, "[migration] v7");
sql.execute(
"CREATE TABLE keypairs (\
id INTEGER PRIMARY KEY, \
@@ -531,10 +513,9 @@ fn open(
params![],
)?;
dbversion = 7;
sql.set_raw_config_int(context, "dbversion", 7)?;
sql.set_config_int(context, "dbversion", 7)?;
}
if dbversion < 10 {
info!(context, "[migration] v10");
sql.execute(
"CREATE TABLE acpeerstates (\
id INTEGER PRIMARY KEY, \
@@ -550,10 +531,9 @@ fn open(
params![],
)?;
dbversion = 10;
sql.set_raw_config_int(context, "dbversion", 10)?;
sql.set_config_int(context, "dbversion", 10)?;
}
if dbversion < 12 {
info!(context, "[migration] v12");
sql.execute(
"CREATE TABLE msgs_mdns ( msg_id INTEGER, contact_id INTEGER);",
params![],
@@ -563,10 +543,9 @@ fn open(
params![],
)?;
dbversion = 12;
sql.set_raw_config_int(context, "dbversion", 12)?;
sql.set_config_int(context, "dbversion", 12)?;
}
if dbversion < 17 {
info!(context, "[migration] v17");
sql.execute(
"ALTER TABLE chats ADD COLUMN archived INTEGER DEFAULT 0;",
params![],
@@ -578,20 +557,18 @@ fn open(
)?;
sql.execute("CREATE INDEX msgs_index5 ON msgs (starred);", params![])?;
dbversion = 17;
sql.set_raw_config_int(context, "dbversion", 17)?;
sql.set_config_int(context, "dbversion", 17)?;
}
if dbversion < 18 {
info!(context, "[migration] v18");
sql.execute(
"ALTER TABLE acpeerstates ADD COLUMN gossip_timestamp INTEGER DEFAULT 0;",
params![],
)?;
sql.execute("ALTER TABLE acpeerstates ADD COLUMN gossip_key;", params![])?;
dbversion = 18;
sql.set_raw_config_int(context, "dbversion", 18)?;
sql.set_config_int(context, "dbversion", 18)?;
}
if dbversion < 27 {
info!(context, "[migration] v27");
sql.execute("DELETE FROM msgs WHERE chat_id=1 OR chat_id=2;", params![])?;
sql.execute(
"CREATE INDEX chats_contacts_index2 ON chats_contacts (contact_id);",
@@ -606,10 +583,9 @@ fn open(
params![],
)?;
dbversion = 27;
sql.set_raw_config_int(context, "dbversion", 27)?;
sql.set_config_int(context, "dbversion", 27)?;
}
if dbversion < 34 {
info!(context, "[migration] v34");
sql.execute(
"ALTER TABLE msgs ADD COLUMN hidden INTEGER DEFAULT 0;",
params![],
@@ -636,10 +612,9 @@ fn open(
)?;
recalc_fingerprints = 1;
dbversion = 34;
sql.set_raw_config_int(context, "dbversion", 34)?;
sql.set_config_int(context, "dbversion", 34)?;
}
if dbversion < 39 {
info!(context, "[migration] v39");
sql.execute(
"CREATE TABLE tokens ( id INTEGER PRIMARY KEY, namespc INTEGER DEFAULT 0, foreign_id INTEGER DEFAULT 0, token TEXT DEFAULT '', timestamp INTEGER DEFAULT 0);",
params![]
@@ -667,37 +642,32 @@ fn open(
)?;
}
dbversion = 39;
sql.set_raw_config_int(context, "dbversion", 39)?;
sql.set_config_int(context, "dbversion", 39)?;
}
if dbversion < 40 {
info!(context, "[migration] v40");
sql.execute(
"ALTER TABLE jobs ADD COLUMN thread INTEGER DEFAULT 0;",
params![],
)?;
dbversion = 40;
sql.set_raw_config_int(context, "dbversion", 40)?;
sql.set_config_int(context, "dbversion", 40)?;
}
if dbversion < 41 {
info!(context, "[migration] v41");
update_file_paths = 1;
dbversion = 41;
sql.set_raw_config_int(context, "dbversion", 41)?;
sql.set_config_int(context, "dbversion", 41)?;
}
if dbversion < 42 {
info!(context, "[migration] v42");
sql.execute("UPDATE msgs SET txt='' WHERE type!=10", params![])?;
dbversion = 42;
sql.set_raw_config_int(context, "dbversion", 42)?;
sql.set_config_int(context, "dbversion", 42)?;
}
if dbversion < 44 {
info!(context, "[migration] v44");
sql.execute("ALTER TABLE msgs ADD COLUMN mime_headers TEXT;", params![])?;
dbversion = 44;
sql.set_raw_config_int(context, "dbversion", 44)?;
sql.set_config_int(context, "dbversion", 44)?;
}
if dbversion < 46 {
info!(context, "[migration] v46");
sql.execute(
"ALTER TABLE msgs ADD COLUMN mime_in_reply_to TEXT;",
params![],
@@ -707,7 +677,7 @@ fn open(
params![],
)?;
dbversion = 46;
sql.set_raw_config_int(context, "dbversion", 46)?;
sql.set_config_int(context, "dbversion", 46)?;
}
if dbversion < 47 {
info!(context, "[migration] v47");
@@ -716,7 +686,7 @@ fn open(
params![],
)?;
dbversion = 47;
sql.set_raw_config_int(context, "dbversion", 47)?;
sql.set_config_int(context, "dbversion", 47)?;
}
if dbversion < 48 {
info!(context, "[migration] v48");
@@ -726,7 +696,7 @@ fn open(
)?;
dbversion = 48;
sql.set_raw_config_int(context, "dbversion", 48)?;
sql.set_config_int(context, "dbversion", 48)?;
}
if dbversion < 49 {
info!(context, "[migration] v49");
@@ -735,15 +705,15 @@ fn open(
params![],
)?;
dbversion = 49;
sql.set_raw_config_int(context, "dbversion", 49)?;
sql.set_config_int(context, "dbversion", 49)?;
}
if dbversion < 50 {
info!(context, "[migration] v50");
if 0 != exists_before_update {
sql.set_raw_config_int(context, "show_emails", 2)?;
sql.set_config_int(context, "show_emails", 2)?;
}
dbversion = 50;
sql.set_raw_config_int(context, "dbversion", 50)?;
sql.set_config_int(context, "dbversion", 50)?;
}
if dbversion < 53 {
info!(context, "[migration] v53");
@@ -776,7 +746,7 @@ fn open(
params![],
)?;
dbversion = 53;
sql.set_raw_config_int(context, "dbversion", 53)?;
sql.set_config_int(context, "dbversion", 53)?;
}
if dbversion < 54 {
info!(context, "[migration] v54");
@@ -786,20 +756,18 @@ fn open(
)?;
sql.execute("CREATE INDEX msgs_index6 ON msgs (location_id);", params![])?;
dbversion = 54;
sql.set_raw_config_int(context, "dbversion", 54)?;
sql.set_config_int(context, "dbversion", 54)?;
}
if dbversion < 55 {
info!(context, "[migration] v55");
sql.execute(
"ALTER TABLE locations ADD COLUMN independent INTEGER DEFAULT 0;",
params![],
)?;
sql.set_raw_config_int(context, "dbversion", 55)?;
sql.set_config_int(context, "dbversion", 55)?;
}
if 0 != recalc_fingerprints {
info!(context, "[migration] recalc fingerprints");
sql.query_map(
"SELECT addr FROM acpeerstates;",
params![],
@@ -809,7 +777,7 @@ fn open(
if let Some(ref mut peerstate) = Peerstate::from_addr(context, sql, &addr?)
{
peerstate.recalc_fingerprint();
peerstate.save_to_db(sql, false)?;
peerstate.save_to_db(sql, false).unwrap();
}
}
Ok(())
@@ -820,9 +788,11 @@ fn open(
// versions before 2018-08 save the absolute paths in the database files at "param.f=";
// for newer versions, we copy files always to the blob directory and store relative paths.
// this snippet converts older databases and can be removed after some time.
info!(context, "[migration] update file paths");
info!(context, "[open] update file paths");
let repl_from = sql
.get_raw_config(context, "backup_for")
.get_config(context, "backup_for")
.unwrap_or_else(|| context.get_blobdir().to_string_lossy().into());
let repl_from = dc_ensure_no_slash_safe(&repl_from);
@@ -842,7 +812,7 @@ fn open(
NO_PARAMS,
)?;
sql.set_raw_config(context, "backup_for", None)?;
sql.set_config(context, "backup_for", None)?;
}
}
@@ -1163,8 +1133,12 @@ mod test {
maybe_add_file(&mut files, "$BLOBDIR/world.txt");
maybe_add_file(&mut files, "world2.txt");
assert!(is_file_in_use(&files, None, "hello"));
assert!(!is_file_in_use(&files, Some(".txt"), "hello"));
assert!(is_file_in_use(&files, Some("-suffix"), "world.txt-suffix"));
assert!(is_file_in_use(&mut files, None, "hello"));
assert!(!is_file_in_use(&mut files, Some(".txt"), "hello"));
assert!(is_file_in_use(
&mut files,
Some("-suffix"),
"world.txt-suffix"
));
}
}

View File

@@ -109,8 +109,6 @@ pub enum StockMessage {
MsgLocationDisabled = 65,
#[strum(props(fallback = "Location"))]
Location = 66,
#[strum(props(fallback = "Sticker"))]
Sticker = 67,
}
impl StockMessage {
@@ -118,7 +116,7 @@ impl StockMessage {
///
/// These could be used in logging calls, so no logging here.
fn fallback(&self) -> &'static str {
self.get_str("fallback").unwrap_or_default()
self.get_str("fallback").unwrap()
}
}
@@ -133,7 +131,7 @@ impl Context {
if ptr.is_null() {
Cow::Borrowed(id.fallback())
} else {
let ret = to_string_lossy(ptr);
let ret = to_string(ptr);
unsafe { libc::free(ptr as *mut libc::c_void) };
Cow::Owned(ret)
}
@@ -351,7 +349,9 @@ mod tests {
let contact_id = {
Contact::create(&t.ctx, "Alice", "alice@example.com")
.expect("Failed to create contact Alice");
Contact::create(&t.ctx, "Bob", "bob@example.com").expect("failed to create bob")
let id =
Contact::create(&t.ctx, "Bob", "bob@example.com").expect("failed to create bob");
id
};
assert_eq!(
t.ctx.stock_system_msg(

View File

@@ -21,8 +21,6 @@ if __name__ == "__main__":
sum_unsafe, sum_free, sum_gotoblocks, sum_chars = 0, 0, 0, 0
for fn, unsafe, free, gotoblocks, chars in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
if unsafe + free + gotoblocks + chars == 0:
continue
print("{0: <25} unsafe: {1: >3} free: {2: >3} ok_to_cont: {3: >3} chars: {4: >3}".format(str(fn), unsafe, free, gotoblocks, chars))
sum_unsafe += unsafe
sum_free += free

View File

@@ -1,14 +1,9 @@
use std::collections::HashSet;
use std::ffi::CString;
use std::ptr;
use crate::contact::addr_normalize;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::error::Error;
use mmime::clist::*;
// use mmime::display::*;
use mmime::mailimf::mailimf_msg_id_parse;
use mmime::mailimf::types::*;
use mmime::mailimf::types_helper::*;
use mmime::mailmime::content::*;
@@ -16,7 +11,6 @@ use mmime::mailmime::disposition::*;
use mmime::mailmime::types::*;
use mmime::mailmime::types_helper::*;
use mmime::mailmime::*;
use mmime::mmapstring::*;
use mmime::other::*;
#[macro_export]
@@ -42,30 +36,13 @@ 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() {
Some(to_string_lossy((*ct).ct_subtype))
Some(to_string((*ct).ct_subtype))
} else {
None
}
}
}
pub fn parse_message_id(message_id: &str) -> Result<String, Error> {
let mut dummy = 0;
let c_message_id = CString::new(message_id).unwrap_or_default();
let c_ptr = c_message_id.as_ptr();
let mut rfc724_mid_c = std::ptr::null_mut();
if unsafe { mailimf_msg_id_parse(c_ptr, libc::strlen(c_ptr), &mut dummy, &mut rfc724_mid_c) }
== MAIL_NO_ERROR as libc::c_int
&& !rfc724_mid_c.is_null()
{
let res = to_string_lossy(rfc724_mid_c);
unsafe { libc::free(rfc724_mid_c.cast()) };
Ok(res)
} else {
bail!("could not parse message_id: {}", message_id);
}
}
pub fn get_autocrypt_mime(
mime_undetermined: *mut Mailmime,
) -> Result<(*mut Mailmime, *mut Mailmime), Error> {
@@ -111,192 +88,6 @@ pub fn has_decryptable_data(mime_data: *mut mailmime_data) -> bool {
}
}
pub fn get_field_from(imffields: *mut mailimf_fields) -> Result<String, Error> {
let field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } {
let mb_list = unsafe { (*(*field).fld_data.fld_from).frm_mb_list };
if let Some(addr) = mailimf_find_first_addr(mb_list) {
return Ok(addr);
}
}
bail!("not From field found");
}
pub fn get_field_date(imffields: *mut mailimf_fields) -> Result<i64, Error> {
let field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
let mut message_time = 0;
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()
}
}
}
Ok(message_time)
}
fn mailimf_get_recipients_add_addr(recipients: &mut HashSet<String>, mb: *mut mailimf_mailbox) {
if !mb.is_null() {
let addr_norm = addr_normalize(as_str(unsafe { (*mb).mb_addr_spec }));
recipients.insert(addr_norm.into());
}
}
/*the result is a pointer to mime, must not be freed*/
pub fn mailimf_find_field(
header: *mut mailimf_fields,
wanted_fld_type: libc::c_int,
) -> *mut mailimf_field {
if header.is_null() {
return ptr::null_mut();
}
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 unsafe { (*field).fld_type } == wanted_fld_type {
return field;
}
}
}
ptr::null_mut()
}
/*the result is a pointer to mime, must not be freed*/
pub unsafe fn mailmime_find_mailimf_fields(mime: *mut Mailmime) -> *mut mailimf_fields {
if mime.is_null() {
return ptr::null_mut();
}
match (*mime).mm_type as _ {
MAILMIME_MULTIPLE => {
for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() {
let header = mailmime_find_mailimf_fields(cur_data as *mut _);
if !header.is_null() {
return header;
}
}
}
MAILMIME_MESSAGE => return (*mime).mm_data.mm_message.mm_fields,
_ => {}
}
ptr::null_mut()
}
pub unsafe fn mailimf_find_optional_field(
header: *mut mailimf_fields,
wanted_fld_name: *const libc::c_char,
) -> *mut mailimf_optional_field {
if header.is_null() || (*header).fld_list.is_null() {
return ptr::null_mut();
}
for cur_data in (*(*header).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: *mut mailimf_optional_field = (*field).fld_data.fld_optional_field;
if !optional_field.is_null()
&& !(*optional_field).fld_name.is_null()
&& !(*optional_field).fld_value.is_null()
&& strcasecmp((*optional_field).fld_name, wanted_fld_name) == 0i32
{
return optional_field;
}
}
}
ptr::null_mut()
}
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 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 {
13 => {
fld_to = unsafe { fld.fld_data.fld_to };
if !fld_to.is_null() {
addr_list = unsafe { (*fld_to).to_addr_list };
}
}
14 => {
fld_cc = unsafe { fld.fld_data.fld_cc };
if !fld_cc.is_null() {
addr_list = unsafe { (*fld_cc).cc_addr_list };
}
}
_ => {}
}
if !addr_list.is_null() {
for cur2 in unsafe { &(*(*addr_list).ad_list) } {
let adr = cur2 as *mut mailimf_address;
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,
);
}
}
}
}
}
}
recipients
}
pub fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result<Vec<u8>, Error> {
ensure!(!mime.is_null(), "invalid inputs");
let mime_transfer_encoding =
get_mime_transfer_encoding(mime).unwrap_or(MAILMIME_MECHANISM_BINARY as i32);
let mime_data = unsafe { (*mime).mm_data.mm_single };
decode_dt_data(mime_data, mime_transfer_encoding)
}
pub fn get_mime_transfer_encoding(mime: *mut Mailmime) -> Option<libc::c_int> {
unsafe {
let mm_mime_fields = (*mime).mm_mime_fields;
@@ -317,77 +108,48 @@ pub fn get_mime_transfer_encoding(mime: *mut Mailmime) -> Option<libc::c_int> {
pub fn decode_dt_data(
mime_data: *mut mailmime_data,
mime_transfer_encoding: libc::c_int,
) -> Result<Vec<u8>, Error> {
) -> Result<(*mut libc::c_char, libc::size_t), Error> {
// Decode data according to mime_transfer_encoding
// returns Ok with a (decoded_data,decoded_data_bytes) pointer
// where the caller must make sure to free it.
// It may return Ok(ptr::null_mut(), 0)
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
let decoded_data: *mut libc::c_char;
let mut decoded_data_bytes: libc::size_t = 0;
if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int
|| mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int
|| mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int
{
let decoded_data = unsafe { (*mime_data).dt_data.dt_text.dt_data };
let decoded_data_bytes = unsafe { (*mime_data).dt_data.dt_text.dt_length };
if decoded_data.is_null() || decoded_data_bytes == 0 {
bail!("No data to decode found");
} else {
let result = unsafe {
std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes)
};
return Ok(result.to_vec());
unsafe {
decoded_data = (*mime_data).dt_data.dt_text.dt_data as *mut _;
decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
}
ensure!(
!decoded_data.is_null() && decoded_data_bytes > 0,
"could not decode mime message"
);
} else {
let mut current_index: libc::size_t = 0;
unsafe {
let r = mailmime_part_parse(
(*mime_data).dt_data.dt_text.dt_data,
(*mime_data).dt_data.dt_text.dt_length,
&mut current_index,
mime_transfer_encoding,
&mut transfer_decoding_buffer,
&mut decoded_data_bytes,
);
if r != MAILIMF_NO_ERROR as libc::c_int
|| transfer_decoding_buffer.is_null()
|| decoded_data_bytes <= 0
{
bail!("mailmime_part_parse returned error or invalid data");
}
decoded_data = transfer_decoding_buffer;
}
}
// unsafe { display_mime_data(mime_data) };
let mut current_index = 0;
let mut transfer_decoding_buffer = ptr::null_mut();
let mut decoded_data_bytes = 0;
let r = unsafe {
mailmime_part_parse(
(*mime_data).dt_data.dt_text.dt_data,
(*mime_data).dt_data.dt_text.dt_length,
&mut current_index,
mime_transfer_encoding,
&mut transfer_decoding_buffer,
&mut decoded_data_bytes,
)
};
if r == MAILIMF_NO_ERROR as libc::c_int
&& !transfer_decoding_buffer.is_null()
&& decoded_data_bytes > 0
{
let result = unsafe {
std::slice::from_raw_parts(transfer_decoding_buffer as *const u8, decoded_data_bytes)
}
.to_vec();
// we return a fresh vec and transfer_decoding_buffer is not used or passed anywhere
// so it's safe to free it right away, as mailman_part_parse has
// allocated it fresh.
unsafe { mmap_string_unref(transfer_decoding_buffer) };
return Ok(result);
}
Err(format_err!("Failed to to decode"))
}
pub fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> Option<String> {
if mb_list.is_null() {
return None;
}
for cur in unsafe { (*(*mb_list).mb_list).into_iter() } {
let mb = cur as *mut mailimf_mailbox;
if !mb.is_null() && !unsafe { (*mb).mb_addr_spec.is_null() } {
let addr = unsafe { as_str((*mb).mb_addr_spec) };
return Some(addr_normalize(addr).to_string());
}
}
None
Ok((decoded_data, decoded_data_bytes))
}
/**************************************
@@ -418,11 +180,7 @@ pub fn add_filename_part(
pub fn new_custom_field(fields: *mut mailimf_fields, name: &str, value: &str) {
unsafe {
let field = mailimf_field_new_custom(name.strdup(), value.strdup());
let res = mailimf_fields_add(fields, field);
assert!(
res as u32 == MAILIMF_NO_ERROR,
"could not create mailimf field"
);
mailimf_fields_add(fields, field);
}
}
@@ -448,8 +206,8 @@ pub fn append_ct_param(
value: &str,
) -> Result<(), Error> {
unsafe {
let name_c = CString::new(name).unwrap_or_default();
let value_c = CString::new(value).unwrap_or_default();
let name_c = CString::new(name).unwrap();
let value_c = CString::new(value).unwrap();
clist_append!(
(*content).ct_parameters,
@@ -463,7 +221,7 @@ pub fn append_ct_param(
}
pub fn new_content_type(content_type: &str) -> Result<*mut mailmime_content, Error> {
let ct = CString::new(content_type).unwrap_or_default();
let ct = CString::new(content_type).unwrap();
let content: *mut mailmime_content;
// mailmime_content_new_with_str only parses but does not retain/own ct
unsafe {
@@ -499,24 +257,6 @@ pub fn content_type_needs_encoding(content: *const mailmime_content) -> bool {
}
}
pub fn new_mailbox_list(displayname: &str, addr: &str) -> *mut mailimf_mailbox_list {
let mbox: *mut mailimf_mailbox_list = unsafe { mailimf_mailbox_list_new_empty() };
unsafe {
mailimf_mailbox_list_add(
mbox,
mailimf_mailbox_new(
if !displayname.is_empty() {
dc_encode_header_words(&displayname).strdup()
} else {
ptr::null_mut()
},
addr.strdup(),
),
);
}
mbox
}
#[cfg(test)]
mod tests {
use super::*;
@@ -536,16 +276,4 @@ mod tests {
new_content_type("application/pgp-encrypted").unwrap()
));
}
#[test]
fn test_parse_message_id() {
assert_eq!(
parse_message_id("Mr.PRUe8HJBoaO.3whNvLCMFU0@testrun.org").unwrap(),
"Mr.PRUe8HJBoaO.3whNvLCMFU0@testrun.org"
);
assert_eq!(
parse_message_id("<Mr.PRUe8HJBoaO.3whNvLCMFU0@testrun.org>").unwrap(),
"Mr.PRUe8HJBoaO.3whNvLCMFU0@testrun.org"
);
}
}