diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 40d524d1f..0a05332e7 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -21,7 +21,7 @@ use deltachat::peerstate::*; use deltachat::qr::*; use deltachat::sql; use deltachat::x::*; -use num_traits::FromPrimitive; +use deltachat::Event; /// Reset database tables. This function is called from Core cmdline. /// Argument is a bitmask, executing single or multiple actions in one call. @@ -86,7 +86,10 @@ pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 { info!(context, "(8) Rest but server config reset."); } - context.call_cb(Event::MSGS_CHANGED, 0, 0); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); 1 } @@ -198,11 +201,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int as_str(real_spec) ); if read_cnt > 0 { - context.call_cb( - Event::MSGS_CHANGED, - 0 as libc::uintptr_t, - 0 as libc::uintptr_t, - ); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); } success = 1 } @@ -1016,16 +1018,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E res.get_text2() ); } - "event" => { - ensure!(!arg1.is_empty(), "Argument missing."); - let event = arg1.parse()?; - let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?; - let r = context.call_cb(event, 0 as libc::uintptr_t, 0 as libc::uintptr_t); - println!( - "Sending event {:?}({}), received value {}.", - event, event as usize, r as libc::c_int, - ); - } + // TODO: implement this again, unclear how to match this through though, without writing a parser. + // "event" => { + // ensure!(!arg1.is_empty(), "Argument missing."); + // let event = arg1.parse()?; + // let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?; + // let r = context.call_cb(event, 0 as libc::uintptr_t, 0 as libc::uintptr_t); + // println!( + // "Sending event {:?}({}), received value {}.", + // event, event as usize, r as libc::c_int, + // ); + // } "fileinfo" => { ensure!(!arg1.is_empty(), "Argument missing."); diff --git a/examples/repl/main.rs b/examples/repl/main.rs index fb8505eea..3bc7f8014 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -22,13 +22,13 @@ use std::sync::{Arc, Mutex, RwLock}; use deltachat::config; use deltachat::configure::*; -use deltachat::constants::*; use deltachat::context::*; use deltachat::dc_tools::*; use deltachat::job::*; use deltachat::oauth2::*; use deltachat::securejoin::*; use deltachat::x::*; +use deltachat::Event; use rustyline::completion::{Completer, FilenameCompleter, Pair}; use rustyline::config::OutputStreamType; use rustyline::error::ReadlineError; @@ -43,96 +43,78 @@ use self::cmdline::*; // Event Handler -fn receive_event( - _context: &Context, - event: Event, - data1: libc::uintptr_t, - data2: libc::uintptr_t, -) -> libc::uintptr_t { +fn receive_event(_context: &Context, event: Event) -> libc::uintptr_t { match event { - Event::GET_STRING => {} - Event::INFO => { + Event::GetString { .. } => {} + Event::Info(msg) => { /* do not show the event as this would fill the screen */ - println!("{}", to_string(data2 as *const _),); + println!("{}", msg); } - Event::SMTP_CONNECTED => { - println!("[DC_EVENT_SMTP_CONNECTED] {}", to_string(data2 as *const _)); + Event::SmtpConnected(msg) => { + println!("[DC_EVENT_SMTP_CONNECTED] {}", msg); } - Event::IMAP_CONNECTED => { - println!("[DC_EVENT_IMAP_CONNECTED] {}", to_string(data2 as *const _),); + Event::ImapConnected(msg) => { + println!("[DC_EVENT_IMAP_CONNECTED] {}", msg); } - Event::SMTP_MESSAGE_SENT => { - println!( - "[DC_EVENT_SMTP_MESSAGE_SENT] {}", - to_string(data2 as *const _), - ); + Event::SmtpMessageSent(msg) => { + println!("[DC_EVENT_SMTP_MESSAGE_SENT] {}", msg); } - Event::WARNING => { - println!("[Warning] {}", to_string(data2 as *const _),); + Event::Warning(msg) => { + println!("[Warning] {}", msg); } - Event::ERROR => { - println!( - "\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m", - to_string(data2 as *const _), - ); + Event::Error(msg) => { + println!("\x1b[31m[DC_EVENT_ERROR] {}\x1b[0m", msg); } - Event::ERROR_NETWORK => { + Event::ErrorNetwork(c, msg) => { println!( "\x1b[31m[DC_EVENT_ERROR_NETWORK] first={}, msg={}\x1b[0m", - data1 as usize, - to_string(data2 as *const _), + c, msg ); } - Event::ERROR_SELF_NOT_IN_GROUP => { - println!( - "\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m", - to_string(data2 as *const _), - ); + Event::ErrorSelfNotInGroup(msg) => { + println!("\x1b[31m[DC_EVENT_ERROR_SELF_NOT_IN_GROUP] {}\x1b[0m", msg); } - Event::MSGS_CHANGED => { + Event::MsgsChanged { chat_id, msg_id } => { print!( - "\x1b[33m{{Received DC_EVENT_MSGS_CHANGED({}, {})}}\n\x1b[0m", - data1 as usize, data2 as usize, + "\x1b[33m{{Received DC_EVENT_MSGS_CHANGED(chat_id={}, msg_id={})}}\n\x1b[0m", + chat_id, msg_id, ); } - Event::CONTACTS_CHANGED => { + Event::ContactsChanged(_) => { print!("\x1b[33m{{Received DC_EVENT_CONTACTS_CHANGED()}}\n\x1b[0m"); } - Event::LOCATION_CHANGED => { + Event::LocationChanged(contact) => { print!( - "\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={})}}\n\x1b[0m", - data1 as usize, + "\x1b[33m{{Received DC_EVENT_LOCATION_CHANGED(contact={:?})}}\n\x1b[0m", + contact, ); } - Event::CONFIGURE_PROGRESS => { + Event::ConfigureProgress(progress) => { print!( "\x1b[33m{{Received DC_EVENT_CONFIGURE_PROGRESS({} ‰)}}\n\x1b[0m", - data1 as usize, + progress, ); } - Event::IMEX_PROGRESS => { + Event::ImexProgress(progress) => { print!( "\x1b[33m{{Received DC_EVENT_IMEX_PROGRESS({} ‰)}}\n\x1b[0m", - data1 as usize, + progress, ); } - Event::IMEX_FILE_WRITTEN => { + Event::ImexFileWritten(file) => { print!( "\x1b[33m{{Received DC_EVENT_IMEX_FILE_WRITTEN({})}}\n\x1b[0m", - to_string(data1 as *const _) + file.display() ); } - Event::CHAT_MODIFIED => { + Event::ChatModified(chat) => { print!( "\x1b[33m{{Received DC_EVENT_CHAT_MODIFIED({})}}\n\x1b[0m", - data1 as usize, + chat ); } _ => { - print!( - "\x1b[33m{{Received {:?}({}, {})}}\n\x1b[0m", - event, data1 as usize, data2 as usize, - ); + print!("\x1b[33m{{Received {:?}}}\n\x1b[0m", event); } } diff --git a/examples/simple.rs b/examples/simple.rs index 677348379..3236c7618 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,6 +1,5 @@ extern crate deltachat; -use std::ffi::CStr; use std::sync::{Arc, RwLock}; use std::{thread, time}; use tempfile::tempdir; @@ -9,31 +8,32 @@ use deltachat::chat; use deltachat::chatlist::*; use deltachat::config; use deltachat::configure::*; -use deltachat::constants::Event; use deltachat::contact::*; use deltachat::context::*; use deltachat::job::{ perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs, }; +use deltachat::Event; -fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize { - println!("[{:?}]", event); +fn cb(_ctx: &Context, event: Event) -> usize { + print!("[{:?}]", event); match event { - Event::CONFIGURE_PROGRESS => { - println!(" progress: {}", data1); + Event::ConfigureProgress(progress) => { + print!(" progress: {}\n", progress); 0 } - Event::INFO | Event::WARNING | Event::ERROR | Event::ERROR_NETWORK => { - println!( - " {}", - unsafe { CStr::from_ptr(data2 as *const _) } - .to_str() - .unwrap() - ); + Event::Info(msg) + | Event::Warning(msg) + | Event::Error(msg) + | Event::ErrorNetwork(_, msg) => { + print!(" {}\n", msg); + 0 + } + _ => { + print!("\n"); 0 } - _ => 0, } } diff --git a/src/chat.rs b/src/chat.rs index 864dcf220..ff157df56 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -2,8 +2,6 @@ use std::ffi::CString; use std::path::{Path, PathBuf}; use std::ptr; -use libc::uintptr_t; - use crate::chatlist::*; use crate::config::*; use crate::constants::*; @@ -11,6 +9,7 @@ use crate::contact::*; use crate::context::Context; use crate::dc_tools::*; use crate::error::Error; +use crate::events::Event; use crate::job::*; use crate::message::*; use crate::param::*; @@ -259,11 +258,9 @@ impl Chat { if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup) && 0 == is_contact_in_chat(context, self.id, 1 as u32) { - log_event!( + emit_event!( context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot send message; self not in group.", + Event::ErrorSelfNotInGroup("Cannot send message; self not in group.".into()) ); return Ok(0); } @@ -502,7 +499,10 @@ pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result { } if send_event { - context.call_cb(Event::MSGS_CHANGED, 0, 0); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); } ensure!(chat_id > 0, "failed to load create chat"); @@ -542,7 +542,10 @@ pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result( msg.state = MessageState::OutPreparing; let msg_id = prepare_msg_common(context, chat_id, msg)?; - context.call_cb( - Event::MSGS_CHANGED, - msg.chat_id as uintptr_t, - msg.id as uintptr_t, - ); + context.call_cb(Event::MsgsChanged { + chat_id: msg.chat_id, + msg_id: msg.id, + }); Ok(msg_id) } @@ -790,14 +792,13 @@ pub fn send_msg(context: &Context, chat_id: u32, msg: &mut Message) -> Result Result<(), Error> { params![chat_id as i32], )?; - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); Ok(()) } @@ -1064,7 +1068,10 @@ pub fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { params![], )?; - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { + msg_id: 0, + chat_id: 0, + }); Ok(()) } @@ -1169,7 +1176,10 @@ pub fn archive(context: &Context, chat_id: u32, archive: bool) -> Result<(), Err params![archive, chat_id as i32], )?; - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { + msg_id: 0, + chat_id: 0, + }); Ok(()) } @@ -1211,7 +1221,10 @@ pub fn delete(context: &Context, chat_id: u32) -> Result<(), Error> { params![chat_id as i32], )?; - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { + msg_id: 0, + chat_id: 0, + }); job_kill_action(context, Action::Housekeeping); job_add(context, Action::Housekeeping, 0, Params::new(), 10); @@ -1279,7 +1292,10 @@ pub unsafe fn create_group_chat( set_draft_raw(context, chat_id, Some(&mut draft_msg)); } - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { + msg_id: 0, + chat_id: 0, + }); } Ok(chat_id) @@ -1328,11 +1344,11 @@ pub fn add_contact_to_chat_ex( || !Contact::real_exists_by_id(context, contact_id) && contact_id != DC_CONTACT_ID_SELF) { if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) { - log_event!( + emit_event!( context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot add contact to group; self not in group.", + Event::ErrorSelfNotInGroup( + "Cannot add contact to group; self not in group.".into() + ) ); } else { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ @@ -1385,13 +1401,12 @@ pub fn add_contact_to_chat_ex( msg.param.set(Param::Arg, contact.get_addr()); msg.param.set_int(Param::Arg2, flags); msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default(); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - msg.id as uintptr_t, - ); + context.call_cb(Event::MsgsChanged { + chat_id, + msg_id: msg.id, + }); } - context.call_cb(Event::MSGS_CHANGED, chat_id as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { chat_id, msg_id: 0 }); success = true; } } @@ -1474,11 +1489,11 @@ pub unsafe fn remove_contact_from_chat( if let Ok(chat) = Chat::load_from_db(context, chat_id) { if real_group_exists(context, chat_id) { if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) { - log_event!( + emit_event!( context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot remove contact from chat; self not in group.", + Event::ErrorSelfNotInGroup( + "Cannot remove contact from chat; self not in group.".into() + ) ); } else { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ @@ -1504,11 +1519,10 @@ pub unsafe 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).unwrap_or_default(); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - msg.id as uintptr_t, - ); + context.call_cb(Event::MsgsChanged { + chat_id, + msg_id: msg.id, + }); } } if sql::execute( @@ -1519,7 +1533,7 @@ pub unsafe fn remove_contact_from_chat( ) .is_ok() { - context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::ChatModified(chat_id)); success = true; } } @@ -1571,11 +1585,9 @@ pub unsafe fn set_chat_name( if &chat.name == new_name.as_ref() { success = true; } else if !(is_contact_in_chat(context, chat_id, 1) == 1) { - log_event!( + emit_event!( context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot set chat name; self not in group", + Event::ErrorSelfNotInGroup("Cannot set chat name; self not in group".into()) ); } else { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ @@ -1604,17 +1616,12 @@ pub unsafe fn set_chat_name( msg.param.set(Param::Arg, &chat.name); } msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default(); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - msg.id as uintptr_t, - ); + context.call_cb(Event::MsgsChanged { + chat_id, + msg_id: msg.id, + }); } - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); + context.call_cb(Event::ChatModified(chat_id)); success = true; } } @@ -1640,11 +1647,11 @@ pub fn set_chat_profile_image( if real_group_exists(context, chat_id) { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ if !(is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF) == 1i32) { - log_event!( + emit_event!( context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot set chat profile image; self not in group.", + Event::ErrorSelfNotInGroup( + "Cannot set chat profile image; self not in group.".into() + ) ); bail!("Failed to set profile image"); } @@ -1677,9 +1684,15 @@ pub fn set_chat_profile_image( DC_CONTACT_ID_SELF, )); msg.id = send_msg(context, chat_id, &mut msg).unwrap_or_default(); - emit_event!(context, Event::MSGS_CHANGED, chat_id, msg.id); + emit_event!( + context, + Event::MsgsChanged { + chat_id, + msg_id: msg.id + } + ); } - emit_event!(context, Event::CHAT_MODIFIED, chat_id, 0); + emit_event!(context, Event::ChatModified(chat_id)); return Ok(()); } } @@ -1773,11 +1786,10 @@ pub unsafe fn forward_msgs( } for i in (0..created_db_entries.len()).step_by(2) { - context.call_cb( - Event::MSGS_CHANGED, - created_db_entries[i] as uintptr_t, - created_db_entries[i + 1] as uintptr_t, - ); + context.call_cb(Event::MsgsChanged { + chat_id: created_db_entries[i], + msg_id: created_db_entries[i + 1], + }); } } @@ -1858,11 +1870,7 @@ pub fn add_device_msg(context: &Context, chat_id: u32, text: impl AsRef) { as_str(rfc724_mid), ); unsafe { free(rfc724_mid as *mut libc::c_void) }; - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - msg_id as uintptr_t, - ); + context.call_cb(Event::MsgsChanged { chat_id, msg_id }); } #[cfg(test)] diff --git a/src/configure/mod.rs b/src/configure/mod.rs index 4e2fc15d5..eeb9aaa74 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -1,4 +1,3 @@ -use libc::uintptr_t; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use crate::constants::*; @@ -19,14 +18,10 @@ use auto_mozilla::moz_autoconfigure; macro_rules! progress { ($context:tt, $progress:expr) => { assert!( - $progress >= 0 && $progress <= 1000, + $progress > 0 && $progress <= 1000, "value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success" ); - $context.call_cb( - Event::CONFIGURE_PROGRESS, - $progress as uintptr_t, - 0 as uintptr_t, - ); + $context.call_cb($crate::events::Event::ConfigureProgress($progress)); }; } @@ -567,7 +562,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: &Job) { dc_free_ongoing(context); } - progress!(context, (if success { 1000 } else { 0 })); + progress!(context, if success { 1000 } else { 0 }); } /******************************************************************************* diff --git a/src/constants.rs b/src/constants.rs index ddb9049b6..caaf8bde5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,9 +1,8 @@ //! Constants #![allow(non_camel_case_types, dead_code)] -use lazy_static::lazy_static; - use deltachat_derive::*; +use lazy_static::lazy_static; lazy_static! { pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string(); @@ -247,242 +246,6 @@ mod tests { // If you do not want to handle an event, it is always safe to return 0, // so there is no need to add a "case" for every event. -#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] -#[repr(u32)] -pub enum Event { - /// The library-user may write an informational string to the log. - /// Passed to the callback given to dc_context_new(). - /// This event should not be reported to the end-user using a popup or something like that. - /// @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 - INFO = 100, - - /// Emitted when SMTP connection is established and login was successful. - /// - /// @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 - SMTP_CONNECTED = 101, - - /// Emitted when IMAP connection is established and login was successful. - /// - /// @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 - IMAP_CONNECTED = 102, - - /// Emitted when a message was successfully sent to the SMTP 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 - SMTP_MESSAGE_SENT = 103, - - /// The library-user should write a warning string to the log. - /// Passed to the callback given to dc_context_new(). - /// - /// This event should not be reported to the end-user using a popup or something like that. - /// - /// @param data1 0 - /// @param data2 (const char*) Warning string in english language. - /// Must not be free()'d or modified and is valid only until the callback returns. - /// @return 0 - WARNING = 300, - - /// The library-user should report an error to the end-user. - /// Passed to the callback given to dc_context_new(). - /// - /// As most things are asynchronous, things may go wrong at any time and the user - /// should not be disturbed by a dialog or so. Instead, use a bubble or so. - /// - /// However, for ongoing processes (eg. configure()) - /// or for functions that are expected to fail (eg. dc_continue_key_transfer()) - /// it might be better to delay showing these events until the function has really - /// failed (returned false). It should be sufficient to report only the _last_ error - /// in a messasge box then. - /// - /// @param data1 0 - /// @param data2 (const char*) Error string, always set, never NULL. Frequent error strings are - /// localized using #DC_EVENT_GET_STRING, however, most error strings will be in english language. - /// Must not be free()'d or modified and is valid only until the callback returns. - /// @return 0 - ERROR = 400, - - /// An action cannot be performed because there is no network available. - /// - /// The library will typically try over after a some time - /// and when dc_maybe_network() is called. - /// - /// Network errors should be reported to users in a non-disturbing way, - /// however, as network errors may come in a sequence, - /// it is not useful to raise each an every error to the user. - /// For this purpose, data1 is set to 1 if the error is probably worth reporting. - /// - /// Moreover, if the UI detects that the device is offline, - /// it is probably more useful to report this to the user - /// instead of the string from data2. - /// - /// @param data1 (int) 1=first/new network error, should be reported the user; - /// 0=subsequent network error, should be logged only - /// @param data2 (const char*) Error string, always set, never NULL. - /// Must not be free()'d or modified and is valid only until the callback returns. - /// @return 0 - ERROR_NETWORK = 401, - - /// An action cannot be performed because the user is not in the group. - /// Reported eg. after a call to - /// dc_set_chat_name(), dc_set_chat_profile_image(), - /// dc_add_contact_to_chat(), dc_remove_contact_from_chat(), - /// dc_send_text_msg() or another sending function. - /// - /// @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 - ERROR_SELF_NOT_IN_GROUP = 410, - - /// Messages or chats changed. One or more messages or chats changed for various - /// reasons in the database: - /// - Messages sent, received or removed - /// - Chats created, deleted or archived - /// - A draft has been set - /// - /// @param data1 (int) chat_id for single added messages - /// @param data2 (int) msg_id for single added messages - /// @return 0 - MSGS_CHANGED = 2000, - - /// There is a fresh message. Typically, the user will show an notification - /// when receiving this message. - /// - /// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event. - /// - /// @param data1 (int) chat_id - /// @param data2 (int) msg_id - /// @return 0 - INCOMING_MSG = 2005, - - /// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to - /// DC_STATE_OUT_DELIVERED, see dc_msg_get_state(). - /// - /// @param data1 (int) chat_id - /// @param data2 (int) msg_id - /// @return 0 - MSG_DELIVERED = 2010, - - /// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to - /// DC_STATE_OUT_FAILED, see dc_msg_get_state(). - /// - /// @param data1 (int) chat_id - /// @param data2 (int) msg_id - /// @return 0 - MSG_FAILED = 2012, - - /// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to - /// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). - /// - /// @param data1 (int) chat_id - /// @param data2 (int) msg_id - /// @return 0 - MSG_READ = 2015, - - /// Chat changed. The name or the image of a chat group was changed or members were added or removed. - /// Or the verify state of a chat has changed. - /// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat() - /// and dc_remove_contact_from_chat(). - /// - /// @param data1 (int) chat_id - /// @param data2 0 - /// @return 0 - CHAT_MODIFIED = 2020, - - /// Contact(s) created, renamed, blocked or deleted. - /// - /// @param data1 (int) If not 0, this is the contact_id of an added contact that should be selected. - /// @param data2 0 - /// @return 0 - CONTACTS_CHANGED = 2030, - - /// Location of one or more contact has changed. - /// - /// @param data1 (int) contact_id of the contact for which the location has changed. - /// If the locations of several contacts have been changed, - /// eg. after calling dc_delete_all_locations(), this parameter is set to 0. - /// @param data2 0 - /// @return 0 - LOCATION_CHANGED = 2035, - - /// Inform about the configuration progress started by configure(). - /// - /// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done - /// @param data2 0 - /// @return 0 - CONFIGURE_PROGRESS = 2041, - - /// Inform about the import/export progress started by dc_imex(). - /// - /// @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done - /// @param data2 0 - /// @return 0 - IMEX_PROGRESS = 2051, - - /// A file has been exported. A file has been written by dc_imex(). - /// This event may be sent multiple times by a single call to dc_imex(). - /// - /// A typical purpose for a handler of this event may be to make the file public to some system - /// services. - /// - /// @param data1 (const char*) Path and file name. - /// Must not be free()'d or modified and is valid only until the callback returns. - /// @param data2 0 - /// @return 0 - IMEX_FILE_WRITTEN = 2052, - - /// Progress information of a secure-join handshake from the view of the inviter - /// (Alice, the person who shows the QR code). - /// - /// These events are typically sent after a joiner has scanned the QR code - /// generated by dc_get_securejoin_qr(). - /// - /// @param data1 (int) ID of the contact that wants to join. - /// @param data2 (int) Progress as: - /// 300=vg-/vc-request received, typically shown as "bob@addr joins". - /// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified". - /// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol. - /// 1000=Protocol finished for this contact. - /// @return 0 - SECUREJOIN_INVITER_PROGRESS = 2060, - - /// Progress information of a secure-join handshake from the view of the joiner - /// (Bob, the person who scans the QR code). - /// The events are typically sent while dc_join_securejoin(), which - /// may take some time, is executed. - /// @param data1 (int) ID of the inviting contact. - /// @param data2 (int) Progress as: - /// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself." - /// (Bob has verified alice and waits until Alice does the same for him) - /// @return 0 - SECUREJOIN_JOINER_PROGRESS = 2061, - - // the following events are functions that should be provided by the frontends - /// Requeste a localized string from the frontend. - /// @param data1 (int) ID of the string to request, one of the DC_STR_/// constants. - /// @param data2 (int) The count. If the requested string contains a placeholder for a numeric value, - /// the ui may use this value to return different strings on different plural forms. - /// @return (const char*) Null-terminated UTF-8 string. - /// The string will be free()'d by the core, - /// so it must be allocated using malloc() or a compatible function. - /// Return 0 if the ui cannot provide the requested string - /// the core will use a default string in english language then. - GET_STRING = 2091, -} - const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated; const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated; const DC_ERROR_SEE_STRING: usize = 0; // deprecated; diff --git a/src/contact.rs b/src/contact.rs index bd2641e8c..5dbe8ed40 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -2,7 +2,6 @@ use std::path::PathBuf; use deltachat_derive::*; use itertools::Itertools; -use libc::uintptr_t; use rusqlite; use crate::aheader::EncryptPreference; @@ -12,6 +11,7 @@ use crate::context::Context; use crate::dc_tools::*; use crate::e2ee; use crate::error::Result; +use crate::events::Event; use crate::key::*; use crate::login_param::LoginParam; use crate::message::MessageState; @@ -214,15 +214,13 @@ impl Contact { let (contact_id, sth_modified) = Contact::add_or_lookup(context, name, addr, Origin::ManuallyCreated)?; let blocked = Contact::is_blocked_load(context, contact_id); - context.call_cb( - Event::CONTACTS_CHANGED, - (if sth_modified == Modifier::Created { - contact_id + context.call_cb(Event::ContactsChanged( + if sth_modified == Modifier::Created { + Some(contact_id) } else { - 0 - }) as uintptr_t, - 0 as uintptr_t, - ); + None + }, + )); if blocked { Contact::unblock(context, contact_id); } @@ -243,7 +241,10 @@ impl Contact { ) .is_ok() { - context.call_cb(Event::MSGS_CHANGED, 0, 0); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); } } @@ -436,7 +437,7 @@ impl Contact { } } if modify_cnt > 0 { - context.call_cb(Event::CONTACTS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::ContactsChanged(None)); } Ok(modify_cnt) @@ -673,7 +674,7 @@ impl Contact { params![contact_id as i32], ) { Ok(_) => { - context.call_cb(Event::CONTACTS_CHANGED, 0, 0); + context.call_cb(Event::ContactsChanged(None)); return Ok(()); } Err(err) => { @@ -938,11 +939,7 @@ fn set_block_contact(context: &Context, contact_id: u32, new_blocking: bool) { params![new_blocking, 100, contact_id as i32], ).is_ok() { Contact::mark_noticed(context, contact_id); - context.call_cb( - Event::CONTACTS_CHANGED, - 0, - 0, - ); + context.call_cb(Event::ContactsChanged(None)); } } } diff --git a/src/context.rs b/src/context.rs index b1938d60e..5a1eb27bc 100644 --- a/src/context.rs +++ b/src/context.rs @@ -9,6 +9,7 @@ use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::error::*; +use crate::events::Event; use crate::imap::*; use crate::job::*; use crate::job_thread::JobThread; @@ -33,7 +34,7 @@ use crate::sql::Sql; /// /// This callback must return 0 unless stated otherwise in the event /// description at [Event]. -pub type ContextCallback = dyn Fn(&Context, Event, uintptr_t, uintptr_t) -> uintptr_t + Send + Sync; +pub type ContextCallback = dyn Fn(&Context, Event) -> uintptr_t + Send + Sync; #[derive(DebugStub)] pub struct Context { @@ -133,8 +134,8 @@ impl Context { self.blobdir.as_path() } - pub fn call_cb(&self, event: Event, data1: uintptr_t, data2: uintptr_t) -> uintptr_t { - (*self.cb)(self, event, data1, data2) + pub fn call_cb(&self, event: Event) -> uintptr_t { + (*self.cb)(self, event) } pub fn get_info(&self) -> HashMap<&'static str, String> { @@ -433,7 +434,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); std::fs::write(&dbfile, b"123").unwrap(); - let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOs".into(), dbfile); + let res = Context::new(Box::new(|_, _| 0), "FakeOs".into(), dbfile); assert!(res.is_err()); } @@ -441,7 +442,7 @@ mod tests { fn test_blobdir_exists() { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); - Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap(); + Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).unwrap(); let blobdir = tmp.path().join("db.sqlite-blobs"); assert!(blobdir.is_dir()); } @@ -452,7 +453,7 @@ mod tests { let dbfile = tmp.path().join("db.sqlite"); let blobdir = tmp.path().join("db.sqlite-blobs"); std::fs::write(&blobdir, b"123").unwrap(); - let res = Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile); + let res = Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile); assert!(res.is_err()); } @@ -462,7 +463,7 @@ mod tests { let subdir = tmp.path().join("subdir"); let dbfile = subdir.join("db.sqlite"); let dbfile2 = dbfile.clone(); - Context::new(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile).unwrap(); + Context::new(Box::new(|_, _| 0), "FakeOS".into(), dbfile).unwrap(); assert!(subdir.is_dir()); assert!(dbfile2.is_file()); } @@ -472,7 +473,7 @@ mod tests { let tmp = tempfile::tempdir().unwrap(); let dbfile = tmp.path().join("db.sqlite"); let blobdir = tmp.path().join("blobs"); - let res = Context::with_blobdir(Box::new(|_, _, _, _| 0), "FakeOS".into(), dbfile, blobdir); + let res = Context::with_blobdir(Box::new(|_, _| 0), "FakeOS".into(), dbfile, blobdir); assert!(res.is_err()); } diff --git a/src/dc_imex.rs b/src/dc_imex.rs index f53e89382..a2dc5df0e 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -2,7 +2,6 @@ use std::ffi::CString; use std::path::Path; use std::ptr; -use libc::uintptr_t; use mmime::mailmime_content::*; use mmime::mmapstring::*; use mmime::other::*; @@ -16,6 +15,7 @@ use crate::context::Context; use crate::dc_tools::*; use crate::e2ee; use crate::error::*; +use crate::events::Event; use crate::job::*; use crate::key::*; use crate::message::*; @@ -502,7 +502,8 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) { error!(context, "No Import/export dir/file given.",); } else { info!(context, "Import/export process started.",); - context.call_cb(Event::IMEX_PROGRESS, 10 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::ImexProgress(10)); + if !context.sql.is_open() { error!(context, "Import/export: Database not opened.",); } else { @@ -551,11 +552,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) { } dc_free_ongoing(context); } - context.call_cb( - Event::IMEX_PROGRESS, - (if 0 != success { 1000 } else { 0 }) as uintptr_t, - 0 as uintptr_t, - ); + context.call_cb(Event::ImexProgress(if 0 != success { 1000 } else { 0 })); } /******************************************************************************* @@ -637,7 +634,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char if permille > 990 { permille = 990 } - context.call_cb(Event::IMEX_PROGRESS, permille as uintptr_t, 0); + context.call_cb(Event::ImexProgress(permille)); if file_blob.is_empty() { continue; } @@ -773,11 +770,7 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> bool { if permille > 990 { permille = 990; } - context.call_cb( - Event::IMEX_PROGRESS, - permille as uintptr_t, - 0 as uintptr_t, - ); + context.call_cb(Event::ImexProgress(permille)); let name_f = entry.file_name(); let name = name_f.to_string_lossy(); @@ -828,11 +821,9 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> bool { .set_config_int(context, "backup_time", now as i32) .is_ok() { - context.call_cb( - Event::IMEX_FILE_WRITTEN, - dest_pathNfilename as uintptr_t, - 0, - ); + context.call_cb(Event::ImexFileWritten( + as_path(dest_pathNfilename).to_path_buf(), + )); success = true; } } @@ -1063,11 +1054,7 @@ unsafe fn export_key_to_asc_file( if !key.write_asc_to_file(as_path(file_name), context) { error!(context, "Cannot write key to {}", as_str(file_name),); } else { - context.call_cb( - Event::IMEX_FILE_WRITTEN, - file_name as uintptr_t, - 0i32 as uintptr_t, - ); + context.call_cb(Event::ImexFileWritten(as_path(file_name).to_path_buf())); success = true; } free(file_name as *mut libc::c_void); @@ -1079,8 +1066,6 @@ unsafe fn export_key_to_asc_file( mod tests { use super::*; - use num_traits::ToPrimitive; - use crate::test_utils::*; #[test] @@ -1103,11 +1088,13 @@ mod tests { assert!(msg.contains("-----END PGP MESSAGE-----\n")); } - fn ac_setup_msg_cb(ctx: &Context, evt: Event, d1: uintptr_t, d2: uintptr_t) -> uintptr_t { - if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() { - unsafe { "hello\r\nthere".strdup() as usize } - } else { - logging_cb(ctx, evt, d1, d2) + fn ac_setup_msg_cb(ctx: &Context, evt: Event) -> libc::uintptr_t { + match evt { + Event::GetString { + id: StockMessage::AcSetupMsgBody, + .. + } => unsafe { "hello\r\nthere".strdup() as usize }, + _ => logging_cb(ctx, evt), } } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 523ef04cc..deb861de5 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -2,7 +2,6 @@ use std::ffi::CString; use std::ptr; use itertools::join; -use libc::uintptr_t; use mmime::clist::*; use mmime::mailimf::*; use mmime::mailimf_types::*; @@ -21,6 +20,7 @@ use crate::dc_mimeparser::*; use crate::dc_strencode::*; use crate::dc_tools::*; use crate::error::Result; +use crate::events::Event; use crate::job::*; use crate::location; use crate::message::*; @@ -31,6 +31,12 @@ use crate::sql; use crate::stock::StockMessage; use crate::x::*; +#[derive(Debug, PartialEq, Eq)] +enum CreateEvent { + MsgsChanged, + IncomingMsg, +} + /// Receive a message and add it to the database. pub unsafe fn dc_receive_imf( context: &Context, @@ -82,7 +88,7 @@ pub unsafe fn dc_receive_imf( let rfc724_mid = std::ptr::null_mut(); let mut sent_timestamp = 0; let mut created_db_entries = Vec::new(); - let mut create_event_to_send = Some(Event::MSGS_CHANGED); + let mut create_event_to_send = Some(CreateEvent::MsgsChanged); let mut rr_event_to_send = Vec::new(); let mut to_ids = Vec::with_capacity(16); @@ -90,18 +96,31 @@ pub unsafe fn dc_receive_imf( // helper method to handle early exit and memory cleanup let cleanup = |context: &Context, rfc724_mid: *mut libc::c_char, - create_event_to_send: &Option, + create_event_to_send: &Option, created_db_entries: &Vec<(usize, usize)>, rr_event_to_send: &Vec<(u32, u32)>| { free(rfc724_mid.cast()); if let Some(create_event_to_send) = create_event_to_send { for (msg_id, insert_id) in created_db_entries { - context.call_cb(*create_event_to_send, *msg_id, *insert_id); + let event = match create_event_to_send { + CreateEvent::MsgsChanged => Event::MsgsChanged { + msg_id: *msg_id as u32, + chat_id: *insert_id as u32, + }, + CreateEvent::IncomingMsg => Event::IncomingMsg { + msg_id: *msg_id as u32, + chat_id: *insert_id as u32, + }, + }; + context.call_cb(event); } } for (chat_id, msg_id) in rr_event_to_send { - context.call_cb(Event::MSG_READ, *chat_id as uintptr_t, *msg_id as uintptr_t); + context.call_cb(Event::MsgRead { + chat_id: *chat_id, + msg_id: *msg_id, + }); } }; @@ -279,7 +298,7 @@ unsafe fn add_parts( to_self: i32, insert_msg_id: &mut u32, created_db_entries: &mut Vec<(usize, usize)>, - create_event_to_send: &mut Option, + create_event_to_send: &mut Option, ) -> Result<()> { let mut state: MessageState; let mut msgrmsg: libc::c_int; @@ -730,9 +749,9 @@ unsafe fn add_parts( if 0 != from_id_blocked { *create_event_to_send = None; } else if Blocked::Not != chat_id_blocked { - *create_event_to_send = Some(Event::MSGS_CHANGED); + *create_event_to_send = Some(CreateEvent::MsgsChanged); } else { - *create_event_to_send = Some(Event::INCOMING_MSG); + *create_event_to_send = Some(CreateEvent::IncomingMsg); } } @@ -967,11 +986,7 @@ fn save_locations( } } if send_event { - context.call_cb( - Event::LOCATION_CHANGED, - from_id as uintptr_t, - 0 as uintptr_t, - ); + context.call_cb(Event::LocationChanged(Some(from_id))); } } @@ -1321,7 +1336,7 @@ unsafe fn create_or_lookup_group( ) .is_ok() { - context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0); + context.call_cb(Event::ChatModified(chat_id)); } } if !X_MrGrpImageChanged.is_empty() { @@ -1403,7 +1418,7 @@ unsafe fn create_or_lookup_group( } if 0 != send_EVENT_CHAT_MODIFIED { - context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::ChatModified(chat_id)); } // check the number of receivers - @@ -1578,7 +1593,7 @@ unsafe fn create_or_lookup_adhoc_group( chat::add_to_chat_contacts_table(context, chat_id, member_id); } - context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::ChatModified(chat_id)); cleanup( grpname, diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 000000000..ab7920dd2 --- /dev/null +++ b/src/events.rs @@ -0,0 +1,232 @@ +use std::path::PathBuf; + +use strum::EnumProperty; + +use crate::stock::StockMessage; + +impl Event { + /// Returns the corresponding Event id. + pub fn as_id(&self) -> i32 { + self.get_str("id") + .expect("missing id") + .parse() + .expect("invalid id") + } +} + +#[derive(Debug, Clone, PartialEq, Eq, EnumProperty)] +pub enum Event { + /// The library-user may write an informational string to the log. + /// Passed to the callback given to dc_context_new(). + /// This event should not be reported to the end-user using a popup or something like that. + /// + /// @return 0 + #[strum(props(id = "100"))] + Info(String), + + /// Emitted when SMTP connection is established and login was successful. + /// + /// @return 0 + #[strum(props(id = "101"))] + SmtpConnected(String), + + /// Emitted when IMAP connection is established and login was successful. + /// + /// @return 0 + #[strum(props(id = "102"))] + ImapConnected(String), + + /// Emitted when a message was successfully sent to the SMTP server. + /// + /// @return 0 + #[strum(props(id = "103"))] + SmtpMessageSent(String), + + /// The library-user should write a warning string to the log. + /// Passed to the callback given to dc_context_new(). + /// + /// This event should not be reported to the end-user using a popup or something like that. + /// + /// @return 0 + #[strum(props(id = "300"))] + Warning(String), + + /// The library-user should report an error to the end-user. + /// Passed to the callback given to dc_context_new(). + /// + /// As most things are asynchronous, things may go wrong at any time and the user + /// should not be disturbed by a dialog or so. Instead, use a bubble or so. + /// + /// However, for ongoing processes (eg. configure()) + /// or for functions that are expected to fail (eg. dc_continue_key_transfer()) + /// it might be better to delay showing these events until the function has really + /// failed (returned false). It should be sufficient to report only the _last_ error + /// in a messasge box then. + /// + /// @return + #[strum(props(id = "400"))] + Error(String), + + /// An action cannot be performed because there is no network available. + /// + /// The library will typically try over after a some time + /// and when dc_maybe_network() is called. + /// + /// Network errors should be reported to users in a non-disturbing way, + /// however, as network errors may come in a sequence, + /// it is not useful to raise each an every error to the user. + /// For this purpose, data1 is set to 1 if the error is probably worth reporting. + /// + /// Moreover, if the UI detects that the device is offline, + /// it is probably more useful to report this to the user + /// instead of the string from data2. + /// + /// @param data1 (usize) 1=first/new network error, should be reported the user; + /// 0=subsequent network error, should be logged only + /// @param data2 (String) string + /// @return 0 + #[strum(props(id = "401"))] + ErrorNetwork(usize, String), + + /// An action cannot be performed because the user is not in the group. + /// Reported eg. after a call to + /// dc_set_chat_name(), dc_set_chat_profile_image(), + /// dc_add_contact_to_chat(), dc_remove_contact_from_chat(), + /// dc_send_text_msg() or another sending function. + /// + /// @return 0 + #[strum(props(id = "410"))] + ErrorSelfNotInGroup(String), + + /// Messages or chats changed. One or more messages or chats changed for various + /// reasons in the database: + /// - Messages sent, received or removed + /// - Chats created, deleted or archived + /// - A draft has been set + /// + /// @return 0 + #[strum(props(id = "2000"))] + MsgsChanged { chat_id: u32, msg_id: u32 }, + + /// There is a fresh message. Typically, the user will show an notification + /// when receiving this message. + /// + /// There is no extra #DC_EVENT_MSGS_CHANGED event send together with this event. + /// + /// @return 0 + #[strum(props(id = "2005"))] + IncomingMsg { chat_id: u32, msg_id: u32 }, + + /// A single message is sent successfully. State changed from DC_STATE_OUT_PENDING to + /// DC_STATE_OUT_DELIVERED, see dc_msg_get_state(). + /// + /// @return 0 + #[strum(props(id = "2010"))] + MsgDelivered { chat_id: u32, msg_id: u32 }, + + /// A single message could not be sent. State changed from DC_STATE_OUT_PENDING or DC_STATE_OUT_DELIVERED to + /// DC_STATE_OUT_FAILED, see dc_msg_get_state(). + /// + /// @return 0 + #[strum(props(id = "2012"))] + MsgFailed { chat_id: u32, msg_id: u32 }, + + /// A single message is read by the receiver. State changed from DC_STATE_OUT_DELIVERED to + /// DC_STATE_OUT_MDN_RCVD, see dc_msg_get_state(). + /// + /// @return 0 + #[strum(props(id = "2015"))] + MsgRead { chat_id: u32, msg_id: u32 }, + + /// Chat changed. The name or the image of a chat group was changed or members were added or removed. + /// Or the verify state of a chat has changed. + /// See dc_set_chat_name(), dc_set_chat_profile_image(), dc_add_contact_to_chat() + /// and dc_remove_contact_from_chat(). + /// + /// @return 0 + #[strum(props(id = "2020"))] + ChatModified(u32), + + /// Contact(s) created, renamed, blocked or deleted. + /// + /// @param data1 (int) If set, this is the contact_id of an added contact that should be selected. + /// @return 0 + #[strum(props(id = "2030"))] + ContactsChanged(Option), + + /// Location of one or more contact has changed. + /// + /// @param data1 (u32) contact_id of the contact for which the location has changed. + /// If the locations of several contacts have been changed, + /// eg. after calling dc_delete_all_locations(), this parameter is set to `None`. + /// @return 0 + #[strum(props(id = "2035"))] + LocationChanged(Option), + + /// Inform about the configuration progress started by configure(). + /// + /// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done + /// @return 0 + #[strum(props(id = "2041"))] + ConfigureProgress(usize), + + /// Inform about the import/export progress started by dc_imex(). + /// + /// @param data1 (usize) 0=error, 1-999=progress in permille, 1000=success and done + /// @param data2 0 + /// @return 0 + #[strum(props(id = "2051"))] + ImexProgress(usize), + + /// A file has been exported. A file has been written by dc_imex(). + /// This event may be sent multiple times by a single call to dc_imex(). + /// + /// A typical purpose for a handler of this event may be to make the file public to some system + /// services. + /// + /// @param data2 0 + /// @return 0 + #[strum(props(id = "2052"))] + ImexFileWritten(PathBuf), + + /// Progress information of a secure-join handshake from the view of the inviter + /// (Alice, the person who shows the QR code). + /// + /// These events are typically sent after a joiner has scanned the QR code + /// generated by dc_get_securejoin_qr(). + /// + /// @param data1 (int) ID of the contact that wants to join. + /// @param data2 (int) Progress as: + /// 300=vg-/vc-request received, typically shown as "bob@addr joins". + /// 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified". + /// 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol. + /// 1000=Protocol finished for this contact. + /// @return 0 + #[strum(props(id = "2060"))] + SecurejoinInviterProgress { contact_id: u32, progress: usize }, + + /// Progress information of a secure-join handshake from the view of the joiner + /// (Bob, the person who scans the QR code). + /// The events are typically sent while dc_join_securejoin(), which + /// may take some time, is executed. + /// @param data1 (int) ID of the inviting contact. + /// @param data2 (int) Progress as: + /// 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself." + /// (Bob has verified alice and waits until Alice does the same for him) + /// @return 0 + #[strum(props(id = "2061"))] + SecurejoinJoinerProgress { contact_id: u32, progress: usize }, + + // the following events are functions that should be provided by the frontends + /// Requeste a localized string from the frontend. + /// @param data1 (int) ID of the string to request, one of the DC_STR_/// constants. + /// @param data2 (int) The count. If the requested string contains a placeholder for a numeric value, + /// the ui may use this value to return different strings on different plural forms. + /// @return (const char*) Null-terminated UTF-8 string. + /// The string will be free()'d by the core, + /// so it must be allocated using malloc() or a compatible function. + /// Return 0 if the ui cannot provide the requested string + /// the core will use a default string in english language then. + #[strum(props(id = "2091"))] + GetString { id: StockMessage, count: usize }, +} diff --git a/src/imap.rs b/src/imap.rs index 160b0ae77..86b411766 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -12,6 +12,7 @@ use crate::context::Context; use crate::dc_receive_imf::dc_receive_imf; use crate::dc_tools::CStringExt; use crate::dc_tools::*; +use crate::events::Event; use crate::job::{job_add, Action}; use crate::login_param::LoginParam; use crate::message::{dc_rfc724_mid_exists, dc_update_msg_move_state, dc_update_server_uid}; @@ -437,14 +438,15 @@ impl Imap { let imap_server: &str = config.imap_server.as_ref(); let imap_port = config.imap_port; - log_event!( + emit_event!( context, - Event::ERROR_NETWORK, - 0, - "Could not connect to IMAP-server {}:{}. ({})", - imap_server, - imap_port, - err + Event::ErrorNetwork( + 0, + format!( + "Could not connect to IMAP-server {}:{}. ({})", + imap_server, imap_port, err + ) + ) ); return false; @@ -460,7 +462,10 @@ impl Imap { true } Err((err, _)) => { - log_event!(context, Event::ERROR_NETWORK, 0, "Cannot login ({})", err); + emit_event!( + context, + Event::ErrorNetwork(0, format!("Cannot login ({})", err)) + ); self.unsetup_handle(context); false @@ -553,13 +558,12 @@ impl Imap { let caps_list = caps .iter() .fold(String::new(), |s, c| s + &format!(" {:?}", c)); - log_event!( + emit_event!( context, - Event::IMAP_CONNECTED, - 0, - "IMAP-LOGIN as {}, capabilities: {}", - lp.mail_user, - caps_list, + Event::ImapConnected(format!( + "IMAP-LOGIN as {}, capabilities: {}", + lp.mail_user, caps_list, + )) ); (false, can_idle, has_xlist) } diff --git a/src/job.rs b/src/job.rs index 46d5d7e05..43fb07957 100644 --- a/src/job.rs +++ b/src/job.rs @@ -3,7 +3,6 @@ use std::ptr; use std::time::Duration; use deltachat_derive::{FromSql, ToSql}; -use libc::uintptr_t; use mmime::clist::*; use rand::{thread_rng, Rng}; @@ -14,6 +13,7 @@ use crate::context::Context; use crate::dc_imex::*; use crate::dc_mimefactory::*; use crate::dc_tools::*; +use crate::events::Event; use crate::imap::*; use crate::location; use crate::login_param::LoginParam; @@ -189,11 +189,10 @@ impl Job { params![self.foreign_id as i32], ) .unwrap_or_default(); - context.call_cb( - Event::MSG_DELIVERED, - chat_id as uintptr_t, - self.foreign_id as uintptr_t, - ); + context.call_cb(Event::MsgDelivered { + chat_id: chat_id as u32, + msg_id: self.foreign_id, + }); } } } else { diff --git a/src/lib.rs b/src/lib.rs index 0d578a3cb..20954f241 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,9 @@ mod log; #[macro_use] pub mod error; +pub(crate) mod events; +pub use events::*; + mod aheader; pub mod chat; pub mod chatlist; diff --git a/src/location.rs b/src/location.rs index 07f63e84f..ef67d7c17 100644 --- a/src/location.rs +++ b/src/location.rs @@ -1,14 +1,13 @@ use bitflags::bitflags; -use libc::uintptr_t; use quick_xml; use quick_xml::events::{BytesEnd, BytesStart, BytesText}; use crate::chat; -use crate::constants::Event; use crate::constants::*; use crate::context::*; use crate::dc_tools::*; use crate::error::Error; +use crate::events::Event; use crate::job::*; use crate::message::*; use crate::param::*; @@ -225,11 +224,7 @@ pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) { context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); chat::add_device_msg(context, chat_id, stock_str); } - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as uintptr_t, - 0i32 as uintptr_t, - ); + context.call_cb(Event::ChatModified(chat_id)); if 0 != seconds { schedule_MAYBE_SEND_LOCATIONS(context, 0i32); job_add( @@ -292,7 +287,7 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> l } } if continue_streaming { - context.call_cb(Event::LOCATION_CHANGED, 1, 0); + context.call_cb(Event::LocationChanged(Some(1))); }; schedule_MAYBE_SEND_LOCATIONS(context, 0); } @@ -369,7 +364,7 @@ fn is_marker(txt: &str) -> bool { pub fn delete_all(context: &Context) -> Result<(), Error> { sql::execute(context, &context.sql, "DELETE FROM locations;", params![])?; - context.call_cb(Event::LOCATION_CHANGED, 0, 0); + context.call_cb(Event::LocationChanged(None)); Ok(()) } @@ -653,11 +648,7 @@ pub fn job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) { ).is_ok() { let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); chat::add_device_msg(context, chat_id, stock_str); - context.call_cb( - Event::CHAT_MODIFIED, - chat_id as usize, - 0, - ); + context.call_cb(Event::ChatModified(chat_id)); } } } diff --git a/src/log.rs b/src/log.rs index e542172b5..b9c1275d3 100644 --- a/src/log.rs +++ b/src/log.rs @@ -4,7 +4,8 @@ macro_rules! info { info!($ctx, $msg,) }; ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => { - log_event!($ctx, $crate::constants::Event::INFO, 0, $msg, $($args),*); + let formatted = format!($msg, $($args),*); + emit_event!($ctx, $crate::Event::Info(formatted)); }; } @@ -14,7 +15,8 @@ macro_rules! warn { warn!($ctx, $msg,) }; ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => { - log_event!($ctx, $crate::constants::Event::WARNING, 0, $msg, $($args),*); + let formatted = format!($msg, $($args),*); + emit_event!($ctx, $crate::Event::Warning(formatted)); }; } @@ -24,26 +26,14 @@ macro_rules! error { error!($ctx, $msg,) }; ($ctx:expr, $msg:expr, $($args:expr),* $(,)?) => { - log_event!($ctx, $crate::constants::Event::ERROR, 0, $msg, $($args),*); - }; -} - -#[macro_export] -macro_rules! log_event { - ($ctx:expr, $data1:expr, $msg:expr) => { - log_event!($ctx, $data1, $msg,) - }; - ($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => { let formatted = format!($msg, $($args),*); - let formatted_c = std::ffi::CString::new(formatted).unwrap(); - $ctx.call_cb($event, $data1 as libc::uintptr_t, - formatted_c.as_ptr() as libc::uintptr_t); + emit_event!($ctx, $crate::Event::Error(formatted)); }; } #[macro_export] macro_rules! emit_event { - ($ctx:expr, $event:expr, $data1:expr, $data2:expr) => { - $ctx.call_cb($event, $data1 as libc::uintptr_t, $data2 as libc::uintptr_t); + ($ctx:expr, $event:expr) => { + $ctx.call_cb($event); }; } diff --git a/src/message.rs b/src/message.rs index b8bb8a382..021bdbf3e 100644 --- a/src/message.rs +++ b/src/message.rs @@ -3,7 +3,6 @@ use std::path::{Path, PathBuf}; use std::ptr; use deltachat_derive::{FromSql, ToSql}; -use libc::uintptr_t; use phf::phf_map; use crate::chat::{self, Chat}; @@ -12,6 +11,7 @@ use crate::contact::*; use crate::context::*; use crate::dc_tools::*; use crate::error::Error; +use crate::events::Event; use crate::job::*; use crate::lot::{Lot, LotState, Meaning}; use crate::param::*; @@ -518,7 +518,10 @@ pub unsafe fn dc_delete_msgs(context: &Context, msg_ids: *const u32, msg_cnt: li } if 0 != msg_cnt { - context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); job_kill_action(context, Action::Housekeeping); job_add(context, Action::Housekeeping, 0, Params::new(), 10); }; @@ -586,7 +589,10 @@ pub fn dc_markseen_msgs(context: &Context, msg_ids: *const u32, msg_cnt: usize) } if send_event { - context.call_cb(Event::MSGS_CHANGED, 0, 0); + context.call_cb(Event::MsgsChanged { + chat_id: 0, + msg_id: 0, + }); } true @@ -1023,11 +1029,10 @@ pub fn dc_set_msg_failed(context: &Context, msg_id: u32, error: Option { +macro_rules! joiner_progress { + ($context:tt, $contact_id:expr, $progress:expr) => { assert!( $progress >= 0 && $progress <= 1000, "value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success" ); - $context.call_cb( - $event, - $contact_id as libc::uintptr_t, - $progress as libc::uintptr_t, - ); - }; -} - -macro_rules! joiner_progress { - ($context:tt, $contact_id:expr, $progress:expr) => { - progress!( - $context, - Event::SECUREJOIN_JOINER_PROGRESS, - $contact_id, - $progress - ); + $context.call_cb($crate::events::Event::SecurejoinJoinerProgress { + contact_id: $contact_id, + progress: $progress, + }); }; } macro_rules! inviter_progress { ($context:tt, $contact_id:expr, $progress:expr) => { - progress!( - $context, - Event::SECUREJOIN_INVITER_PROGRESS, - $contact_id, - $progress + assert!( + $progress >= 0 && $progress <= 1000, + "value in range 0..1000 expected with: 0=error, 1..999=progress, 1000=success" ); + $context.call_cb($crate::events::Event::SecurejoinInviterProgress { + contact_id: $contact_id, + progress: $progress, + }); }; } @@ -523,7 +514,7 @@ pub fn handle_securejoin_handshake( Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited); info!(context, "Auth verified.",); secure_connection_established(context, contact_chat_id); - emit_event!(context, Event::CONTACTS_CHANGED, contact_id, 0); + emit_event!(context, Event::ContactsChanged(Some(contact_id))); inviter_progress!(context, contact_id, 600); if join_vg { let field_grpid = lookup_field(mimeparser, "Secure-Join-Group").unwrap_or_default(); @@ -594,7 +585,7 @@ pub fn handle_securejoin_handshake( return ret; } Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); - emit_event!(context, Event::CONTACTS_CHANGED, 0, 0); + emit_event!(context, Event::ContactsChanged(None)); let cg_member_added = lookup_field(mimeparser, "Chat-Group-Member-Added").unwrap_or_default(); if join_vg && !addr_equals_self(context, cg_member_added) { @@ -657,7 +648,7 @@ fn secure_connection_established(context: &Context, contact_chat_id: u32) { }; let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr); chat::add_device_msg(context, contact_chat_id, msg); - emit_event!(context, Event::CHAT_MODIFIED, contact_chat_id, 0); + emit_event!(context, Event::ChatModified(contact_chat_id)); } fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> Option { @@ -771,7 +762,7 @@ pub fn handle_degrade_event(context: &Context, peerstate: &Peerstate) { let msg = context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr); chat::add_device_msg(context, contact_chat_id, msg); - emit_event!(context, Event::CHAT_MODIFIED, contact_chat_id, 0); + emit_event!(context, Event::ChatModified(contact_chat_id)); } } } diff --git a/src/smtp.rs b/src/smtp.rs index 2d32396cf..ae0007be8 100644 --- a/src/smtp.rs +++ b/src/smtp.rs @@ -1,9 +1,9 @@ use lettre::smtp::client::net::*; use lettre::*; -use crate::constants::Event; use crate::constants::*; use crate::context::Context; +use crate::events::Event; use crate::login_param::LoginParam; use crate::oauth2::*; @@ -52,7 +52,7 @@ impl Smtp { } if lp.send_server.is_empty() || lp.send_port == 0 { - log_event!(context, Event::ERROR_NETWORK, 0, "SMTP bad parameters.",); + context.call_cb(Event::ErrorNetwork(0, "SMTP bad parameters.".into())); } self.from = if let Ok(addr) = EmailAddress::new(lp.addr.clone()) { @@ -111,13 +111,10 @@ impl Smtp { .credentials(creds) .connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited); self.transport = Some(client.transport()); - log_event!( - context, - Event::SMTP_CONNECTED, - 0, + context.call_cb(Event::SmtpConnected(format!( "SMTP-LOGIN as {} ok", lp.send_user, - ); + ))); true } Err(err) => { @@ -143,12 +140,9 @@ impl Smtp { match transport.send(mail) { Ok(_) => { - log_event!( - context, - Event::SMTP_MESSAGE_SENT, - 0, - "Message was sent to SMTP server", - ); + context.call_cb(Event::SmtpMessageSent( + "Message was sent to SMTP server".into(), + )); self.transport_connected = true; 1 } diff --git a/src/stock.rs b/src/stock.rs index d0f0d2800..28481d5c5 100644 --- a/src/stock.rs +++ b/src/stock.rs @@ -3,10 +3,10 @@ use std::borrow::Cow; use strum::EnumProperty; use strum_macros::EnumProperty; -use crate::constants::Event; use crate::contact::*; use crate::context::Context; use crate::dc_tools::*; +use crate::events::Event; use libc::free; /// Stock strings @@ -128,7 +128,7 @@ impl Context { /// translation, then this string will be returned. Otherwise a /// default (English) string is returned. pub fn stock_str(&self, id: StockMessage) -> Cow { - let ptr = self.call_cb(Event::GET_STRING, id as usize, 0) as *mut libc::c_char; + let ptr = self.call_cb(Event::GetString { id, count: 0 }) as *mut libc::c_char; if ptr.is_null() { Cow::Borrowed(id.fallback()) } else { @@ -259,16 +259,13 @@ mod tests { assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "No messages."); } - fn test_stock_str_no_fallback_cb( - _ctx: &Context, - evt: Event, - d1: uintptr_t, - _d2: uintptr_t, - ) -> uintptr_t { - if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() { - unsafe { "Hello there".strdup() as usize } - } else { - 0 + fn test_stock_str_no_fallback_cb(_ctx: &Context, evt: Event) -> uintptr_t { + match evt { + Event::GetString { + id: StockMessage::NoMessages, + .. + } => unsafe { "Hello there".strdup() as usize }, + _ => 0, } } diff --git a/src/test_utils.rs b/src/test_utils.rs index c6d6caaf4..b3bacaa0c 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -2,14 +2,13 @@ //! //! This module is only compiled for test runs. -use std::ffi::CStr; - use libc::uintptr_t; use tempfile::{tempdir, TempDir}; use crate::config::Config; -use crate::constants::{Event, KeyType}; +use crate::constants::KeyType; use crate::context::{Context, ContextCallback}; +use crate::events::Event; use crate::key; /// A Context and temporary directory. @@ -32,7 +31,7 @@ pub fn test_context(callback: Option>) -> TestContext { let dbfile = dir.path().join("db.sqlite"); let cb: Box = match callback { Some(cb) => cb, - None => Box::new(|_, _, _, _| 0), + None => Box::new(|_, _| 0), }; let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap(); TestContext { ctx: ctx, dir: dir } @@ -47,12 +46,11 @@ pub fn dummy_context() -> TestContext { test_context(None) } -pub fn logging_cb(_ctx: &Context, evt: Event, _d1: uintptr_t, d2: uintptr_t) -> uintptr_t { - let to_str = unsafe { |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap() }; +pub fn logging_cb(_ctx: &Context, evt: Event) -> uintptr_t { match evt { - Event::INFO => println!("I: {}", to_str(d2)), - Event::WARNING => println!("W: {}", to_str(d2)), - Event::ERROR => println!("E: {}", to_str(d2)), + Event::Info(msg) => println!("I: {}", msg), + Event::Warning(msg) => println!("W: {}", msg), + Event::Error(msg) => println!("E: {}", msg), _ => (), } 0 diff --git a/tests/stress.rs b/tests/stress.rs index 88447312e..f94e97091 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -7,7 +7,6 @@ use tempfile::{tempdir, TempDir}; use deltachat::chat::{self, Chat}; use deltachat::config; -use deltachat::constants::*; use deltachat::contact::*; use deltachat::context::*; use deltachat::dc_imex::*; @@ -16,6 +15,7 @@ use deltachat::keyring::*; use deltachat::oauth2::*; use deltachat::pgp::*; use deltachat::x::*; +use deltachat::Event; use libc; /* some data used for testing @@ -562,12 +562,7 @@ fn test_encryption_decryption() { assert_eq!(plain, original_text); } -fn cb( - _context: &Context, - _event: Event, - _data1: libc::uintptr_t, - _data2: libc::uintptr_t, -) -> libc::uintptr_t { +fn cb(_context: &Context, _event: Event) -> libc::uintptr_t { 0 }