mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
Parse multiple servers in Mozilla autoconfig
Co-Authored-By: Simon Laux <mobile.info@simonlaux.de>
This commit is contained in:
committed by
link2xt
parent
94e52b5598
commit
c19e35b68d
@@ -1,7 +1,9 @@
|
|||||||
//! # Thunderbird's Autoconfiguration implementation
|
//! # Thunderbird's Autoconfiguration implementation
|
||||||
//!
|
//!
|
||||||
//! Documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration */
|
//! Documentation: https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration
|
||||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
use quick_xml::events::{BytesStart, Event};
|
||||||
|
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::login_param::LoginParam;
|
use crate::login_param::LoginParam;
|
||||||
@@ -11,22 +13,18 @@ use super::read_url::read_url;
|
|||||||
use super::Error;
|
use super::Error;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MozAutoconfigure<'a> {
|
struct Server {
|
||||||
pub in_emailaddr: &'a str,
|
pub typ: String,
|
||||||
pub in_emaildomain: &'a str,
|
pub hostname: String,
|
||||||
pub in_emaillocalpart: &'a str,
|
pub port: u16,
|
||||||
pub out: LoginParam,
|
pub sockettype: Socket,
|
||||||
pub out_imap_set: bool,
|
pub username: String,
|
||||||
pub out_smtp_set: bool,
|
|
||||||
pub tag_server: MozServer,
|
|
||||||
pub tag_config: MozConfigTag,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug)]
|
||||||
enum MozServer {
|
struct MozAutoconfigure {
|
||||||
Undefined,
|
pub incoming_servers: Vec<Server>,
|
||||||
Imap,
|
pub outgoing_servers: Vec<Server>,
|
||||||
Smtp,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -38,10 +36,139 @@ enum MozConfigTag {
|
|||||||
Username,
|
Username,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Error> {
|
/// Parses a single IncomingServer or OutgoingServer section.
|
||||||
let mut reader = quick_xml::Reader::from_str(xml_raw);
|
fn parse_server<B: BufRead>(
|
||||||
reader.trim_text(true);
|
reader: &mut quick_xml::Reader<B>,
|
||||||
|
server_event: &BytesStart,
|
||||||
|
) -> Result<Option<Server>, quick_xml::Error> {
|
||||||
|
let end_tag = String::from_utf8_lossy(server_event.name())
|
||||||
|
.trim()
|
||||||
|
.to_lowercase();
|
||||||
|
|
||||||
|
let typ = server_event
|
||||||
|
.attributes()
|
||||||
|
.find(|attr| {
|
||||||
|
attr.as_ref()
|
||||||
|
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "type")
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.map(|typ| {
|
||||||
|
typ.unwrap()
|
||||||
|
.unescape_and_decode_value(reader)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_lowercase()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut hostname = None;
|
||||||
|
let mut port = None;
|
||||||
|
let mut sockettype = Socket::Automatic;
|
||||||
|
let mut username = None;
|
||||||
|
|
||||||
|
let mut tag_config = MozConfigTag::Undefined;
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
loop {
|
||||||
|
match reader.read_event(&mut buf)? {
|
||||||
|
Event::Start(ref event) => {
|
||||||
|
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||||
|
|
||||||
|
if tag == "hostname" {
|
||||||
|
tag_config = MozConfigTag::Hostname;
|
||||||
|
} else if tag == "port" {
|
||||||
|
tag_config = MozConfigTag::Port;
|
||||||
|
} else if tag == "sockettype" {
|
||||||
|
tag_config = MozConfigTag::Sockettype;
|
||||||
|
} else if tag == "username" {
|
||||||
|
tag_config = MozConfigTag::Username;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::End(ref event) => {
|
||||||
|
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||||
|
|
||||||
|
if tag == end_tag {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Text(ref event) => {
|
||||||
|
let val = event
|
||||||
|
.unescape_and_decode(reader)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.trim()
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
match tag_config {
|
||||||
|
MozConfigTag::Hostname => hostname = Some(val),
|
||||||
|
MozConfigTag::Port => port = Some(val.parse().unwrap_or_default()),
|
||||||
|
MozConfigTag::Username => username = Some(val),
|
||||||
|
MozConfigTag::Sockettype => {
|
||||||
|
let val_lower = val.to_lowercase();
|
||||||
|
if val_lower == "ssl" {
|
||||||
|
sockettype = Socket::SSL;
|
||||||
|
}
|
||||||
|
if val_lower == "starttls" {
|
||||||
|
sockettype = Socket::STARTTLS;
|
||||||
|
}
|
||||||
|
if val_lower == "plain" {
|
||||||
|
sockettype = Socket::Plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Eof => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (Some(hostname), Some(port), Some(username)) = (hostname, port, username) {
|
||||||
|
Ok(Some(Server {
|
||||||
|
typ,
|
||||||
|
hostname,
|
||||||
|
port,
|
||||||
|
sockettype,
|
||||||
|
username,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_xml_reader<B: BufRead>(
|
||||||
|
reader: &mut quick_xml::Reader<B>,
|
||||||
|
) -> Result<MozAutoconfigure, quick_xml::Error> {
|
||||||
|
let mut incoming_servers = Vec::new();
|
||||||
|
let mut outgoing_servers = Vec::new();
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
loop {
|
||||||
|
match reader.read_event(&mut buf)? {
|
||||||
|
Event::Start(ref event) => {
|
||||||
|
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||||
|
|
||||||
|
if tag == "incomingserver" {
|
||||||
|
if let Some(incoming_server) = parse_server(reader, event)? {
|
||||||
|
incoming_servers.push(incoming_server);
|
||||||
|
}
|
||||||
|
} else if tag == "outgoingserver" {
|
||||||
|
if let Some(outgoing_server) = parse_server(reader, event)? {
|
||||||
|
outgoing_servers.push(outgoing_server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Eof => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(MozAutoconfigure {
|
||||||
|
incoming_servers,
|
||||||
|
outgoing_servers,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses XML and fills in address and domain placeholders.
|
||||||
|
fn parse_xml_with_address(in_emailaddr: &str, xml_raw: &str) -> Result<MozAutoconfigure, Error> {
|
||||||
// Split address into local part and domain part.
|
// Split address into local part and domain part.
|
||||||
let parts: Vec<&str> = in_emailaddr.rsplitn(2, '@').collect();
|
let parts: Vec<&str> = in_emailaddr.rsplitn(2, '@').collect();
|
||||||
let (in_emaillocalpart, in_emaildomain) = match &parts[..] {
|
let (in_emaillocalpart, in_emaildomain) = match &parts[..] {
|
||||||
@@ -49,48 +176,79 @@ fn parse_xml(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Error> {
|
|||||||
_ => return Err(Error::InvalidEmailAddress(in_emailaddr.to_string())),
|
_ => return Err(Error::InvalidEmailAddress(in_emailaddr.to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut moz_ac = MozAutoconfigure {
|
let mut reader = quick_xml::Reader::from_str(xml_raw);
|
||||||
in_emailaddr,
|
reader.trim_text(true);
|
||||||
in_emaildomain,
|
|
||||||
in_emaillocalpart,
|
let moz_ac = parse_xml_reader(&mut reader).map_err(|error| Error::InvalidXml {
|
||||||
out: LoginParam::new(),
|
position: reader.buffer_position(),
|
||||||
out_imap_set: false,
|
error,
|
||||||
out_smtp_set: false,
|
})?;
|
||||||
tag_server: MozServer::Undefined,
|
|
||||||
tag_config: MozConfigTag::Undefined,
|
let fill_placeholders = |val: &str| -> String {
|
||||||
|
val.replace("%EMAILADDRESS%", in_emailaddr)
|
||||||
|
.replace("%EMAILLOCALPART%", in_emaillocalpart)
|
||||||
|
.replace("%EMAILDOMAIN%", in_emaildomain)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buf = Vec::new();
|
let fill_server_placeholders = |server: Server| -> Server {
|
||||||
loop {
|
Server {
|
||||||
let event = reader
|
typ: server.typ,
|
||||||
.read_event(&mut buf)
|
hostname: fill_placeholders(&server.hostname),
|
||||||
.map_err(|error| Error::InvalidXml {
|
port: server.port,
|
||||||
position: reader.buffer_position(),
|
sockettype: server.sockettype,
|
||||||
error,
|
username: fill_placeholders(&server.username),
|
||||||
})?;
|
|
||||||
|
|
||||||
match event {
|
|
||||||
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) => {
|
|
||||||
moz_autoconfigure_text_cb(e, &mut moz_ac, &reader)
|
|
||||||
}
|
|
||||||
quick_xml::events::Event::Eof => break,
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
buf.clear();
|
};
|
||||||
|
|
||||||
|
Ok(MozAutoconfigure {
|
||||||
|
incoming_servers: moz_ac
|
||||||
|
.incoming_servers
|
||||||
|
.into_iter()
|
||||||
|
.map(fill_server_placeholders)
|
||||||
|
.collect(),
|
||||||
|
outgoing_servers: moz_ac
|
||||||
|
.outgoing_servers
|
||||||
|
.into_iter()
|
||||||
|
.map(fill_server_placeholders)
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses XML into `LoginParam` structure.
|
||||||
|
fn parse_loginparam(in_emailaddr: &str, xml_raw: &str) -> Result<LoginParam, Error> {
|
||||||
|
let moz_ac = parse_xml_with_address(in_emailaddr, xml_raw)?;
|
||||||
|
|
||||||
|
let mut login_param = LoginParam::new();
|
||||||
|
if let Some(imap_server) = moz_ac
|
||||||
|
.incoming_servers
|
||||||
|
.into_iter()
|
||||||
|
.find(|incoming_server| incoming_server.typ == "imap")
|
||||||
|
{
|
||||||
|
login_param.imap.server = imap_server.hostname;
|
||||||
|
login_param.imap.port = imap_server.port;
|
||||||
|
login_param.imap.security = imap_server.sockettype;
|
||||||
|
login_param.imap.user = imap_server.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
if moz_ac.out.imap.server.is_empty()
|
if let Some(smtp_server) = moz_ac
|
||||||
|| moz_ac.out.imap.port == 0
|
.outgoing_servers
|
||||||
|| moz_ac.out.smtp.server.is_empty()
|
.into_iter()
|
||||||
|| moz_ac.out.smtp.port == 0
|
.find(|outgoing_server| outgoing_server.typ == "smtp")
|
||||||
{
|
{
|
||||||
Err(Error::IncompleteAutoconfig(moz_ac.out))
|
login_param.smtp.server = smtp_server.hostname;
|
||||||
|
login_param.smtp.port = smtp_server.port;
|
||||||
|
login_param.smtp.security = smtp_server.sockettype;
|
||||||
|
login_param.smtp.user = smtp_server.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
if login_param.imap.server.is_empty()
|
||||||
|
|| login_param.imap.port == 0
|
||||||
|
|| login_param.smtp.server.is_empty()
|
||||||
|
|| login_param.smtp.port == 0
|
||||||
|
{
|
||||||
|
Err(Error::IncompleteAutoconfig(login_param))
|
||||||
} else {
|
} else {
|
||||||
Ok(moz_ac.out)
|
Ok(login_param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,7 +259,7 @@ pub async fn moz_autoconfigure(
|
|||||||
) -> Result<LoginParam, Error> {
|
) -> Result<LoginParam, Error> {
|
||||||
let xml_raw = read_url(context, url).await?;
|
let xml_raw = read_url(context, url).await?;
|
||||||
|
|
||||||
let res = parse_xml(¶m_in.addr, &xml_raw);
|
let res = parse_loginparam(¶m_in.addr, &xml_raw);
|
||||||
if let Err(err) = &res {
|
if let Err(err) = &res {
|
||||||
warn!(
|
warn!(
|
||||||
context,
|
context,
|
||||||
@@ -111,212 +269,58 @@ pub async fn moz_autoconfigure(
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn moz_autoconfigure_text_cb<B: std::io::BufRead>(
|
|
||||||
event: &BytesText,
|
|
||||||
moz_ac: &mut MozAutoconfigure,
|
|
||||||
reader: &quick_xml::Reader<B>,
|
|
||||||
) {
|
|
||||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
|
||||||
|
|
||||||
let addr = moz_ac.in_emailaddr;
|
|
||||||
let email_local = moz_ac.in_emaillocalpart;
|
|
||||||
let email_domain = moz_ac.in_emaildomain;
|
|
||||||
|
|
||||||
let val = val
|
|
||||||
.trim()
|
|
||||||
.replace("%EMAILADDRESS%", addr)
|
|
||||||
.replace("%EMAILLOCALPART%", email_local)
|
|
||||||
.replace("%EMAILDOMAIN%", email_domain);
|
|
||||||
|
|
||||||
match moz_ac.tag_server {
|
|
||||||
MozServer::Imap => match moz_ac.tag_config {
|
|
||||||
MozConfigTag::Hostname => moz_ac.out.imap.server = val,
|
|
||||||
MozConfigTag::Port => moz_ac.out.imap.port = val.parse().unwrap_or_default(),
|
|
||||||
MozConfigTag::Username => moz_ac.out.imap.user = val,
|
|
||||||
MozConfigTag::Sockettype => {
|
|
||||||
let val_lower = val.to_lowercase();
|
|
||||||
if val_lower == "ssl" {
|
|
||||||
moz_ac.out.imap.security = Socket::SSL;
|
|
||||||
}
|
|
||||||
if val_lower == "starttls" {
|
|
||||||
moz_ac.out.imap.security = Socket::STARTTLS;
|
|
||||||
}
|
|
||||||
if val_lower == "plain" {
|
|
||||||
moz_ac.out.imap.security = Socket::Plain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
MozServer::Smtp => match moz_ac.tag_config {
|
|
||||||
MozConfigTag::Hostname => moz_ac.out.smtp.server = val,
|
|
||||||
MozConfigTag::Port => moz_ac.out.smtp.port = val.parse().unwrap_or_default(),
|
|
||||||
MozConfigTag::Username => moz_ac.out.smtp.user = val,
|
|
||||||
MozConfigTag::Sockettype => {
|
|
||||||
let val_lower = val.to_lowercase();
|
|
||||||
if val_lower == "ssl" {
|
|
||||||
moz_ac.out.smtp.security = Socket::SSL;
|
|
||||||
}
|
|
||||||
if val_lower == "starttls" {
|
|
||||||
moz_ac.out.smtp.security = Socket::STARTTLS;
|
|
||||||
}
|
|
||||||
if val_lower == "plain" {
|
|
||||||
moz_ac.out.smtp.security = Socket::Plain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
MozServer::Undefined => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn moz_autoconfigure_endtag_cb(event: &BytesEnd, moz_ac: &mut MozAutoconfigure) {
|
|
||||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
|
||||||
|
|
||||||
if tag == "incomingserver" {
|
|
||||||
if moz_ac.tag_server == MozServer::Imap {
|
|
||||||
moz_ac.out_imap_set = true;
|
|
||||||
}
|
|
||||||
moz_ac.tag_server = MozServer::Undefined;
|
|
||||||
moz_ac.tag_config = MozConfigTag::Undefined;
|
|
||||||
} else if tag == "outgoingserver" {
|
|
||||||
if moz_ac.tag_server == MozServer::Smtp {
|
|
||||||
moz_ac.out_smtp_set = true;
|
|
||||||
}
|
|
||||||
moz_ac.tag_server = MozServer::Undefined;
|
|
||||||
moz_ac.tag_config = MozConfigTag::Undefined;
|
|
||||||
} else {
|
|
||||||
moz_ac.tag_config = MozConfigTag::Undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn moz_autoconfigure_starttag_cb<B: std::io::BufRead>(
|
|
||||||
event: &BytesStart,
|
|
||||||
moz_ac: &mut MozAutoconfigure,
|
|
||||||
reader: &quick_xml::Reader<B>,
|
|
||||||
) {
|
|
||||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
|
||||||
|
|
||||||
if tag == "incomingserver" {
|
|
||||||
moz_ac.tag_server = if let Some(typ) = event.attributes().find(|attr| {
|
|
||||||
attr.as_ref()
|
|
||||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "type")
|
|
||||||
.unwrap_or_default()
|
|
||||||
}) {
|
|
||||||
let typ = typ
|
|
||||||
.unwrap()
|
|
||||||
.unescape_and_decode_value(reader)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_lowercase();
|
|
||||||
|
|
||||||
if typ == "imap" && !moz_ac.out_imap_set {
|
|
||||||
MozServer::Imap
|
|
||||||
} else {
|
|
||||||
MozServer::Undefined
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MozServer::Undefined
|
|
||||||
};
|
|
||||||
moz_ac.tag_config = MozConfigTag::Undefined;
|
|
||||||
} else if tag == "outgoingserver" {
|
|
||||||
moz_ac.tag_server = if !moz_ac.out_smtp_set {
|
|
||||||
MozServer::Smtp
|
|
||||||
} else {
|
|
||||||
MozServer::Undefined
|
|
||||||
};
|
|
||||||
moz_ac.tag_config = MozConfigTag::Undefined;
|
|
||||||
} else if tag == "hostname" {
|
|
||||||
moz_ac.tag_config = MozConfigTag::Hostname;
|
|
||||||
} else if tag == "port" {
|
|
||||||
moz_ac.tag_config = MozConfigTag::Port;
|
|
||||||
} else if tag == "sockettype" {
|
|
||||||
moz_ac.tag_config = MozConfigTag::Sockettype;
|
|
||||||
} else if tag == "username" {
|
|
||||||
moz_ac.tag_config = MozConfigTag::Username;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_outlook_autoconfig() {
|
fn test_parse_outlook_autoconfig() {
|
||||||
// Copied from https://autoconfig.thunderbird.net/v1.1/outlook.com on 2019-10-11
|
let xml_raw = include_str!("../../test-data/autoconfig/outlook.com.xml");
|
||||||
let xml_raw =
|
let res = parse_loginparam("example@outlook.com", xml_raw).expect("XML parsing failed");
|
||||||
"<clientConfig version=\"1.1\">
|
|
||||||
<emailProvider id=\"outlook.com\">
|
|
||||||
<domain>hotmail.com</domain>
|
|
||||||
<domain>hotmail.co.uk</domain>
|
|
||||||
<domain>hotmail.co.jp</domain>
|
|
||||||
<domain>hotmail.com.br</domain>
|
|
||||||
<domain>hotmail.de</domain>
|
|
||||||
<domain>hotmail.fr</domain>
|
|
||||||
<domain>hotmail.it</domain>
|
|
||||||
<domain>hotmail.es</domain>
|
|
||||||
<domain>live.com</domain>
|
|
||||||
<domain>live.co.uk</domain>
|
|
||||||
<domain>live.co.jp</domain>
|
|
||||||
<domain>live.de</domain>
|
|
||||||
<domain>live.fr</domain>
|
|
||||||
<domain>live.it</domain>
|
|
||||||
<domain>live.jp</domain>
|
|
||||||
<domain>msn.com</domain>
|
|
||||||
<domain>outlook.com</domain>
|
|
||||||
<displayName>Outlook.com (Microsoft)</displayName>
|
|
||||||
<displayShortName>Outlook</displayShortName>
|
|
||||||
<incomingServer type=\"exchange\">
|
|
||||||
<hostname>outlook.office365.com</hostname>
|
|
||||||
<port>443</port>
|
|
||||||
<username>%EMAILADDRESS%</username>
|
|
||||||
<socketType>SSL</socketType>
|
|
||||||
<authentication>OAuth2</authentication>
|
|
||||||
<owaURL>https://outlook.office365.com/owa/</owaURL>
|
|
||||||
<ewsURL>https://outlook.office365.com/ews/exchange.asmx</ewsURL>
|
|
||||||
<useGlobalPreferredServer>true</useGlobalPreferredServer>
|
|
||||||
</incomingServer>
|
|
||||||
<incomingServer type=\"imap\">
|
|
||||||
<hostname>outlook.office365.com</hostname>
|
|
||||||
<port>993</port>
|
|
||||||
<socketType>SSL</socketType>
|
|
||||||
<authentication>password-cleartext</authentication>
|
|
||||||
<username>%EMAILADDRESS%</username>
|
|
||||||
</incomingServer>
|
|
||||||
<incomingServer type=\"pop3\">
|
|
||||||
<hostname>outlook.office365.com</hostname>
|
|
||||||
<port>995</port>
|
|
||||||
<socketType>SSL</socketType>
|
|
||||||
<authentication>password-cleartext</authentication>
|
|
||||||
<username>%EMAILADDRESS%</username>
|
|
||||||
<pop3>
|
|
||||||
<leaveMessagesOnServer>true</leaveMessagesOnServer>
|
|
||||||
<!-- Outlook.com docs specifically mention that POP3 deletes have effect on the main inbox on webmail and IMAP -->
|
|
||||||
</pop3>
|
|
||||||
</incomingServer>
|
|
||||||
<outgoingServer type=\"smtp\">
|
|
||||||
<hostname>smtp.office365.com</hostname>
|
|
||||||
<port>587</port>
|
|
||||||
<socketType>STARTTLS</socketType>
|
|
||||||
<authentication>password-cleartext</authentication>
|
|
||||||
<username>%EMAILADDRESS%</username>
|
|
||||||
</outgoingServer>
|
|
||||||
<documentation url=\"http://windows.microsoft.com/en-US/windows/outlook/send-receive-from-app\">
|
|
||||||
<descr lang=\"en\">Set up an email app with Outlook.com</descr>
|
|
||||||
</documentation>
|
|
||||||
</emailProvider>
|
|
||||||
<webMail>
|
|
||||||
<loginPage url=\"https://www.outlook.com/\"/>
|
|
||||||
<loginPageInfo url=\"https://www.outlook.com/\">
|
|
||||||
<username>%EMAILADDRESS%</username>
|
|
||||||
<usernameField id=\"i0116\" name=\"login\"/>
|
|
||||||
<passwordField id=\"i0118\" name=\"passwd\"/>
|
|
||||||
<loginButton id=\"idSIButton9\" name=\"SI\"/>
|
|
||||||
</loginPageInfo>
|
|
||||||
</webMail>
|
|
||||||
</clientConfig>";
|
|
||||||
let res = parse_xml("example@outlook.com", xml_raw).expect("XML parsing failed");
|
|
||||||
assert_eq!(res.imap.server, "outlook.office365.com");
|
assert_eq!(res.imap.server, "outlook.office365.com");
|
||||||
assert_eq!(res.imap.port, 993);
|
assert_eq!(res.imap.port, 993);
|
||||||
assert_eq!(res.smtp.server, "smtp.office365.com");
|
assert_eq!(res.smtp.server, "smtp.office365.com");
|
||||||
assert_eq!(res.smtp.port, 587);
|
assert_eq!(res.smtp.port, 587);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_lakenet_autoconfig() {
|
||||||
|
let xml_raw = include_str!("../../test-data/autoconfig/lakenet.ch.xml");
|
||||||
|
let res =
|
||||||
|
parse_xml_with_address("example@lakenet.ch", xml_raw).expect("XML parsing failed");
|
||||||
|
|
||||||
|
assert_eq!(res.incoming_servers.len(), 4);
|
||||||
|
|
||||||
|
assert_eq!(res.incoming_servers[0].typ, "imap");
|
||||||
|
assert_eq!(res.incoming_servers[0].hostname, "mail.lakenet.ch");
|
||||||
|
assert_eq!(res.incoming_servers[0].port, 993);
|
||||||
|
assert_eq!(res.incoming_servers[0].sockettype, Socket::SSL);
|
||||||
|
assert_eq!(res.incoming_servers[0].username, "example@lakenet.ch");
|
||||||
|
|
||||||
|
assert_eq!(res.incoming_servers[1].typ, "imap");
|
||||||
|
assert_eq!(res.incoming_servers[1].hostname, "mail.lakenet.ch");
|
||||||
|
assert_eq!(res.incoming_servers[1].port, 143);
|
||||||
|
assert_eq!(res.incoming_servers[1].sockettype, Socket::STARTTLS);
|
||||||
|
assert_eq!(res.incoming_servers[1].username, "example@lakenet.ch");
|
||||||
|
|
||||||
|
assert_eq!(res.incoming_servers[2].typ, "pop3");
|
||||||
|
assert_eq!(res.incoming_servers[2].hostname, "mail.lakenet.ch");
|
||||||
|
assert_eq!(res.incoming_servers[2].port, 995);
|
||||||
|
assert_eq!(res.incoming_servers[2].sockettype, Socket::SSL);
|
||||||
|
assert_eq!(res.incoming_servers[2].username, "example@lakenet.ch");
|
||||||
|
|
||||||
|
assert_eq!(res.incoming_servers[3].typ, "pop3");
|
||||||
|
assert_eq!(res.incoming_servers[3].hostname, "mail.lakenet.ch");
|
||||||
|
assert_eq!(res.incoming_servers[3].port, 110);
|
||||||
|
assert_eq!(res.incoming_servers[3].sockettype, Socket::STARTTLS);
|
||||||
|
assert_eq!(res.incoming_servers[3].username, "example@lakenet.ch");
|
||||||
|
|
||||||
|
assert_eq!(res.outgoing_servers.len(), 1);
|
||||||
|
|
||||||
|
assert_eq!(res.outgoing_servers[0].typ, "smtp");
|
||||||
|
assert_eq!(res.outgoing_servers[0].hostname, "mail.lakenet.ch");
|
||||||
|
assert_eq!(res.outgoing_servers[0].port, 587);
|
||||||
|
assert_eq!(res.outgoing_servers[0].sockettype, Socket::STARTTLS);
|
||||||
|
assert_eq!(res.outgoing_servers[0].username, "example@lakenet.ch");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
59
test-data/autoconfig/lakenet.ch.xml
Normal file
59
test-data/autoconfig/lakenet.ch.xml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<clientConfig version="1.1">
|
||||||
|
<emailProvider id="lakenet.ch">
|
||||||
|
<domain>%EMAILDOMAIN%</domain>
|
||||||
|
<displayName>%EMAILDOMAIN% Mail</displayName>
|
||||||
|
<displayShortName>%EMAILDOMAIN%</displayShortName>
|
||||||
|
<incomingServer type="imap">
|
||||||
|
<hostname>mail.lakenet.ch</hostname>
|
||||||
|
<port>993</port>
|
||||||
|
<socketType>SSL</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</incomingServer>
|
||||||
|
<incomingServer type="imap">
|
||||||
|
<hostname>mail.lakenet.ch</hostname>
|
||||||
|
<port>143</port>
|
||||||
|
<socketType>STARTTLS</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</incomingServer>
|
||||||
|
<incomingServer type="pop3">
|
||||||
|
<hostname>mail.lakenet.ch</hostname>
|
||||||
|
<port>995</port>
|
||||||
|
<socketType>SSL</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</incomingServer>
|
||||||
|
<incomingServer type="pop3">
|
||||||
|
<hostname>mail.lakenet.ch</hostname>
|
||||||
|
<port>110</port>
|
||||||
|
<socketType>STARTTLS</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</incomingServer>
|
||||||
|
<outgoingServer type="smtp">
|
||||||
|
<hostname>mail.lakenet.ch</hostname>
|
||||||
|
<port>587</port>
|
||||||
|
<socketType>STARTTLS</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</outgoingServer>
|
||||||
|
<documentation url="https://www.lakenet.ch/">
|
||||||
|
<descr lang="it">Impostazioni per le e-mail LakeNet</descr>
|
||||||
|
<descr lang="fr">Reglages pour le courriel e-mail LakeNet</descr>
|
||||||
|
<descr lang="en">Settings for LakeNet's e-mail accounts</descr>
|
||||||
|
</documentation>
|
||||||
|
</emailProvider>
|
||||||
|
<webMail>
|
||||||
|
<loginPage url="https://lakenet.ch/webmail/" />
|
||||||
|
<loginPageInfo url="https://lakenet.ch/webmail/">
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
<usernameField id="rcmloginuser" name="_user"/>
|
||||||
|
<passwordField id="rcmloginpwd" name="_pass"/>
|
||||||
|
<loginButton id="rcmloginsubmit"/>
|
||||||
|
</loginPageInfo>
|
||||||
|
</webMail>
|
||||||
|
<clientConfigUpdate url="https://lakenet.ch/.well-known/autoconfig/mail/config-v1.1.xml" />
|
||||||
|
</clientConfig>
|
||||||
71
test-data/autoconfig/outlook.com.xml
Normal file
71
test-data/autoconfig/outlook.com.xml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<clientConfig version="1.1">
|
||||||
|
<!-- Retrieved from https://autoconfig.thunderbird.net/v1.1/outlook.com on 2019-10-11 -->
|
||||||
|
<emailProvider id="outlook.com">
|
||||||
|
<domain>hotmail.com</domain>
|
||||||
|
<domain>hotmail.co.uk</domain>
|
||||||
|
<domain>hotmail.co.jp</domain>
|
||||||
|
<domain>hotmail.com.br</domain>
|
||||||
|
<domain>hotmail.de</domain>
|
||||||
|
<domain>hotmail.fr</domain>
|
||||||
|
<domain>hotmail.it</domain>
|
||||||
|
<domain>hotmail.es</domain>
|
||||||
|
<domain>live.com</domain>
|
||||||
|
<domain>live.co.uk</domain>
|
||||||
|
<domain>live.co.jp</domain>
|
||||||
|
<domain>live.de</domain>
|
||||||
|
<domain>live.fr</domain>
|
||||||
|
<domain>live.it</domain>
|
||||||
|
<domain>live.jp</domain>
|
||||||
|
<domain>msn.com</domain>
|
||||||
|
<domain>outlook.com</domain>
|
||||||
|
<displayName>Outlook.com (Microsoft)</displayName>
|
||||||
|
<displayShortName>Outlook</displayShortName>
|
||||||
|
<incomingServer type="exchange">
|
||||||
|
<hostname>outlook.office365.com</hostname>
|
||||||
|
<port>443</port>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
<socketType>SSL</socketType>
|
||||||
|
<authentication>OAuth2</authentication>
|
||||||
|
<owaURL>https://outlook.office365.com/owa/</owaURL>
|
||||||
|
<ewsURL>https://outlook.office365.com/ews/exchange.asmx</ewsURL>
|
||||||
|
<useGlobalPreferredServer>true</useGlobalPreferredServer>
|
||||||
|
</incomingServer>
|
||||||
|
<incomingServer type="imap">
|
||||||
|
<hostname>outlook.office365.com</hostname>
|
||||||
|
<port>993</port>
|
||||||
|
<socketType>SSL</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</incomingServer>
|
||||||
|
<incomingServer type="pop3">
|
||||||
|
<hostname>outlook.office365.com</hostname>
|
||||||
|
<port>995</port>
|
||||||
|
<socketType>SSL</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
<pop3>
|
||||||
|
<leaveMessagesOnServer>true</leaveMessagesOnServer>
|
||||||
|
<!-- Outlook.com docs specifically mention that POP3 deletes have effect on the main inbox on webmail and IMAP -->
|
||||||
|
</pop3>
|
||||||
|
</incomingServer>
|
||||||
|
<outgoingServer type="smtp">
|
||||||
|
<hostname>smtp.office365.com</hostname>
|
||||||
|
<port>587</port>
|
||||||
|
<socketType>STARTTLS</socketType>
|
||||||
|
<authentication>password-cleartext</authentication>
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
</outgoingServer>
|
||||||
|
<documentation url="http://windows.microsoft.com/en-US/windows/outlook/send-receive-from-app">
|
||||||
|
<descr lang="en">Set up an email app with Outlook.com</descr>
|
||||||
|
</documentation>
|
||||||
|
</emailProvider>
|
||||||
|
<webMail>
|
||||||
|
<loginPage url="https://www.outlook.com/"/>
|
||||||
|
<loginPageInfo url="https://www.outlook.com/">
|
||||||
|
<username>%EMAILADDRESS%</username>
|
||||||
|
<usernameField id="i0116" name="login"/>
|
||||||
|
<passwordField id="i0118" name="passwd"/>
|
||||||
|
<loginButton id="idSIButton9" name="SI"/>
|
||||||
|
</loginPageInfo>
|
||||||
|
</webMail>
|
||||||
|
</clientConfig>";
|
||||||
Reference in New Issue
Block a user