mirror of
https://github.com/chatmail/core.git
synced 2026-07-23 03:13:06 +03:00
Compare commits
1 Commits
message_tr
...
flub-locat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e21399813 |
@@ -2951,32 +2951,6 @@ int64_t dc_msg_get_sort_timestamp (const dc_msg_t* msg);
|
||||
*/
|
||||
char* dc_msg_get_text (const dc_msg_t* msg);
|
||||
|
||||
/**
|
||||
* Get the text of the message.
|
||||
* If there is no text associated with the message, an empty string is returned.
|
||||
* NULL is never returned.
|
||||
*
|
||||
* Same as dc_msg_get_text, but isn't truncated
|
||||
*
|
||||
* To get information about the message and more/raw text, use dc_get_msg_info().
|
||||
*
|
||||
* @memberof dc_msg_t
|
||||
* @param msg The message object.
|
||||
* @return Message text. The result must be released using dc_str_unref(). Never returns NULL.
|
||||
*/
|
||||
char* dc_msg_get_full_text (const dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a message is truncated.
|
||||
*
|
||||
* @memberof dc_msg_t
|
||||
* @param msg The message object.
|
||||
* @return 1=message is truncated, 0=message not truncated.
|
||||
*/
|
||||
int dc_msg_is_truncated (const dc_msg_t* msg);
|
||||
|
||||
// define DC_MSG_TRUNCATE_THRESHOLD
|
||||
|
||||
/**
|
||||
* Find out full path, file name and extension of the file associated with a
|
||||
|
||||
@@ -2420,26 +2420,7 @@ pub unsafe extern "C" fn dc_msg_get_text(msg: *mut dc_msg_t) -> *mut libc::c_cha
|
||||
return dc_strdup(ptr::null());
|
||||
}
|
||||
let ffi_msg = &*msg;
|
||||
ffi_msg.message.get_text(false).unwrap_or_default().strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_msg_get_full_text(msg: *mut dc_msg_t) -> *mut libc::c_char {
|
||||
if msg.is_null() {
|
||||
eprintln!("ignoring careless call to dc_msg_get_full_text()");
|
||||
return dc_strdup(ptr::null());
|
||||
}
|
||||
let ffi_msg = &*msg;
|
||||
ffi_msg.message.get_text(true).unwrap_or_default().strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn is_truncated(msg: *mut dc_msg_t) -> libc::c_int {
|
||||
if msg.is_null() {
|
||||
return 0
|
||||
}
|
||||
let ffi_msg = &*msg;
|
||||
ffi_msg.message.is_truncated().into()
|
||||
ffi_msg.message.get_text().unwrap_or_default().strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@@ -189,7 +189,7 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
|
||||
_ => "",
|
||||
};
|
||||
let temp2 = dc_timestamp_to_str(msg.get_timestamp());
|
||||
let msgtext = msg.get_text(false);
|
||||
let msgtext = msg.get_text();
|
||||
info!(
|
||||
context,
|
||||
"{}#{}{}{}: {} (Contact#{}): {} {}{}{}{}{} [{}]",
|
||||
@@ -353,7 +353,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
configure\n\
|
||||
connect\n\
|
||||
disconnect\n\
|
||||
interrupt\n\
|
||||
maybenetwork\n\
|
||||
housekeeping\n\
|
||||
help imex (Import/Export)\n\
|
||||
@@ -494,9 +493,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"info" => {
|
||||
println!("{:#?}", context.get_info());
|
||||
}
|
||||
"interrupt" => {
|
||||
interrupt_imap_idle(context);
|
||||
}
|
||||
"maybenetwork" => {
|
||||
maybe_network(context);
|
||||
}
|
||||
|
||||
22
src/chat.rs
22
src/chat.rs
@@ -258,7 +258,7 @@ impl Chat {
|
||||
}
|
||||
|
||||
if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup)
|
||||
&& !is_contact_in_chat(context, self.id, DC_CONTACT_ID_SELF)
|
||||
&& !is_contact_in_chat(context, self.id, 1 as u32)
|
||||
{
|
||||
emit_event!(
|
||||
context,
|
||||
@@ -414,7 +414,7 @@ impl Chat {
|
||||
&context.sql,
|
||||
"INSERT INTO locations \
|
||||
(timestamp,from_id,chat_id, latitude,longitude,independent)\
|
||||
VALUES (?,?,?, ?,?,1);", // 1=DC_CONTACT_ID_SELF
|
||||
VALUES (?,?,?, ?,?,1);",
|
||||
params![
|
||||
timestamp,
|
||||
DC_CONTACT_ID_SELF,
|
||||
@@ -446,7 +446,7 @@ impl Chat {
|
||||
params![
|
||||
new_rfc724_mid,
|
||||
self.id as i32,
|
||||
DC_CONTACT_ID_SELF,
|
||||
1i32,
|
||||
to_id as i32,
|
||||
timestamp,
|
||||
msg.type_0,
|
||||
@@ -914,7 +914,7 @@ fn do_set_draft(context: &Context, chat_id: u32, msg: &mut Message) -> Result<()
|
||||
VALUES (?,?,?, ?,?,?,?,?);",
|
||||
params![
|
||||
chat_id as i32,
|
||||
DC_CONTACT_ID_SELF,
|
||||
1,
|
||||
time(),
|
||||
msg.type_0,
|
||||
MessageState::OutDraft,
|
||||
@@ -993,8 +993,8 @@ pub fn get_chat_msgs(
|
||||
" ON m.chat_id=chats.id",
|
||||
" LEFT JOIN contacts",
|
||||
" ON m.from_id=contacts.id",
|
||||
" WHERE m.from_id!=1", // 1=DC_CONTACT_ID_SELF
|
||||
" AND m.from_id!=2", // 2=DC_CONTACT_ID_DEVICE
|
||||
" WHERE m.from_id!=1",
|
||||
" AND m.from_id!=2",
|
||||
" AND m.hidden=0",
|
||||
" AND chats.blocked=2",
|
||||
" AND contacts.blocked=0",
|
||||
@@ -1350,7 +1350,7 @@ pub fn create_group_chat(
|
||||
let chat_id = sql::get_rowid(context, &context.sql, "chats", "grpid", grpid);
|
||||
|
||||
if chat_id != 0 {
|
||||
if add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF) {
|
||||
if add_to_chat_contacts_table(context, chat_id, 1) {
|
||||
let mut draft_msg = Message::new(Viewtype::Text);
|
||||
draft_msg.set_text(Some(draft_txt));
|
||||
set_draft_raw(context, chat_id, &mut draft_msg);
|
||||
@@ -1557,7 +1557,7 @@ pub fn remove_contact_from_chat(
|
||||
/* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */
|
||||
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, DC_CONTACT_ID_SELF) {
|
||||
if !is_contact_in_chat(context, chat_id, 1 as u32) {
|
||||
emit_event!(
|
||||
context,
|
||||
Event::ErrorSelfNotInGroup(
|
||||
@@ -1653,7 +1653,7 @@ pub fn set_chat_name(
|
||||
if real_group_exists(context, chat_id) {
|
||||
if chat.name == new_name.as_ref() {
|
||||
success = true;
|
||||
} else if !is_contact_in_chat(context, chat_id, DC_CONTACT_ID_SELF) {
|
||||
} else if !is_contact_in_chat(context, chat_id, 1) {
|
||||
emit_event!(
|
||||
context,
|
||||
Event::ErrorSelfNotInGroup("Cannot set chat name; self not in group".into())
|
||||
@@ -1905,8 +1905,8 @@ pub fn add_device_msg(context: &Context, chat_id: u32, text: impl AsRef<str>) {
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,rfc724_mid) VALUES (?,?,?, ?,?,?, ?,?);",
|
||||
params![
|
||||
chat_id as i32,
|
||||
DC_CONTACT_ID_DEVICE,
|
||||
DC_CONTACT_ID_DEVICE,
|
||||
2,
|
||||
2,
|
||||
dc_create_smeared_timestamp(context),
|
||||
Viewtype::Text,
|
||||
MessageState::InNoticed,
|
||||
|
||||
@@ -295,7 +295,7 @@ impl Chatlist {
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if let Ok(lastmsg) = Message::load_from_db(context, lastmsg_id) {
|
||||
if lastmsg.from_id != DC_CONTACT_ID_SELF
|
||||
if lastmsg.from_id != 1
|
||||
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
lastcontact = Contact::load_from_db(context, lastmsg.from_id).ok();
|
||||
|
||||
@@ -531,26 +531,21 @@ fn try_smtp_one_param(context: &Context, param: &LoginParam) -> Option<bool> {
|
||||
param.send_user, param.send_server, param.send_port, param.server_flags
|
||||
);
|
||||
info!(context, "Trying: {}", inf);
|
||||
match context
|
||||
if context
|
||||
.smtp
|
||||
.clone()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.connect(context, ¶m)
|
||||
{
|
||||
Ok(()) => {
|
||||
info!(context, "success: {}", inf);
|
||||
Some(true)
|
||||
}
|
||||
Err(err) => {
|
||||
if context.shall_stop_ongoing() {
|
||||
Some(false)
|
||||
} else {
|
||||
warn!(context, "could not connect: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
info!(context, "success: {}", inf);
|
||||
return Some(true);
|
||||
}
|
||||
if context.shall_stop_ongoing() {
|
||||
return Some(false);
|
||||
}
|
||||
info!(context, "could not connect: {}", inf);
|
||||
None
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
|
||||
@@ -249,8 +249,6 @@ impl Default for Viewtype {
|
||||
}
|
||||
}
|
||||
|
||||
pub const DC_MSG_TRUNCATE_THRESHOLD:usize = 30000;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -264,7 +264,7 @@ impl Contact {
|
||||
.unwrap_or_default();
|
||||
|
||||
if addr_normalized == addr_self {
|
||||
return DC_CONTACT_ID_SELF;
|
||||
return 1;
|
||||
}
|
||||
|
||||
context.sql.query_get_value(
|
||||
@@ -301,7 +301,7 @@ impl Contact {
|
||||
.unwrap_or_default();
|
||||
|
||||
if addr == addr_self {
|
||||
return Ok((DC_CONTACT_ID_SELF, sth_modified));
|
||||
return Ok((1, sth_modified));
|
||||
}
|
||||
|
||||
if !may_be_valid_addr(&addr) {
|
||||
|
||||
@@ -391,7 +391,7 @@ unsafe fn add_parts(
|
||||
} else {
|
||||
MessageState::InFresh
|
||||
};
|
||||
*to_id = DC_CONTACT_ID_SELF;
|
||||
*to_id = 1;
|
||||
// handshake messages must be processed _before_ chats are created
|
||||
// (eg. contacs may be marked as verified)
|
||||
if mime_parser.lookup_field("Secure-Join").is_some() {
|
||||
@@ -551,9 +551,8 @@ unsafe fn add_parts(
|
||||
if to_ids.is_empty() && 0 != to_self {
|
||||
// from_id==to_id==DC_CONTACT_ID_SELF - this is a self-sent messages,
|
||||
// maybe an Autocrypt Setup Messag
|
||||
let (id, bl) =
|
||||
chat::create_or_lookup_by_contact_id(context, DC_CONTACT_ID_SELF, Blocked::Not)
|
||||
.unwrap_or_default();
|
||||
let (id, bl) = chat::create_or_lookup_by_contact_id(context, 1, Blocked::Not)
|
||||
.unwrap_or_default();
|
||||
*chat_id = id;
|
||||
chat_id_blocked = bl;
|
||||
|
||||
@@ -1351,8 +1350,8 @@ unsafe fn create_or_lookup_adhoc_group(
|
||||
if !member_ids.contains(&from_id) {
|
||||
member_ids.push(from_id);
|
||||
}
|
||||
if !member_ids.contains(&DC_CONTACT_ID_SELF) {
|
||||
member_ids.push(DC_CONTACT_ID_SELF);
|
||||
if !member_ids.contains(&1) {
|
||||
member_ids.push(1);
|
||||
}
|
||||
if member_ids.len() < 3 {
|
||||
// too few contacts given
|
||||
@@ -1472,7 +1471,7 @@ fn create_adhoc_grp_id(context: &Context, member_ids: &[u32]) -> String {
|
||||
.sql
|
||||
.query_map(
|
||||
format!(
|
||||
"SELECT addr FROM contacts WHERE id IN({}) AND id!=1", // 1=DC_CONTACT_ID_SELF
|
||||
"SELECT addr FROM contacts WHERE id IN({}) AND id!=1",
|
||||
member_ids_str
|
||||
),
|
||||
params![],
|
||||
@@ -1526,7 +1525,7 @@ fn search_chat_ids_by_contact_ids(
|
||||
WHERE cc.chat_id IN(SELECT chat_id FROM chats_contacts WHERE contact_id IN({})) \
|
||||
AND c.type=120 \
|
||||
AND cc.contact_id!=1 \
|
||||
ORDER BY cc.chat_id, cc.contact_id;", // 1=DC_CONTACT_ID_SELF
|
||||
ORDER BY cc.chat_id, cc.contact_id;",
|
||||
contact_ids_str
|
||||
),
|
||||
params![],
|
||||
|
||||
14
src/job.rs
14
src/job.rs
@@ -135,7 +135,8 @@ impl Job {
|
||||
if !context.smtp.lock().unwrap().is_connected() {
|
||||
let loginparam = LoginParam::from_database(context, "configured_");
|
||||
let connected = context.smtp.lock().unwrap().connect(context, &loginparam);
|
||||
if connected.is_err() {
|
||||
|
||||
if !connected {
|
||||
self.try_again_later(3, None);
|
||||
return;
|
||||
}
|
||||
@@ -172,10 +173,10 @@ impl Job {
|
||||
// its ok/error response processing. Note that if a message
|
||||
// was sent we need to mark it in the database ASAP as we
|
||||
// otherwise might send it twice.
|
||||
let mut smtp = context.smtp.lock().unwrap();
|
||||
match smtp.send(context, recipients_list, body, self.job_id) {
|
||||
let mut sock = context.smtp.lock().unwrap();
|
||||
match sock.send(context, recipients_list, body) {
|
||||
Err(err) => {
|
||||
smtp.disconnect();
|
||||
sock.disconnect();
|
||||
warn!(context, "smtp failed: {}", err);
|
||||
self.try_again_later(-1, Some(err.to_string()));
|
||||
}
|
||||
@@ -504,6 +505,8 @@ pub fn perform_smtp_jobs(context: &Context) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform_smtp_fetch(_context: &Context) {}
|
||||
|
||||
pub fn perform_smtp_idle(context: &Context) {
|
||||
info!(context, "SMTP-idle started...",);
|
||||
{
|
||||
@@ -1022,9 +1025,8 @@ pub fn interrupt_smtp_idle(context: &Context) {
|
||||
}
|
||||
|
||||
pub fn interrupt_imap_idle(context: &Context) {
|
||||
info!(context, "Interrupting INBOX-IDLE...",);
|
||||
info!(context, "Interrupting IMAP-IDLE...",);
|
||||
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = true;
|
||||
|
||||
context.inbox.read().unwrap().interrupt_idle();
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ pub struct JobThread {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct JobState {
|
||||
idle: bool,
|
||||
jobs_needed: bool,
|
||||
jobs_needed: i32,
|
||||
suspended: bool,
|
||||
using_handle: bool,
|
||||
}
|
||||
@@ -58,7 +58,7 @@ impl JobThread {
|
||||
|
||||
pub fn interrupt_idle(&self, context: &Context) {
|
||||
{
|
||||
self.state.0.lock().unwrap().jobs_needed = true;
|
||||
self.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, "Interrupting {}-IDLE...", self.name);
|
||||
@@ -139,13 +139,13 @@ impl JobThread {
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if state.jobs_needed {
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
self.name,
|
||||
);
|
||||
state.jobs_needed = false;
|
||||
state.jobs_needed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,13 @@ impl Kml {
|
||||
}
|
||||
}
|
||||
|
||||
// location streaming
|
||||
/// Starts streaming locations to a chat.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `context` - The [Context].
|
||||
/// * `chat_id` - The ID of the chat to send locations to.
|
||||
/// * `seconds` - The duration for which to stream the location.
|
||||
pub fn send_locations_to_chat(context: &Context, chat_id: u32, seconds: i64) {
|
||||
let now = time();
|
||||
if !(seconds < 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL) {
|
||||
@@ -275,7 +281,7 @@ pub fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64) -> b
|
||||
accuracy,
|
||||
time(),
|
||||
chat_id,
|
||||
DC_CONTACT_ID_SELF,
|
||||
1,
|
||||
]
|
||||
) {
|
||||
warn!(context, "failed to store location {:?}", err);
|
||||
|
||||
@@ -381,23 +381,10 @@ impl Message {
|
||||
self.timestamp_sort
|
||||
}
|
||||
|
||||
pub fn is_truncated(&self) -> bool {
|
||||
if let Some(text) = &self.text {
|
||||
return text.chars().count() > DC_MSG_TRUNCATE_THRESHOLD
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_text(&self, full_message: bool) -> Option<String> {
|
||||
if full_message {
|
||||
self.text
|
||||
pub fn get_text(&self) -> Option<String> {
|
||||
self.text
|
||||
.as_ref()
|
||||
.map(|text| text.to_string())
|
||||
} else {
|
||||
self.text
|
||||
.as_ref()
|
||||
.map(|text| dc_truncate(text, DC_MSG_TRUNCATE_THRESHOLD, false).to_string())
|
||||
}
|
||||
.map(|text| dc_truncate(text, 30000, false).to_string())
|
||||
}
|
||||
|
||||
pub fn get_filename(&self) -> Option<String> {
|
||||
@@ -727,7 +714,7 @@ pub fn get_msg_info(context: &Context, msg_id: MsgId) -> String {
|
||||
ret += "\n";
|
||||
}
|
||||
|
||||
if msg.from_id == DC_CONTACT_ID_DEVICE || msg.to_id == DC_CONTACT_ID_DEVICE {
|
||||
if msg.from_id == 2 || msg.to_id == 2 {
|
||||
// device-internal message, no further details needed
|
||||
return ret;
|
||||
}
|
||||
|
||||
49
src/smtp.rs
49
src/smtp.rs
@@ -44,24 +44,27 @@ impl Smtp {
|
||||
}
|
||||
|
||||
/// Connect using the provided login params
|
||||
pub fn connect(&mut self, context: &Context, lp: &LoginParam) -> Result<(), Error> {
|
||||
pub fn connect(&mut self, context: &Context, lp: &LoginParam) -> bool {
|
||||
if self.is_connected() {
|
||||
warn!(context, "SMTP already connected.");
|
||||
return Ok(());
|
||||
return true;
|
||||
}
|
||||
|
||||
if lp.send_server.is_empty() || lp.send_port == 0 {
|
||||
context.call_cb(Event::ErrorNetwork("SMTP bad parameters.".into()));
|
||||
bail!("SMTP Bad parameters");
|
||||
}
|
||||
|
||||
self.from = match EmailAddress::new(lp.addr.clone()) {
|
||||
Ok(addr) => Some(addr),
|
||||
Err(err) => {
|
||||
bail!("invalid login address {}: {}", lp.addr, err);
|
||||
}
|
||||
self.from = if let Ok(addr) = EmailAddress::new(lp.addr.clone()) {
|
||||
Some(addr)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if self.from.is_none() {
|
||||
// TODO: print error
|
||||
return false;
|
||||
}
|
||||
|
||||
let domain = &lp.send_server;
|
||||
let port = lp.send_port as u16;
|
||||
|
||||
@@ -73,12 +76,11 @@ impl Smtp {
|
||||
let addr = &lp.addr;
|
||||
let send_pw = &lp.send_pw;
|
||||
let access_token = dc_get_oauth2_access_token(context, addr, send_pw, false);
|
||||
ensure!(
|
||||
access_token.is_some(),
|
||||
"could not get oaut2_access token addr={}",
|
||||
addr
|
||||
);
|
||||
if access_token.is_none() {
|
||||
return false;
|
||||
}
|
||||
let user = &lp.send_user;
|
||||
|
||||
(
|
||||
lettre::smtp::authentication::Credentials::new(
|
||||
user.to_string(),
|
||||
@@ -123,27 +125,27 @@ impl Smtp {
|
||||
"SMTP-LOGIN as {} ok",
|
||||
lp.send_user,
|
||||
)));
|
||||
return Ok(());
|
||||
return true;
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("SMTP: failed to connect {:?}", err);
|
||||
warn!(context, "SMTP: failed to connect {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
bail!("SMTP: failed to setup connection {:?}", err);
|
||||
warn!(context, "SMTP: failed to setup connection {:?}", err);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// SMTP-Send a prepared mail to recipients.
|
||||
/// on successful send out Ok() is returned.
|
||||
/// returns boolean whether send was successful.
|
||||
pub fn send<'a>(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
recipients: Vec<EmailAddress>,
|
||||
message: Vec<u8>,
|
||||
job_id: u32,
|
||||
) -> Result<(), Error> {
|
||||
let message_len = message.len();
|
||||
|
||||
@@ -154,15 +156,12 @@ impl Smtp {
|
||||
.join(",");
|
||||
|
||||
if let Some(ref mut transport) = self.transport {
|
||||
let envelope = match Envelope::new(self.from.clone(), recipients) {
|
||||
Ok(env) => env,
|
||||
Err(err) => {
|
||||
bail!("{}", err);
|
||||
}
|
||||
};
|
||||
let envelope = Envelope::new(self.from.clone(), recipients);
|
||||
ensure!(envelope.is_ok(), "internal smtp-message construction fail");
|
||||
let envelope = envelope.unwrap();
|
||||
let mail = SendableEmail::new(
|
||||
envelope,
|
||||
format!("{}", job_id), // only used for internal logging
|
||||
"mail-id".into(), // TODO: random id
|
||||
message,
|
||||
);
|
||||
|
||||
|
||||
1
test-data/key/private2.asc
Normal file
1
test-data/key/private2.asc
Normal file
@@ -0,0 +1 @@
|
||||
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
|
||||
1
test-data/key/public2.asc
Normal file
1
test-data/key/public2.asc
Normal file
@@ -0,0 +1 @@
|
||||
xsBNBF297/EBCADEgI6RhCnbfNnKqlPzHNAmCVJ65KniO0reqaR94CFxcMRcO3k5XZDAo0u1KbqobehYm39ggtoqtmattjgG/AbR+Ex90CjbT1PbIi4Efkt91deMzt4G/S/POtqnIt8nbPSohMRkTOBKI0y7DSGneZTx+uj/GuE+aWbM2ubENsW283onnUKqDUOtxqQoBT3vWocsS6iViZCjqhqqQPX4OVK0gaQDLFCm63NC5VnazSvJkmDFFhG2bN8ncZ3wVibai7r3scF+I+hCnR4ll+/Q02WdpCSW0V26DXUQiCadTri6jDAi3JAGCnsCUiXR9780yAms+idWjPAXv2l9PfvSYkITABEBAAHNETxib2JAZXhhbXBsZS5jb20+wsCJBBABCAAzAhkBBQJdvfAxAhsDBAsJCAcGFQgJCgsCAxYCARYhBMCyk5nNgMGX89zC8GVj9ZoUEAYDAAoJEGVj9ZoUEAYDWBoIAJhfY396iAauf0X+WBBTjEEJR9svmbxIIaGah3GSlYpAVXK44mLjKeJIdDzxFJ7nhfs+wEkSpU0NMnQsifdtZd+bjEN1hQxJq3WEoeyPEEp/KFOw312zt1ucysDao59oji4LLkZhKPjBz7v8e/DaWKty45Cv2t2/3+g5IWHChmyzbd0hhGpuQ6osR4iNV9xatLYWncJMUgow1YLgtAV2XBu/5B0bZA9oqHw9JX0oMWmBjHvU2ngsreUbQTcA10S2ExzfFjE9WeArTv7suVQLmcteBxLqjbFZ3UqpLraJmNejdRp0SE+OJAtiZKhq2PYPm6Wl+i2VLdScjkiqnsLjx9XOwE0EXb3v8QEIAMbfa0AldcdhCUX1ma7eZ2bA7zYLI2RbKNvBePk4Dnig/MVJBe2PUVBbo78TCxnExLZl7P7+faelKxWR5IVyy70NLglYlpL22Q/Ul3GBuUjhqCpolpkvBfqjrFa8L1Oo7g8vkrkFLPK9Ul1MVlAQ6mt/oWWObWWiO1FfHw4NiHMvGnnkRQSEg2qbmNzXvi4YIt1RhBCg1hdUJP9l1k8avRFpO9rcF3x3MM7ZuzED7zR7j0qExyguu76zYRsI/q3COy5Gw+kwl0hpN2nQSUqS0g6xZ7zxbB09ygXUS4IkZHN2tCBMgEzQh8axIQ+3ogaDC7RDESTi24+T7JXIjFY9FaEAEQEAAcLAdgQYAQgAIAUCXb3wMQIbDBYhBMCyk5nNgMGX89zC8GVj9ZoUEAYDAAoJEGVj9ZoUEAYD41wH/jIQgva+k3vmGtfYDR5tB/IdEpc6MjGJxo2NwOkBKYJfaigyK3dmZ1DY8ZfkYMfQ9s5d4cW3Lel4t7nRH5Vh5FiaIWlDuxfGVTMLNpOzlXswgHlwckrfJucVWk3/hLT/xStsSjC+SwKSC6+ejmHIqkSqbTztwVCABg63otzREV4NspEsSrO0+SUD+n2mpFFeo4ULjPXEtlJzrmoJNdByDBEODiMFUyw0voMXN13ZqFv46HVtmembBxc8tJXtHX8rvC2ODiyygI3y3HENJPYR+CBGY/v8K8sg35i7PidUEsK/V3NJRTU0WkI+NS+4b80xE5KxizQMTDNPiSuTOlb7gO4=
|
||||
476
tests/location.rs
Normal file
476
tests/location.rs
Normal file
@@ -0,0 +1,476 @@
|
||||
//! Integration tests for location streaming.
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::mem::discriminant;
|
||||
use std::path::Path;
|
||||
use std::sync::{atomic, Arc, Condvar, Mutex};
|
||||
use std::thread;
|
||||
|
||||
use itertools::Itertools;
|
||||
use libc::uintptr_t;
|
||||
use serde::Deserialize;
|
||||
use tempfile;
|
||||
|
||||
use deltachat::chat;
|
||||
use deltachat::config::Config;
|
||||
use deltachat::contact::Contact;
|
||||
use deltachat::context::Context;
|
||||
use deltachat::job;
|
||||
use deltachat::location;
|
||||
use deltachat::Event;
|
||||
|
||||
/// Credentials for a test account.
|
||||
///
|
||||
/// This is populated by the JSON returned from the account provider's
|
||||
/// API.
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct AccountCredentials {
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
impl AccountCredentials {
|
||||
/// Creates a new online account.
|
||||
///
|
||||
/// Invoke the API of the account provider to create a new
|
||||
/// temporary account.
|
||||
fn new(provider_url: &str) -> AccountCredentials {
|
||||
let (post_url, token) = provider_url.splitn(2, '#').next_tuple().unwrap();
|
||||
let mut data: HashMap<&str, u64> = HashMap::new();
|
||||
data.insert("token_create_user", token.parse().unwrap());
|
||||
let client = reqwest::Client::new();
|
||||
let mut response = client.post(post_url).json(&data).send().unwrap();
|
||||
assert!(
|
||||
response.status().is_success(),
|
||||
format!("Failed to create new tmpuser: {}", response.status())
|
||||
);
|
||||
response.json().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EventsItem {
|
||||
acc_name: String,
|
||||
when: std::time::Duration,
|
||||
event: Event,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EventsQueue {
|
||||
name: String,
|
||||
events: Mutex<VecDeque<EventsItem>>,
|
||||
cond: Condvar,
|
||||
}
|
||||
|
||||
impl EventsQueue {
|
||||
fn new(name: &str) -> EventsQueue {
|
||||
EventsQueue {
|
||||
name: name.to_string(),
|
||||
events: Mutex::new(VecDeque::new()),
|
||||
cond: Condvar::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push(&self, evt: EventsItem) {
|
||||
let mut queue = self.events.lock().unwrap();
|
||||
queue.push_back(evt);
|
||||
self.cond.notify_all();
|
||||
}
|
||||
|
||||
fn wait_for(&self, event: Event, data: bool) -> Result<(), ()> {
|
||||
println!(
|
||||
"==> [{}] Waiting for: {:?} match-data={}",
|
||||
self.name, event, data
|
||||
);
|
||||
let mut queue = self.events.lock().unwrap();
|
||||
let start_time = std::time::Instant::now();
|
||||
loop {
|
||||
while let Some(item) = queue.pop_front() {
|
||||
let hit = match data {
|
||||
true => event == item.event,
|
||||
false => discriminant(&event) == discriminant(&item.event),
|
||||
};
|
||||
self.log_event(&item);
|
||||
if hit {
|
||||
println!(
|
||||
"<== [{}] Found {:?} match-data={} in {:?}",
|
||||
self.name,
|
||||
event,
|
||||
data,
|
||||
start_time.elapsed()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
if start_time.elapsed().as_secs() > 25 {
|
||||
println!(
|
||||
"=!= [{}] Timed out waiting for {:?} match-data={}",
|
||||
self.name, event, data
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
queue = self.cond.wait(queue).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
let mut queue = self.events.lock().unwrap();
|
||||
while let Some(item) = queue.pop_front() {
|
||||
self.log_event(&item);
|
||||
}
|
||||
}
|
||||
|
||||
fn log_event(&self, item: &EventsItem) {
|
||||
match &item.event {
|
||||
Event::Info(msg) => println!("I [{} {:?}]: {}", item.acc_name, item.when, msg),
|
||||
Event::Warning(msg) => println!("W [{} {:?}]: {}", item.acc_name, item.when, msg),
|
||||
Event::Error(msg) => println!("E [{} {:?}]: {}", item.acc_name, item.when, msg),
|
||||
_ => println!("Evt [{} {:?}]: {:?}", item.acc_name, item.when, item.event),
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_log_events(&self) {
|
||||
let mut queue = self.events.lock().unwrap();
|
||||
for item in queue.iter() {
|
||||
self.log_event(item)
|
||||
}
|
||||
queue.retain(|item| match item.event {
|
||||
Event::Info(_) | Event::Warning(_) | Event::Error(_) => false,
|
||||
_ => true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// A Configured DeltaChat account.
|
||||
#[derive(Debug)]
|
||||
struct Account {
|
||||
name: String,
|
||||
creds: AccountCredentials,
|
||||
ctx: Arc<Context>,
|
||||
events: Arc<EventsQueue>,
|
||||
running: Arc<atomic::AtomicBool>,
|
||||
imap_handle: Option<thread::JoinHandle<()>>,
|
||||
mvbox_handle: Option<thread::JoinHandle<()>>,
|
||||
sentbox_handle: Option<thread::JoinHandle<()>>,
|
||||
smtp_handle: Option<thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
fn new(name: &str, dir: &Path, keys: KeyPair, start: std::time::Instant) -> Account {
|
||||
// Create events queue and callback.
|
||||
let events = Arc::new(EventsQueue::new(name));
|
||||
let events_cb = Arc::clone(&events);
|
||||
let name_cb = name.to_string();
|
||||
let cb = move |_ctx: &Context, evt: Event| -> uintptr_t {
|
||||
events_cb.push(EventsItem {
|
||||
acc_name: name_cb.clone(),
|
||||
when: start.elapsed(),
|
||||
event: evt,
|
||||
});
|
||||
0
|
||||
};
|
||||
|
||||
// Create and configure the context.
|
||||
let dbfile = dir.join(format!("{}.db", name));
|
||||
let creds = AccountCredentials::new(&Account::liveconfig_url());
|
||||
println!("Account credentials for {}: {:#?}", name, creds);
|
||||
let ctx = Arc::new(Context::new(Box::new(cb), "TestClient".into(), dbfile).unwrap());
|
||||
ctx.set_config(Config::Addr, Some(&creds.email)).unwrap();
|
||||
ctx.set_config(Config::MailPw, Some(&creds.password))
|
||||
.unwrap();
|
||||
keys.save_as_self(&ctx);
|
||||
deltachat::configure::configure(&ctx);
|
||||
|
||||
// Start the threads.
|
||||
let running = Arc::new(atomic::AtomicBool::new(true));
|
||||
let imap_handle = Self::start_imap(name, Arc::clone(&ctx), Arc::clone(&running));
|
||||
let mvbox_handle = Self::start_mvbox(name, Arc::clone(&ctx), Arc::clone(&running));
|
||||
let sentbox_handle = Self::start_sentbox(name, Arc::clone(&ctx), Arc::clone(&running));
|
||||
let smtp_handle = Self::start_smtp(name, Arc::clone(&ctx), Arc::clone(&running));
|
||||
events.clear_log_events();
|
||||
|
||||
Account {
|
||||
name: name.to_string(),
|
||||
creds,
|
||||
ctx,
|
||||
events,
|
||||
running,
|
||||
imap_handle: Some(imap_handle),
|
||||
mvbox_handle: Some(mvbox_handle),
|
||||
sentbox_handle: Some(sentbox_handle),
|
||||
smtp_handle: Some(smtp_handle),
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the liveconfig URL.
|
||||
///
|
||||
/// Prefers the `DCC_TMPACCOUNT_PROVIDER`, will also use the
|
||||
/// `DCC_PY_LIVECONFIG` environment variable and finally fall back
|
||||
/// to finding a file named `liveconfig` and starting with
|
||||
/// `#:provider:https://`.
|
||||
fn liveconfig_url() -> String {
|
||||
if let Some(url) = std::env::var("DCC_TMPACCOUNT_PROVIDER").ok() {
|
||||
return url;
|
||||
}
|
||||
if let Some(url) = std::env::var("DCC_PY_LIVECONFIG").ok() {
|
||||
return url;
|
||||
}
|
||||
let mut dir = Some(Path::new(".").canonicalize().unwrap());
|
||||
loop {
|
||||
let cfg_fname = match dir {
|
||||
Some(path) => {
|
||||
dir = path.parent().map(|p| p.to_path_buf());
|
||||
path.join("liveconfig")
|
||||
}
|
||||
None => break,
|
||||
};
|
||||
if cfg_fname.is_file() {
|
||||
let raw_data = std::fs::read(&cfg_fname).unwrap();
|
||||
let data = String::from_utf8(raw_data).unwrap();
|
||||
for line in data.lines() {
|
||||
if line.starts_with("#:provider:https://") {
|
||||
let (_, url) = line.split_at(11);
|
||||
return url.to_string();
|
||||
}
|
||||
}
|
||||
panic!("No provider URL in {}", cfg_fname.display());
|
||||
}
|
||||
}
|
||||
panic!("Found no liveconfig");
|
||||
}
|
||||
|
||||
fn start_imap(
|
||||
name: &str,
|
||||
ctx: Arc<Context>,
|
||||
running: Arc<atomic::AtomicBool>,
|
||||
) -> thread::JoinHandle<()> {
|
||||
thread::Builder::new()
|
||||
.name(format!("{}-imap", name))
|
||||
.spawn(move || {
|
||||
while running.load(atomic::Ordering::Relaxed) {
|
||||
job::perform_imap_jobs(&ctx);
|
||||
job::perform_imap_fetch(&ctx);
|
||||
if !running.load(atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
job::perform_imap_idle(&ctx);
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn start_mvbox(
|
||||
name: &str,
|
||||
ctx: Arc<Context>,
|
||||
running: Arc<atomic::AtomicBool>,
|
||||
) -> thread::JoinHandle<()> {
|
||||
thread::Builder::new()
|
||||
.name(format!("{}-mvbox", name))
|
||||
.spawn(move || {
|
||||
while running.load(atomic::Ordering::Relaxed) {
|
||||
job::perform_mvbox_jobs(&ctx);
|
||||
job::perform_mvbox_fetch(&ctx);
|
||||
if !running.load(atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
job::perform_mvbox_idle(&ctx);
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn start_sentbox(
|
||||
name: &str,
|
||||
ctx: Arc<Context>,
|
||||
running: Arc<atomic::AtomicBool>,
|
||||
) -> thread::JoinHandle<()> {
|
||||
thread::Builder::new()
|
||||
.name(format!("{}-sentbox", name))
|
||||
.spawn(move || {
|
||||
while running.load(atomic::Ordering::Relaxed) {
|
||||
job::perform_sentbox_jobs(&ctx);
|
||||
job::perform_sentbox_fetch(&ctx);
|
||||
if !running.load(atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
job::perform_sentbox_idle(&ctx);
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn start_smtp(
|
||||
name: &str,
|
||||
ctx: Arc<Context>,
|
||||
running: Arc<atomic::AtomicBool>,
|
||||
) -> thread::JoinHandle<()> {
|
||||
thread::Builder::new()
|
||||
.name(format!("{}-smtp", name))
|
||||
.spawn(move || {
|
||||
while running.load(atomic::Ordering::Relaxed) {
|
||||
job::perform_smtp_jobs(&ctx);
|
||||
job::perform_smtp_fetch(&ctx);
|
||||
if !running.load(atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
job::perform_smtp_idle(&ctx);
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Goes through the events queue and prints all log events.
|
||||
///
|
||||
/// Each processed event is removed from the queue.
|
||||
fn process_log_events(&self) {}
|
||||
}
|
||||
|
||||
impl Drop for Account {
|
||||
fn drop(&mut self) {
|
||||
println!("Terminating Account {}", self.name);
|
||||
self.running.store(false, atomic::Ordering::Relaxed);
|
||||
job::interrupt_imap_idle(&self.ctx);
|
||||
job::interrupt_mvbox_idle(&self.ctx);
|
||||
self.imap_handle.take().unwrap().join().unwrap();
|
||||
self.mvbox_handle.take().unwrap().join().unwrap();
|
||||
self.events.clear();
|
||||
println!("Account {} Terminated", self.name);
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper struct to handle account key pairs.
|
||||
struct KeyPair {
|
||||
public: deltachat::key::Key,
|
||||
private: deltachat::key::Key,
|
||||
}
|
||||
|
||||
impl KeyPair {
|
||||
/// Create a new [KeyPair].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let alice_keys = KeyPair::new(
|
||||
/// include_str!("../test-data/key/public.asc"),
|
||||
/// include_str!("../test-data/key/private.asc"),
|
||||
/// );
|
||||
/// ```
|
||||
fn new(public_data: &str, private_data: &str) -> KeyPair {
|
||||
let public =
|
||||
deltachat::key::Key::from_base64(public_data, deltachat::constants::KeyType::Public)
|
||||
.unwrap();
|
||||
let private =
|
||||
deltachat::key::Key::from_base64(private_data, deltachat::constants::KeyType::Private)
|
||||
.unwrap();
|
||||
KeyPair { public, private }
|
||||
}
|
||||
|
||||
/// Saves a key into the context as the default key of the self address.
|
||||
///
|
||||
/// [Config::Addr] must already be set.
|
||||
fn save_as_self(&self, ctx: &Context) {
|
||||
let addr = ctx.get_config(Config::Addr).unwrap();
|
||||
let ok = deltachat::key::dc_key_save_self_keypair(
|
||||
&ctx,
|
||||
&self.public,
|
||||
&self.private,
|
||||
&addr,
|
||||
true,
|
||||
&ctx.sql,
|
||||
);
|
||||
assert_eq!(ok, true);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_location_streaming() {
|
||||
// Create accounts
|
||||
let start = std::time::Instant::now();
|
||||
let tmpdir = tempfile::tempdir().unwrap();
|
||||
let alice_keys = KeyPair::new(
|
||||
include_str!("../test-data/key/public.asc"),
|
||||
include_str!("../test-data/key/private.asc"),
|
||||
);
|
||||
let alice = Account::new("alice", tmpdir.path(), alice_keys, start);
|
||||
let bob_keys = KeyPair::new(
|
||||
include_str!("../test-data/key/public2.asc"),
|
||||
include_str!("../test-data/key/private2.asc"),
|
||||
);
|
||||
let bob = Account::new("bob", tmpdir.path(), bob_keys, start);
|
||||
alice
|
||||
.events
|
||||
.wait_for(Event::ConfigureProgress(1000), true)
|
||||
.unwrap();
|
||||
bob.events
|
||||
.wait_for(Event::ConfigureProgress(1000), true)
|
||||
.unwrap();
|
||||
|
||||
// Create contacts and chats.
|
||||
let contact_bob = Contact::create(&alice.ctx, "Bob", &bob.creds.email).unwrap();
|
||||
let contact_alice = Contact::create(&bob.ctx, "Alice", &bob.creds.email).unwrap();
|
||||
let alice_to_bob = deltachat::chat::create_by_contact_id(&alice.ctx, contact_bob).unwrap();
|
||||
let bob_to_alice = deltachat::chat::create_by_contact_id(&bob.ctx, contact_alice).unwrap();
|
||||
alice.events.clear();
|
||||
bob.events.clear();
|
||||
|
||||
println!("### Starting location streaming from Alice to Bob");
|
||||
assert!(!location::is_sending_locations_to_chat(
|
||||
&alice.ctx,
|
||||
alice_to_bob
|
||||
));
|
||||
assert!(!location::is_sending_locations_to_chat(
|
||||
&bob.ctx,
|
||||
bob_to_alice
|
||||
));
|
||||
location::send_locations_to_chat(&alice.ctx, alice_to_bob, 100);
|
||||
assert!(location::is_sending_locations_to_chat(
|
||||
&alice.ctx,
|
||||
alice_to_bob
|
||||
));
|
||||
alice
|
||||
.events
|
||||
.wait_for(Event::SmtpMessageSent(Default::default()), false)
|
||||
.unwrap();
|
||||
assert_eq!(location::set(&alice.ctx, 1.0, 1.0, 1.0), true);
|
||||
alice
|
||||
.events
|
||||
.wait_for(Event::LocationChanged(Default::default()), false)
|
||||
.unwrap();
|
||||
assert_eq!(location::set(&alice.ctx, 1.1, 1.1, 1.0), true);
|
||||
chat::send_text_msg(&alice.ctx, alice_to_bob, "ping".to_string()).unwrap();
|
||||
alice
|
||||
.events
|
||||
.wait_for(Event::SmtpMessageSent(Default::default()), false)
|
||||
.unwrap();
|
||||
|
||||
println!("### Looking for location messages received by Bob");
|
||||
// First message is the "enabled-location-streaming" command.
|
||||
bob.events
|
||||
.wait_for(
|
||||
Event::MsgsChanged {
|
||||
chat_id: Default::default(),
|
||||
msg_id: Default::default(),
|
||||
},
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
// Core emits location changed before the incoming message. Sadly
|
||||
// the the ordering requirement is brittle.
|
||||
bob.events
|
||||
.wait_for(Event::LocationChanged(Default::default()), false)
|
||||
.unwrap();
|
||||
// Next message is the "ping" one which should contain a location.
|
||||
bob.events
|
||||
.wait_for(
|
||||
Event::MsgsChanged {
|
||||
chat_id: Default::default(),
|
||||
msg_id: Default::default(),
|
||||
},
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
let positions = location::get_range(&bob.ctx, bob_to_alice, contact_alice, 0, 0);
|
||||
println!("pos len: {}", positions.len());
|
||||
println!("{:#?}", positions);
|
||||
assert!(false, "THE END");
|
||||
}
|
||||
Reference in New Issue
Block a user