deltachat/configure/
auto_outlook.rs1use std::io::BufRead;
7
8use quick_xml::events::Event;
9
10use super::{Error, ServerParams};
11use crate::context::Context;
12use crate::log::warn;
13use crate::net::read_url;
14use crate::provider::{Protocol, Socket};
15
16#[derive(Debug)]
20struct ProtocolTag {
21 pub typ: String,
25
26 pub server: String,
30
31 pub port: u16,
35
36 pub ssl: bool,
40}
41
42enum ParsingResult {
43 Protocols(Vec<ProtocolTag>),
44
45 RedirectUrl(String),
47}
48
49fn parse_protocol<B: BufRead>(
51 reader: &mut quick_xml::Reader<B>,
52) -> Result<Option<ProtocolTag>, quick_xml::Error> {
53 let mut protocol_type = None;
54 let mut protocol_server = None;
55 let mut protocol_port = None;
56 let mut protocol_ssl = true;
57
58 let mut buf = Vec::new();
59
60 let mut current_tag: Option<String> = None;
61 loop {
62 match reader.read_event_into(&mut buf)? {
63 Event::Start(ref event) => {
64 current_tag = Some(
65 String::from_utf8_lossy(event.name().as_ref())
66 .trim()
67 .to_lowercase(),
68 );
69 }
70 Event::End(ref event) => {
71 let tag = String::from_utf8_lossy(event.name().as_ref())
72 .trim()
73 .to_lowercase();
74 if tag == "protocol" {
75 break;
76 }
77 if Some(tag) == current_tag {
78 current_tag = None;
79 }
80 }
81 Event::Text(ref e) => {
82 let val = e.unescape().unwrap_or_default();
83
84 if let Some(ref tag) = current_tag {
85 match tag.as_str() {
86 "type" => protocol_type = Some(val.trim().to_string()),
87 "server" => protocol_server = Some(val.trim().to_string()),
88 "port" => protocol_port = Some(val.trim().parse().unwrap_or_default()),
89 "ssl" => {
90 protocol_ssl = match val.trim() {
91 "on" => true,
92 "off" => false,
93 _ => true,
94 }
95 }
96 _ => {}
97 };
98 }
99 }
100 Event::Eof => break,
101 _ => {}
102 }
103 }
104
105 if let (Some(protocol_type), Some(protocol_server), Some(protocol_port)) =
106 (protocol_type, protocol_server, protocol_port)
107 {
108 Ok(Some(ProtocolTag {
109 typ: protocol_type,
110 server: protocol_server,
111 port: protocol_port,
112 ssl: protocol_ssl,
113 }))
114 } else {
115 Ok(None)
116 }
117}
118
119fn parse_redirecturl<B: BufRead>(
121 reader: &mut quick_xml::Reader<B>,
122) -> Result<String, quick_xml::Error> {
123 let mut buf = Vec::new();
124 match reader.read_event_into(&mut buf)? {
125 Event::Text(ref e) => {
126 let val = e.unescape().unwrap_or_default();
127 Ok(val.trim().to_string())
128 }
129 _ => Ok("".to_string()),
130 }
131}
132
133fn parse_xml_reader<B: BufRead>(
134 reader: &mut quick_xml::Reader<B>,
135) -> Result<ParsingResult, quick_xml::Error> {
136 let mut protocols = Vec::new();
137
138 let mut buf = Vec::new();
139 loop {
140 match reader.read_event_into(&mut buf)? {
141 Event::Start(ref e) => {
142 let tag = String::from_utf8_lossy(e.name().as_ref())
143 .trim()
144 .to_lowercase();
145
146 if tag == "protocol" {
147 if let Some(protocol) = parse_protocol(reader)? {
148 protocols.push(protocol);
149 }
150 } else if tag == "redirecturl" {
151 let redirecturl = parse_redirecturl(reader)?;
152 return Ok(ParsingResult::RedirectUrl(redirecturl));
153 }
154 }
155 Event::Eof => break,
156 _ => (),
157 }
158 buf.clear();
159 }
160
161 Ok(ParsingResult::Protocols(protocols))
162}
163
164fn parse_xml(xml_raw: &str) -> Result<ParsingResult, Error> {
165 let mut reader = quick_xml::Reader::from_str(xml_raw);
166 reader.config_mut().trim_text(true);
167
168 parse_xml_reader(&mut reader).map_err(|error| Error::InvalidXml {
169 position: reader.buffer_position(),
170 error,
171 })
172}
173
174fn protocols_to_serverparams(protocols: Vec<ProtocolTag>) -> Vec<ServerParams> {
175 protocols
176 .into_iter()
177 .filter_map(|protocol| {
178 Some(ServerParams {
179 protocol: match protocol.typ.to_lowercase().as_ref() {
180 "imap" => Some(Protocol::Imap),
181 "smtp" => Some(Protocol::Smtp),
182 _ => None,
183 }?,
184 socket: match protocol.ssl {
185 true => Socket::Automatic,
186 false => Socket::Plain,
187 },
188 hostname: protocol.server,
189 port: protocol.port,
190 username: String::new(),
191 })
192 })
193 .collect()
194}
195
196pub(crate) async fn outlk_autodiscover(
197 context: &Context,
198 mut url: String,
199) -> Result<Vec<ServerParams>, Error> {
200 for _i in 0..10 {
202 let xml_raw = read_url(context, &url).await?;
203 let res = parse_xml(&xml_raw);
204 if let Err(err) = &res {
205 warn!(context, "{}", err);
206 }
207 match res? {
208 ParsingResult::RedirectUrl(redirect_url) => url = redirect_url,
209 ParsingResult::Protocols(protocols) => {
210 return Ok(protocols_to_serverparams(protocols));
211 }
212 }
213 }
214 Err(Error::Redirection)
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn test_parse_redirect() {
223 let res = parse_xml("
224<?xml version=\"1.0\" encoding=\"utf-8\"?>
225 <Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
226 <Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
227 <Account>
228 <AccountType>email</AccountType>
229 <Action>redirectUrl</Action>
230 <RedirectUrl>https://mail.example.com/autodiscover/autodiscover.xml</RedirectUrl>
231 </Account>
232 </Response>
233 </Autodiscover>
234 ").expect("XML is not parsed successfully");
235 if let ParsingResult::RedirectUrl(url) = res {
236 assert_eq!(
237 url,
238 "https://mail.example.com/autodiscover/autodiscover.xml"
239 );
240 } else {
241 panic!("redirecturl is not found");
242 }
243 }
244
245 #[test]
246 fn test_parse_loginparam() {
247 let res = parse_xml(
248 "\
249<?xml version=\"1.0\" encoding=\"utf-8\"?>
250<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006\">
251 <Response xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a\">
252 <Account>
253 <AccountType>email</AccountType>
254 <Action>settings</Action>
255 <Protocol>
256 <Type>IMAP</Type>
257 <Server>example.com</Server>
258 <Port>993</Port>
259 <SSL>on</SSL>
260 <AuthRequired>on</AuthRequired>
261 </Protocol>
262 <Protocol>
263 <Type>SMTP</Type>
264 <Server>smtp.example.com</Server>
265 <Port>25</Port>
266 <SSL>off</SSL>
267 <AuthRequired>on</AuthRequired>
268 </Protocol>
269 </Account>
270 </Response>
271</Autodiscover>",
272 )
273 .expect("XML is not parsed successfully");
274
275 match res {
276 ParsingResult::Protocols(protocols) => {
277 assert_eq!(protocols[0].typ, "IMAP");
278 assert_eq!(protocols[0].server, "example.com");
279 assert_eq!(protocols[0].port, 993);
280 assert_eq!(protocols[0].ssl, true);
281
282 assert_eq!(protocols[1].typ, "SMTP");
283 assert_eq!(protocols[1].server, "smtp.example.com");
284 assert_eq!(protocols[1].port, 25);
285 assert_eq!(protocols[1].ssl, false);
286 }
287 ParsingResult::RedirectUrl(_) => {
288 panic!("RedirectUrl is not expected");
289 }
290 }
291 }
292}