diff --git a/src/configure/auto_outlook.rs b/src/configure/auto_outlook.rs index ff6a4bc61..36818f3e1 100644 --- a/src/configure/auto_outlook.rs +++ b/src/configure/auto_outlook.rs @@ -3,6 +3,7 @@ use quick_xml::events::BytesEnd; use crate::constants::*; use crate::context::Context; +use crate::error::Error; use crate::login_param::LoginParam; use super::read_autoconf_file; @@ -19,6 +20,100 @@ struct OutlookAutodiscover { pub config_redirecturl: Option, } +enum ParsingResult { + LoginParam(LoginParam), + RedirectUrl(String), +} + +fn outlk_parse_xml(xml_raw: &str) -> Result { + let mut outlk_ad = OutlookAutodiscover { + out: LoginParam::new(), + out_imap_set: false, + out_smtp_set: false, + config_type: None, + config_server: String::new(), + config_port: 0, + config_ssl: String::new(), + config_redirecturl: None, + }; + + let mut reader = quick_xml::Reader::from_str(&xml_raw); + reader.trim_text(true); + + let mut buf = Vec::new(); + + let mut current_tag: Option = None; + + loop { + 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" { + outlk_ad.config_type = None; + outlk_ad.config_server = String::new(); + outlk_ad.config_port = 0; + outlk_ad.config_ssl = String::new(); + outlk_ad.config_redirecturl = None; + + current_tag = None; + } else { + current_tag = Some(tag); + } + } + Ok(quick_xml::events::Event::End(ref e)) => { + outlk_autodiscover_endtag_cb(e, &mut outlk_ad); + current_tag = None; + } + 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 { + match tag.as_str() { + "type" => { + outlk_ad.config_type = Some(val.trim().to_lowercase().to_string()) + } + "server" => outlk_ad.config_server = val.trim().to_string(), + "port" => outlk_ad.config_port = val.trim().parse().unwrap_or_default(), + "ssl" => outlk_ad.config_ssl = val.trim().to_string(), + "redirecturl" => outlk_ad.config_redirecturl = Some(val.trim().to_string()), + _ => {} + }; + } + } + 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 + if outlk_ad.config_redirecturl.is_none() + || outlk_ad.config_redirecturl.as_ref().unwrap().is_empty() + { + if outlk_ad.out.mail_server.is_empty() + || outlk_ad.out.mail_port == 0 + || outlk_ad.out.send_server.is_empty() + || outlk_ad.out.send_port == 0 + { + let r = outlk_ad.out.to_string(); + bail!("Bad or incomplete autoconfig: {}", r,); + } + Ok(ParsingResult::LoginParam(outlk_ad.out)) + } else { + Ok(ParsingResult::RedirectUrl( + outlk_ad.config_redirecturl.unwrap(), + )) + } +} + pub fn outlk_autodiscover( context: &Context, url: &str, @@ -27,94 +122,16 @@ pub fn outlk_autodiscover( let mut url = url.to_string(); /* Follow up to 10 xml-redirects (http-redirects are followed in read_autoconf_file() */ for _i in 0..10 { - let mut outlk_ad = OutlookAutodiscover { - out: LoginParam::new(), - out_imap_set: false, - out_smtp_set: false, - config_type: None, - config_server: String::new(), - config_port: 0, - config_ssl: String::new(), - config_redirecturl: None, - }; - if let Some(xml_raw) = read_autoconf_file(context, &url) { - let mut reader = quick_xml::Reader::from_str(&xml_raw); - reader.trim_text(true); - - let mut buf = Vec::new(); - - let mut current_tag: Option = None; - - loop { - 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" { - outlk_ad.config_type = None; - outlk_ad.config_server = String::new(); - outlk_ad.config_port = 0; - outlk_ad.config_ssl = String::new(); - outlk_ad.config_redirecturl = None; - - current_tag = None; - } else { - current_tag = Some(tag); - } - } - Ok(quick_xml::events::Event::End(ref e)) => { - outlk_autodiscover_endtag_cb(e, &mut outlk_ad); - current_tag = None; - } - 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 { - match tag.as_str() { - "type" => outlk_ad.config_type = Some(val.trim().to_string()), - "server" => outlk_ad.config_server = val.trim().to_string(), - "port" => { - outlk_ad.config_port = val.trim().parse().unwrap_or_default() - } - "ssl" => outlk_ad.config_ssl = val.trim().to_string(), - "redirecturl" => { - outlk_ad.config_redirecturl = Some(val.trim().to_string()) - } - _ => {} - }; - } - } - Err(e) => { - error!( - context, - "Configure xml: Error at position {}: {:?}", - reader.buffer_position(), - e - ); - } - Ok(quick_xml::events::Event::Eof) => break, - _ => (), - } - buf.clear(); - } - - // XML redirect via redirecturl - if outlk_ad.config_redirecturl.is_none() - || outlk_ad.config_redirecturl.as_ref().unwrap().is_empty() - { - if outlk_ad.out.mail_server.is_empty() - || outlk_ad.out.mail_port == 0 - || outlk_ad.out.send_server.is_empty() - || outlk_ad.out.send_port == 0 - { - let r = outlk_ad.out.to_string(); - warn!(context, "Bad or incomplete autoconfig: {}", r,); + match outlk_parse_xml(&xml_raw) { + Err(err) => { + warn!(context, "{}", err); return None; } - return Some(outlk_ad.out); - } else { - url = outlk_ad.config_redirecturl.unwrap(); + Ok(res) => match res { + ParsingResult::RedirectUrl(redirect_url) => url = redirect_url, + ParsingResult::LoginParam(login_param) => return Some(login_param), + }, } } else { return None; @@ -155,3 +172,78 @@ fn outlk_autodiscover_endtag_cb(event: &BytesEnd, outlk_ad: &mut OutlookAutodisc } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_redirect() { + let res = outlk_parse_xml(" + + + + + email + redirectUrl + https://mail.example.com/autodiscover/autodiscover.xml + + + + ").expect("XML is not parsed successfully"); + match res { + ParsingResult::LoginParam(_lp) => { + panic!("redirecturl is not found"); + } + ParsingResult::RedirectUrl(url) => { + assert_eq!( + url, + "https://mail.example.com/autodiscover/autodiscover.xml" + ); + } + } + } + + #[test] + fn test_parse_loginparam() { + let res = outlk_parse_xml( + "\ + + + + + email + settings + + IMAP + example.com + 993 + on + on + + + SMTP + smtp.example.com + 25 + off + on + + + +", + ) + .expect("XML is not parsed successfully"); + + match res { + ParsingResult::LoginParam(lp) => { + assert_eq!(lp.mail_server, "example.com"); + assert_eq!(lp.mail_port, 993); + assert_eq!(lp.send_server, "smtp.example.com"); + assert_eq!(lp.send_port, 25); + } + ParsingResult::RedirectUrl(_) => { + panic!("RedirectUrl is not expected"); + } + } + } +}