mirror of
https://github.com/chatmail/core.git
synced 2026-04-04 06:22:16 +03:00
Compare commits
19 Commits
fix_empty_
...
fix_few_ma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a208c4f6b | ||
|
|
25ab59eb93 | ||
|
|
e6f737be9d | ||
|
|
e94a896478 | ||
|
|
a7a0277330 | ||
|
|
62a2f70fab | ||
|
|
04ec7f86ef | ||
|
|
f9e27934f5 | ||
|
|
f1b978c9c7 | ||
|
|
4eea6b297a | ||
|
|
0c77cadf19 | ||
|
|
b616830aea | ||
|
|
4bc637d6d8 | ||
|
|
bb605e28aa | ||
|
|
deddb8fde4 | ||
|
|
a59f0bd885 | ||
|
|
881e15d218 | ||
|
|
7bf825a253 | ||
|
|
fe7d99ae11 |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,32 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0-beta.9
|
||||
|
||||
- historic: we now use the mailparse crate and lettre-email to generate mime
|
||||
messages. This got rid of mmime completely, the C2rust generated port of the libetpan
|
||||
mime-parse -- IOW 22KLocs of cumbersome code removed! see
|
||||
https://github.com/deltachat/deltachat-core-rust/pull/904#issuecomment-561163330
|
||||
many thanks @dignifiedquire for making everybody's life easier
|
||||
and @jonhoo (from rust-imap fame) for suggesting to use the mailparse crate :)
|
||||
|
||||
- lots of improvements and better error handling in many rust modules
|
||||
thanks @link2xt @flub @r10s, @hpk42 and @dignifiedquire
|
||||
|
||||
- @r10s introduced a new device chat which has an initial
|
||||
welcome message. See
|
||||
https://c.delta.chat/classdc__context__t.html#a1a2aad98bd23c1d21ee42374e241f389
|
||||
for the main new FFI-API.
|
||||
|
||||
- fix moving self-sent messages, thanks @r10s, @flub, @hpk42
|
||||
|
||||
- fix flakyness/sometimes-failing verified/join-protocols,
|
||||
thanks @flub, @r10s, @hpk42
|
||||
|
||||
- new DC_EVENT_SECUREJOIN_SUCCEEDED event
|
||||
|
||||
- many little fixes and rustifications (@link2xt, @flub, @hpk42)
|
||||
|
||||
|
||||
## 1.0.0-beta.8
|
||||
|
||||
- now uses async-email/async-imap as the new base
|
||||
|
||||
@@ -1139,24 +1139,6 @@ void dc_set_draft (dc_context_t* context, uint32_t ch
|
||||
uint32_t dc_add_device_msg (dc_context_t* context, const char* label, dc_msg_t* msg);
|
||||
|
||||
|
||||
/**
|
||||
* Init device-messages and saved-messages chat.
|
||||
* This function adds the device-chat and saved-messages chat
|
||||
* and adds one or more welcome or update-messages.
|
||||
* The ui can add messages on its own using dc_add_device_msg() -
|
||||
* for ordering, either before or after or even without calling this function.
|
||||
*
|
||||
* Chat and message creation is done only once.
|
||||
* So if the user has manually deleted things, they won't be re-created
|
||||
* (however, not seen device messages are added and may re-create the device-chat).
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_update_device_chats (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Check if a device-message with a given label was ever added.
|
||||
* Device-messages can be added dc_add_device_msg().
|
||||
|
||||
@@ -851,21 +851,6 @@ pub unsafe extern "C" fn dc_add_device_msg(
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_update_device_chats(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_update_device_chats()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &mut *context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
ctx.update_device_chats()
|
||||
.unwrap_or_log_default(ctx, "Failed to add device message")
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_was_device_msg_ever_added(
|
||||
context: *mut dc_context_t,
|
||||
|
||||
@@ -832,9 +832,6 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
msg.set_text(Some(arg1.to_string()));
|
||||
chat::add_device_msg(context, None, Some(&mut msg))?;
|
||||
}
|
||||
"updatedevicechats" => {
|
||||
context.update_device_chats()?;
|
||||
}
|
||||
"listmedia" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
|
||||
@@ -1984,7 +1984,6 @@ pub fn add_device_msg(
|
||||
chat_id = create_or_lookup_by_contact_id(context, DC_CONTACT_ID_DEVICE, Blocked::Not)?.0;
|
||||
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
msg.try_calc_and_set_dimensions(context).ok();
|
||||
prepare_msg_blob(context, msg)?;
|
||||
unarchive(context, chat_id)?;
|
||||
|
||||
|
||||
@@ -28,38 +28,27 @@ pub enum Config {
|
||||
SendPort,
|
||||
SmtpCertificateChecks,
|
||||
ServerFlags,
|
||||
|
||||
#[strum(props(default = "INBOX"))]
|
||||
ImapFolder,
|
||||
|
||||
Displayname,
|
||||
Selfstatus,
|
||||
Selfavatar,
|
||||
|
||||
#[strum(props(default = "0"))]
|
||||
BccSelf,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
E2eeEnabled,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
MdnsEnabled,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
InboxWatch,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
SentboxWatch,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
MvboxWatch,
|
||||
|
||||
#[strum(props(default = "1"))]
|
||||
MvboxMove,
|
||||
|
||||
#[strum(props(default = "0"))] // also change ShowEmails.default() on changes
|
||||
ShowEmails,
|
||||
|
||||
SaveMimeHeaders,
|
||||
ConfiguredAddr,
|
||||
ConfiguredMailServer,
|
||||
@@ -77,13 +66,11 @@ pub enum Config {
|
||||
ConfiguredSendSecurity,
|
||||
ConfiguredE2EEEnabled,
|
||||
Configured,
|
||||
|
||||
// Deprecated
|
||||
#[strum(serialize = "sys.version")]
|
||||
SysVersion,
|
||||
|
||||
#[strum(serialize = "sys.msgsize_max_recommended")]
|
||||
SysMsgsizeMaxRecommended,
|
||||
|
||||
#[strum(serialize = "sys.config_keys")]
|
||||
SysConfigKeys,
|
||||
}
|
||||
|
||||
@@ -1,45 +1,17 @@
|
||||
//! # Thunderbird's Autoconfiguration implementation
|
||||
//!
|
||||
//! Documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
|
||||
use failure::Fail;
|
||||
|
||||
//! Thunderbird's Autoconfiguration implementation
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::login_param::LoginParam;
|
||||
|
||||
use super::read_url::read_url;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "Invalid email address: {:?}", _0)]
|
||||
InvalidEmailAddress(String),
|
||||
|
||||
#[fail(display = "XML error at position {}", position)]
|
||||
InvalidXml {
|
||||
position: usize,
|
||||
#[cause]
|
||||
error: quick_xml::Error,
|
||||
},
|
||||
|
||||
#[fail(display = "Bad or incomplete autoconfig")]
|
||||
IncompleteAutoconfig(LoginParam),
|
||||
|
||||
#[fail(display = "Failed to get URL {}", _0)]
|
||||
ReadUrlError(#[cause] super::read_url::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<super::read_url::Error> for Error {
|
||||
fn from(err: super::read_url::Error) -> Error {
|
||||
Error::ReadUrlError(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
use super::read_autoconf_file;
|
||||
/* ******************************************************************************
|
||||
* Thunderbird's Autoconfigure
|
||||
******************************************************************************/
|
||||
/* documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
|
||||
struct MozAutoconfigure<'a> {
|
||||
pub in_emailaddr: &'a str,
|
||||
pub in_emaildomain: &'a str,
|
||||
@@ -51,14 +23,13 @@ struct MozAutoconfigure<'a> {
|
||||
pub tag_config: MozConfigTag,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(PartialEq)]
|
||||
enum MozServer {
|
||||
Undefined,
|
||||
Imap,
|
||||
Smtp,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MozConfigTag {
|
||||
Undefined,
|
||||
Hostname,
|
||||
@@ -67,14 +38,15 @@ enum MozConfigTag {
|
||||
Username,
|
||||
}
|
||||
|
||||
fn parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam> {
|
||||
pub fn moz_parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Error> {
|
||||
let mut reader = quick_xml::Reader::from_str(xml_raw);
|
||||
reader.trim_text(true);
|
||||
|
||||
// Split address into local part and domain part.
|
||||
let p = in_emailaddr
|
||||
.find('@')
|
||||
.ok_or(Error::InvalidEmailAddress(in_emailaddr.to_string()))?;
|
||||
let p = match in_emailaddr.find('@') {
|
||||
Some(i) => i,
|
||||
None => bail!("Email address {} does not contain @", in_emailaddr),
|
||||
};
|
||||
let (in_emaillocalpart, in_emaildomain) = in_emailaddr.split_at(p);
|
||||
let in_emaildomain = &in_emaildomain[1..];
|
||||
|
||||
@@ -91,22 +63,22 @@ fn parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam> {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
loop {
|
||||
let event = reader
|
||||
.read_event(&mut buf)
|
||||
.map_err(|error| Error::InvalidXml {
|
||||
position: reader.buffer_position(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
match event {
|
||||
quick_xml::events::Event::Start(ref e) => {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
moz_autoconfigure_starttag_cb(e, &mut moz_ac, &reader)
|
||||
}
|
||||
quick_xml::events::Event::End(ref e) => moz_autoconfigure_endtag_cb(e, &mut moz_ac),
|
||||
quick_xml::events::Event::Text(ref e) => {
|
||||
Ok(quick_xml::events::Event::End(ref e)) => moz_autoconfigure_endtag_cb(e, &mut moz_ac),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => {
|
||||
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
|
||||
}
|
||||
quick_xml::events::Event::Eof => break,
|
||||
Err(e) => {
|
||||
bail!(
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
@@ -117,26 +89,27 @@ fn parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam> {
|
||||
|| moz_ac.out.send_server.is_empty()
|
||||
|| moz_ac.out.send_port == 0
|
||||
{
|
||||
Err(Error::IncompleteAutoconfig(moz_ac.out))
|
||||
} else {
|
||||
Ok(moz_ac.out)
|
||||
let r = moz_ac.out.to_string();
|
||||
bail!("Bad or incomplete autoconfig: {}", r,);
|
||||
}
|
||||
|
||||
Ok(moz_ac.out)
|
||||
}
|
||||
|
||||
pub fn moz_autoconfigure(
|
||||
context: &Context,
|
||||
url: &str,
|
||||
param_in: &LoginParam,
|
||||
) -> Result<LoginParam> {
|
||||
let xml_raw = read_url(context, url)?;
|
||||
) -> Option<LoginParam> {
|
||||
let xml_raw = read_autoconf_file(context, url)?;
|
||||
|
||||
parse_xml(¶m_in.addr, &xml_raw).map_err(|err| {
|
||||
warn!(
|
||||
context,
|
||||
"Failed to parse Thunderbird autoconfiguration XML: {}", err
|
||||
);
|
||||
err.into()
|
||||
})
|
||||
match moz_parse_xml(¶m_in.addr, &xml_raw) {
|
||||
Err(err) => {
|
||||
warn!(context, "{}", err);
|
||||
None
|
||||
}
|
||||
Ok(lp) => Some(lp),
|
||||
}
|
||||
}
|
||||
|
||||
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
|
||||
@@ -341,7 +314,7 @@ mod tests {
|
||||
</loginPageInfo>
|
||||
</webMail>
|
||||
</clientConfig>";
|
||||
let res = parse_xml("example@outlook.com", xml_raw).expect("XML parsing failed");
|
||||
let res = moz_parse_xml("example@outlook.com", xml_raw).expect("XML parsing failed");
|
||||
assert_eq!(res.mail_server, "outlook.office365.com");
|
||||
assert_eq!(res.mail_port, 993);
|
||||
assert_eq!(res.send_server, "smtp.office365.com");
|
||||
|
||||
@@ -1,43 +1,14 @@
|
||||
//! Outlook's Autodiscover
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
use quick_xml;
|
||||
use quick_xml::events::BytesEnd;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::login_param::LoginParam;
|
||||
|
||||
use super::read_url::read_url;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "XML error at position {}", position)]
|
||||
InvalidXml {
|
||||
position: usize,
|
||||
#[cause]
|
||||
error: quick_xml::Error,
|
||||
},
|
||||
|
||||
#[fail(display = "Bad or incomplete autoconfig")]
|
||||
IncompleteAutoconfig(LoginParam),
|
||||
|
||||
#[fail(display = "Failed to get URL {}", _0)]
|
||||
ReadUrlError(#[cause] super::read_url::Error),
|
||||
|
||||
#[fail(display = "Number of redirection is exceeded")]
|
||||
RedirectionError,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<super::read_url::Error> for Error {
|
||||
fn from(err: super::read_url::Error) -> Error {
|
||||
Error::ReadUrlError(err)
|
||||
}
|
||||
}
|
||||
use super::read_autoconf_file;
|
||||
|
||||
/// Outlook's Autodiscover
|
||||
struct OutlookAutodiscover {
|
||||
pub out: LoginParam,
|
||||
pub out_imap_set: bool,
|
||||
@@ -54,7 +25,7 @@ enum ParsingResult {
|
||||
RedirectUrl(String),
|
||||
}
|
||||
|
||||
fn parse_xml(xml_raw: &str) -> Result<ParsingResult> {
|
||||
fn outlk_parse_xml(xml_raw: &str) -> Result<ParsingResult, Error> {
|
||||
let mut outlk_ad = OutlookAutodiscover {
|
||||
out: LoginParam::new(),
|
||||
out_imap_set: false,
|
||||
@@ -74,15 +45,8 @@ fn parse_xml(xml_raw: &str) -> Result<ParsingResult> {
|
||||
let mut current_tag: Option<String> = None;
|
||||
|
||||
loop {
|
||||
let event = reader
|
||||
.read_event(&mut buf)
|
||||
.map_err(|error| Error::InvalidXml {
|
||||
position: reader.buffer_position(),
|
||||
error,
|
||||
})?;
|
||||
|
||||
match event {
|
||||
quick_xml::events::Event::Start(ref e) => {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
let tag = String::from_utf8_lossy(e.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "protocol" {
|
||||
@@ -97,11 +61,11 @@ fn parse_xml(xml_raw: &str) -> Result<ParsingResult> {
|
||||
current_tag = Some(tag);
|
||||
}
|
||||
}
|
||||
quick_xml::events::Event::End(ref e) => {
|
||||
Ok(quick_xml::events::Event::End(ref e)) => {
|
||||
outlk_autodiscover_endtag_cb(e, &mut outlk_ad);
|
||||
current_tag = None;
|
||||
}
|
||||
quick_xml::events::Event::Text(ref e) => {
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => {
|
||||
let val = e.unescape_and_decode(&reader).unwrap_or_default();
|
||||
|
||||
if let Some(ref tag) = current_tag {
|
||||
@@ -117,14 +81,21 @@ fn parse_xml(xml_raw: &str) -> Result<ParsingResult> {
|
||||
};
|
||||
}
|
||||
}
|
||||
quick_xml::events::Event::Eof => break,
|
||||
Err(e) => {
|
||||
bail!(
|
||||
"Configure xml: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
// XML redirect via redirecturl
|
||||
let res = if outlk_ad.config_redirecturl.is_none()
|
||||
if outlk_ad.config_redirecturl.is_none()
|
||||
|| outlk_ad.config_redirecturl.as_ref().unwrap().is_empty()
|
||||
{
|
||||
if outlk_ad.out.mail_server.is_empty()
|
||||
@@ -132,34 +103,41 @@ fn parse_xml(xml_raw: &str) -> Result<ParsingResult> {
|
||||
|| outlk_ad.out.send_server.is_empty()
|
||||
|| outlk_ad.out.send_port == 0
|
||||
{
|
||||
return Err(Error::IncompleteAutoconfig(outlk_ad.out));
|
||||
let r = outlk_ad.out.to_string();
|
||||
bail!("Bad or incomplete autoconfig: {}", r,);
|
||||
}
|
||||
ParsingResult::LoginParam(outlk_ad.out)
|
||||
Ok(ParsingResult::LoginParam(outlk_ad.out))
|
||||
} else {
|
||||
ParsingResult::RedirectUrl(outlk_ad.config_redirecturl.unwrap())
|
||||
};
|
||||
Ok(res)
|
||||
Ok(ParsingResult::RedirectUrl(
|
||||
outlk_ad.config_redirecturl.unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn outlk_autodiscover(
|
||||
context: &Context,
|
||||
url: &str,
|
||||
_param_in: &LoginParam,
|
||||
) -> Result<LoginParam> {
|
||||
) -> Option<LoginParam> {
|
||||
let mut url = url.to_string();
|
||||
/* Follow up to 10 xml-redirects (http-redirects are followed in read_url() */
|
||||
/* Follow up to 10 xml-redirects (http-redirects are followed in read_autoconf_file() */
|
||||
for _i in 0..10 {
|
||||
let xml_raw = read_url(context, &url)?;
|
||||
let res = parse_xml(&xml_raw);
|
||||
if let Err(err) = &res {
|
||||
warn!(context, "{}", err);
|
||||
}
|
||||
match res? {
|
||||
ParsingResult::RedirectUrl(redirect_url) => url = redirect_url,
|
||||
ParsingResult::LoginParam(login_param) => return Ok(login_param),
|
||||
if let Some(xml_raw) = read_autoconf_file(context, &url) {
|
||||
match outlk_parse_xml(&xml_raw) {
|
||||
Err(err) => {
|
||||
warn!(context, "{}", err);
|
||||
return None;
|
||||
}
|
||||
Ok(res) => match res {
|
||||
ParsingResult::RedirectUrl(redirect_url) => url = redirect_url,
|
||||
ParsingResult::LoginParam(login_param) => return Some(login_param),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Err(Error::RedirectionError)
|
||||
None
|
||||
}
|
||||
|
||||
fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut OutlookAutodiscover) {
|
||||
@@ -201,7 +179,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_redirect() {
|
||||
let res = parse_xml("
|
||||
let res = outlk_parse_xml("
|
||||
<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
|
||||
<Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
|
||||
@@ -228,7 +206,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_parse_loginparam() {
|
||||
let res = parse_xml(
|
||||
let res = outlk_parse_xml(
|
||||
"\
|
||||
<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
//! Email accounts autoconfiguration process module
|
||||
|
||||
mod auto_mozilla;
|
||||
mod auto_outlook;
|
||||
mod read_url;
|
||||
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::config::Config;
|
||||
@@ -16,8 +12,10 @@ use crate::login_param::LoginParam;
|
||||
use crate::oauth2::*;
|
||||
use crate::param::Params;
|
||||
|
||||
use auto_mozilla::moz_autoconfigure;
|
||||
mod auto_outlook;
|
||||
use auto_outlook::outlk_autodiscover;
|
||||
mod auto_mozilla;
|
||||
use auto_mozilla::moz_autoconfigure;
|
||||
|
||||
macro_rules! progress {
|
||||
($context:tt, $progress:expr) => {
|
||||
@@ -168,7 +166,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
"https://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}",
|
||||
param_domain, param_addr_urlencoded
|
||||
);
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok();
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -180,7 +178,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
"https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}",
|
||||
param_domain, param_addr_urlencoded
|
||||
);
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok();
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -193,7 +191,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
"https://{}{}/autodiscover/autodiscover.xml",
|
||||
"", param_domain
|
||||
);
|
||||
param_autoconfig = outlk_autodiscover(context, &url, ¶m).ok();
|
||||
param_autoconfig = outlk_autodiscover(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -204,7 +202,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
"https://{}{}/autodiscover/autodiscover.xml",
|
||||
"autodiscover.", param_domain
|
||||
);
|
||||
param_autoconfig = outlk_autodiscover(context, &url, ¶m).ok();
|
||||
param_autoconfig = outlk_autodiscover(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -216,7 +214,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
"http://autoconfig.{}/mail/config-v1.1.xml?emailaddress={}",
|
||||
param_domain, param_addr_urlencoded
|
||||
);
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok();
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -228,7 +226,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
"http://{}/.well-known/autoconfig/mail/config-v1.1.xml",
|
||||
param_domain
|
||||
);
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok();
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -238,7 +236,7 @@ pub fn JobConfigureImap(context: &Context) {
|
||||
if param_autoconfig.is_none() {
|
||||
/* always SSL for Thunderbird's database */
|
||||
let url = format!("https://autoconfig.thunderbird.net/v1.1/{}", param_domain);
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m).ok();
|
||||
param_autoconfig = moz_autoconfigure(context, &url, ¶m);
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -564,6 +562,27 @@ fn try_smtp_one_param(context: &Context, param: &LoginParam) -> Option<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Configure a Context
|
||||
******************************************************************************/
|
||||
|
||||
pub fn read_autoconf_file(context: &Context, url: &str) -> Option<String> {
|
||||
info!(context, "Testing {} ...", url);
|
||||
|
||||
match reqwest::Client::new()
|
||||
.get(url)
|
||||
.send()
|
||||
.and_then(|mut res| res.text())
|
||||
{
|
||||
Ok(res) => Some(res),
|
||||
Err(_err) => {
|
||||
info!(context, "Can\'t read file.",);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
use crate::context::Context;
|
||||
use failure::Fail;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "URL request error")]
|
||||
GetError(#[cause] reqwest::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
pub fn read_url(context: &Context, url: &str) -> Result<String> {
|
||||
info!(context, "Requesting URL {}", url);
|
||||
|
||||
match reqwest::Client::new()
|
||||
.get(url)
|
||||
.send()
|
||||
.and_then(|mut res| res.text())
|
||||
{
|
||||
Ok(res) => Ok(res),
|
||||
Err(err) => {
|
||||
info!(context, "Can\'t read URL {}", url);
|
||||
|
||||
Err(Error::GetError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/job.rs
21
src/job.rs
@@ -605,7 +605,26 @@ fn set_delivered(context: &Context, msg_id: MsgId) {
|
||||
#[allow(non_snake_case)]
|
||||
pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||
let mut msg = Message::load_from_db(context, msg_id)?;
|
||||
msg.try_calc_and_set_dimensions(context).ok();
|
||||
|
||||
if chat::msgtype_has_file(msg.type_0) {
|
||||
let file_param = msg.param.get_path(Param::File, context)?;
|
||||
if let Some(pathNfilename) = file_param {
|
||||
if (msg.type_0 == Viewtype::Image || msg.type_0 == Viewtype::Gif)
|
||||
&& !msg.param.exists(Param::Width)
|
||||
{
|
||||
msg.param.set_int(Param::Width, 0);
|
||||
msg.param.set_int(Param::Height, 0);
|
||||
|
||||
if let Ok(buf) = dc_read_file(context, pathNfilename) {
|
||||
if let Ok((width, height)) = dc_get_filemeta(&buf) {
|
||||
msg.param.set_int(Param::Width, width as i32);
|
||||
msg.param.set_int(Param::Height, height as i32);
|
||||
}
|
||||
}
|
||||
msg.save_param_to_disk(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* create message */
|
||||
let needs_encryption = msg.param.get_int(Param::GuaranteeE2ee).unwrap_or_default();
|
||||
|
||||
@@ -309,32 +309,6 @@ impl Message {
|
||||
self.param.get_path(Param::File, context).unwrap_or(None)
|
||||
}
|
||||
|
||||
pub fn try_calc_and_set_dimensions(&mut self, context: &Context) -> Result<(), Error> {
|
||||
if chat::msgtype_has_file(self.type_0) {
|
||||
let file_param = self.param.get_path(Param::File, context)?;
|
||||
if let Some(path_and_filename) = file_param {
|
||||
if (self.type_0 == Viewtype::Image || self.type_0 == Viewtype::Gif)
|
||||
&& !self.param.exists(Param::Width)
|
||||
{
|
||||
self.param.set_int(Param::Width, 0);
|
||||
self.param.set_int(Param::Height, 0);
|
||||
|
||||
if let Ok(buf) = dc_read_file(context, path_and_filename) {
|
||||
if let Ok((width, height)) = dc_get_filemeta(&buf) {
|
||||
self.param.set_int(Param::Width, width as i32);
|
||||
self.param.set_int(Param::Height, height as i32);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.id.is_unset() {
|
||||
self.save_param_to_disk(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if a message has a location bound to it.
|
||||
/// These messages are also returned by dc_get_locations()
|
||||
/// and the UI may decide to display a special icon beside such messages,
|
||||
|
||||
@@ -516,7 +516,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
// Set the appropriate Content-Type for the outer message
|
||||
let mut outer_message = PartBuilder::new().header((
|
||||
"Content-Type".to_string(),
|
||||
"multipart/encrypted; protocol=\"application/pgp-encrypted\"".to_string(),
|
||||
"multipart/encrypted; protocol=\"application/pgp-encrypted\";".to_string(),
|
||||
));
|
||||
|
||||
// Store the unprotected headers on the outer message.
|
||||
|
||||
@@ -80,12 +80,15 @@ impl Smtp {
|
||||
return Err(Error::BadParameters);
|
||||
}
|
||||
|
||||
let from =
|
||||
EmailAddress::new(lp.addr.clone()).map_err(|err| Error::InvalidLoginAddress {
|
||||
address: lp.addr.clone(),
|
||||
error: err,
|
||||
})?;
|
||||
self.from = Some(from);
|
||||
self.from = match EmailAddress::new(lp.addr.clone()) {
|
||||
Ok(addr) => Some(addr),
|
||||
Err(err) => {
|
||||
return Err(Error::InvalidLoginAddress {
|
||||
address: lp.addr.clone(),
|
||||
error: err,
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let domain = &lp.send_server;
|
||||
let port = lp.send_port as u16;
|
||||
@@ -132,23 +135,30 @@ impl Smtp {
|
||||
lettre::smtp::ClientSecurity::Wrapper(tls_parameters)
|
||||
};
|
||||
|
||||
let client = lettre::smtp::SmtpClient::new((domain.as_str(), port), security)
|
||||
.map_err(Error::ConnectionSetupFailure)?;
|
||||
|
||||
let client = client
|
||||
.smtp_utf8(true)
|
||||
.credentials(creds)
|
||||
.authentication_mechanism(mechanism)
|
||||
.connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited);
|
||||
let mut trans = client.transport();
|
||||
trans.connect().map_err(Error::ConnectionFailure)?;
|
||||
|
||||
self.transport = Some(trans);
|
||||
self.transport_connected = true;
|
||||
context.call_cb(Event::SmtpConnected(format!(
|
||||
"SMTP-LOGIN as {} ok",
|
||||
lp.send_user,
|
||||
)));
|
||||
Ok(())
|
||||
match lettre::smtp::SmtpClient::new((domain.as_str(), port), security) {
|
||||
Ok(client) => {
|
||||
let client = client
|
||||
.smtp_utf8(true)
|
||||
.credentials(creds)
|
||||
.authentication_mechanism(mechanism)
|
||||
.connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited);
|
||||
let mut trans = client.transport();
|
||||
match trans.connect() {
|
||||
Ok(()) => {
|
||||
self.transport = Some(trans);
|
||||
self.transport_connected = true;
|
||||
context.call_cb(Event::SmtpConnected(format!(
|
||||
"SMTP-LOGIN as {} ok",
|
||||
lp.send_user,
|
||||
)));
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => return Err(Error::ConnectionFailure(err)),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(Error::ConnectionSetupFailure(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
src/stock.rs
111
src/stock.rs
@@ -5,13 +5,9 @@ use std::borrow::Cow;
|
||||
use strum::EnumProperty;
|
||||
use strum_macros::EnumProperty;
|
||||
|
||||
use crate::chat;
|
||||
use crate::constants::{Viewtype, DC_CONTACT_ID_SELF};
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::message::Message;
|
||||
use crate::stock::StockMessage::{DeviceMessagesHint, WelcomeMessage};
|
||||
|
||||
/// Stock strings
|
||||
///
|
||||
@@ -26,169 +22,102 @@ use crate::stock::StockMessage::{DeviceMessagesHint, WelcomeMessage};
|
||||
pub enum StockMessage {
|
||||
#[strum(props(fallback = "No messages."))]
|
||||
NoMessages = 1,
|
||||
|
||||
#[strum(props(fallback = "Me"))]
|
||||
SelfMsg = 2,
|
||||
|
||||
#[strum(props(fallback = "Draft"))]
|
||||
Draft = 3,
|
||||
|
||||
#[strum(props(fallback = "%1$s member(s)"))]
|
||||
Member = 4,
|
||||
|
||||
#[strum(props(fallback = "%1$s contact(s)"))]
|
||||
Contact = 6,
|
||||
|
||||
#[strum(props(fallback = "Voice message"))]
|
||||
VoiceMessage = 7,
|
||||
|
||||
#[strum(props(fallback = "Contact requests"))]
|
||||
DeadDrop = 8,
|
||||
|
||||
#[strum(props(fallback = "Image"))]
|
||||
Image = 9,
|
||||
|
||||
#[strum(props(fallback = "Video"))]
|
||||
Video = 10,
|
||||
|
||||
#[strum(props(fallback = "Audio"))]
|
||||
Audio = 11,
|
||||
|
||||
#[strum(props(fallback = "File"))]
|
||||
File = 12,
|
||||
|
||||
#[strum(props(fallback = "Sent with my Delta Chat Messenger: https://delta.chat"))]
|
||||
StatusLine = 13,
|
||||
|
||||
#[strum(props(fallback = "Hello, I\'ve just created the group \"%1$s\" for us."))]
|
||||
NewGroupDraft = 14,
|
||||
|
||||
#[strum(props(fallback = "Group name changed from \"%1$s\" to \"%2$s\"."))]
|
||||
MsgGrpName = 15,
|
||||
|
||||
#[strum(props(fallback = "Group image changed."))]
|
||||
MsgGrpImgChanged = 16,
|
||||
|
||||
#[strum(props(fallback = "Member %1$s added."))]
|
||||
MsgAddMember = 17,
|
||||
|
||||
#[strum(props(fallback = "Member %1$s removed."))]
|
||||
MsgDelMember = 18,
|
||||
|
||||
#[strum(props(fallback = "Group left."))]
|
||||
MsgGroupLeft = 19,
|
||||
|
||||
#[strum(props(fallback = "GIF"))]
|
||||
Gif = 23,
|
||||
|
||||
#[strum(props(fallback = "Encrypted message"))]
|
||||
EncryptedMsg = 24,
|
||||
|
||||
#[strum(props(fallback = "End-to-end encryption available."))]
|
||||
E2eAvailable = 25,
|
||||
|
||||
#[strum(props(fallback = "Transport-encryption."))]
|
||||
EncrTransp = 27,
|
||||
|
||||
#[strum(props(fallback = "No encryption."))]
|
||||
EncrNone = 28,
|
||||
|
||||
#[strum(props(fallback = "This message was encrypted for another setup."))]
|
||||
CantDecryptMsgBody = 29,
|
||||
|
||||
#[strum(props(fallback = "Fingerprints"))]
|
||||
FingerPrints = 30,
|
||||
|
||||
#[strum(props(fallback = "Return receipt"))]
|
||||
ReadRcpt = 31,
|
||||
|
||||
#[strum(props(fallback = "This is a return receipt for the message \"%1$s\"."))]
|
||||
ReadRcptMailBody = 32,
|
||||
|
||||
#[strum(props(fallback = "Group image deleted."))]
|
||||
MsgGrpImgDeleted = 33,
|
||||
|
||||
#[strum(props(fallback = "End-to-end encryption preferred."))]
|
||||
E2ePreferred = 34,
|
||||
|
||||
#[strum(props(fallback = "%1$s verified."))]
|
||||
ContactVerified = 35,
|
||||
|
||||
#[strum(props(fallback = "Cannot verify %1$s"))]
|
||||
ContactNotVerified = 36,
|
||||
|
||||
#[strum(props(fallback = "Changed setup for %1$s"))]
|
||||
ContactSetupChanged = 37,
|
||||
|
||||
#[strum(props(fallback = "Archived chats"))]
|
||||
ArchivedChats = 40,
|
||||
|
||||
#[strum(props(fallback = "Starred messages"))]
|
||||
StarredMsgs = 41,
|
||||
|
||||
#[strum(props(fallback = "Autocrypt Setup Message"))]
|
||||
AcSetupMsgSubject = 42,
|
||||
|
||||
#[strum(props(
|
||||
fallback = "This is the Autocrypt Setup Message used to transfer your key between clients.\n\nTo decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device."
|
||||
))]
|
||||
AcSetupMsgBody = 43,
|
||||
|
||||
#[strum(props(fallback = "Messages I sent to myself"))]
|
||||
SelfTalkSubTitle = 50,
|
||||
|
||||
#[strum(props(fallback = "Cannot login as %1$s."))]
|
||||
CannotLogin = 60,
|
||||
|
||||
#[strum(props(fallback = "Could not connect to %1$s: %2$s"))]
|
||||
ServerResponse = 61,
|
||||
|
||||
#[strum(props(fallback = "%1$s by %2$s."))]
|
||||
MsgActionByUser = 62,
|
||||
|
||||
#[strum(props(fallback = "%1$s by me."))]
|
||||
MsgActionByMe = 63,
|
||||
|
||||
#[strum(props(fallback = "Location streaming enabled."))]
|
||||
MsgLocationEnabled = 64,
|
||||
|
||||
#[strum(props(fallback = "Location streaming disabled."))]
|
||||
MsgLocationDisabled = 65,
|
||||
|
||||
#[strum(props(fallback = "Location"))]
|
||||
Location = 66,
|
||||
|
||||
#[strum(props(fallback = "Sticker"))]
|
||||
Sticker = 67,
|
||||
|
||||
#[strum(props(fallback = "Device messages"))]
|
||||
DeviceMessages = 68,
|
||||
|
||||
#[strum(props(fallback = "Saved messages"))]
|
||||
SavedMessages = 69,
|
||||
|
||||
#[strum(props(
|
||||
fallback = "Messages in this chat are generated locally by your Delta Chat app. \
|
||||
Its makers use it to inform about app updates and problems during usage."
|
||||
))]
|
||||
DeviceMessagesHint = 70,
|
||||
|
||||
#[strum(props(fallback = "Welcome to Delta Chat! – \
|
||||
Delta Chat looks and feels like other popular messenger apps, \
|
||||
but does not involve centralized control, \
|
||||
tracking or selling you, friends, colleagues or family out to large organizations.\n\n\
|
||||
Technically, Delta Chat is an email application with a modern chat interface. \
|
||||
Email in a new dress if you will 👻\n\n\
|
||||
Use Delta Chat with anyone out of billions of people: just use their e-mail address. \
|
||||
Recipients don't need to install Delta Chat, visit websites or sign up anywhere - \
|
||||
however, of course, if they like, you may point them to 👉 https://get.delta.chat"))]
|
||||
WelcomeMessage = 71,
|
||||
}
|
||||
|
||||
/*
|
||||
"
|
||||
*/
|
||||
|
||||
impl StockMessage {
|
||||
/// Default untranslated strings for stock messages.
|
||||
///
|
||||
@@ -335,27 +264,6 @@ impl Context {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_device_chats(&self) -> Result<(), Error> {
|
||||
// create saved-messages chat;
|
||||
// we do this only once, if the user has deleted the chat, he can recreate it manually.
|
||||
if !self.sql.get_raw_config_bool(&self, "self-chat-added") {
|
||||
self.sql
|
||||
.set_raw_config_bool(&self, "self-chat-added", true)?;
|
||||
chat::create_by_contact_id(&self, DC_CONTACT_ID_SELF)?;
|
||||
}
|
||||
|
||||
// add welcome-messages. by the label, this is done only once,
|
||||
// if the user has deleted the message or the chat, it is not added again.
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.text = Some(self.stock_str(DeviceMessagesHint).to_string());
|
||||
chat::add_device_msg(&self, Some("core-about-device-chat"), Some(&mut msg))?;
|
||||
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.text = Some(self.stock_str(WelcomeMessage).to_string());
|
||||
chat::add_device_msg(&self, Some("core-welcome"), Some(&mut msg))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -365,7 +273,6 @@ mod tests {
|
||||
|
||||
use crate::constants::DC_CONTACT_ID_SELF;
|
||||
|
||||
use crate::chatlist::Chatlist;
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
#[test]
|
||||
@@ -521,22 +428,4 @@ mod tests {
|
||||
"Group name changed from \"Some chat\" to \"Other chat\" by Alice (alice@example.com)."
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_device_chats() {
|
||||
let t = dummy_context();
|
||||
t.ctx.update_device_chats().ok();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 2);
|
||||
|
||||
chat::delete(&t.ctx, chats.get_chat_id(0)).ok();
|
||||
chat::delete(&t.ctx, chats.get_chat_id(1)).ok();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 0);
|
||||
|
||||
// a subsequent call to update_device_chats() must not re-add manally deleted messages or chats
|
||||
t.ctx.update_device_chats().ok();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user