1use std::{io::Read, time::Duration};
2
3use serde::{Deserialize, Serialize};
4
5use super::response::{AutodiscoverResponse, AutodiscoverResult, RedirectType};
6
7use crate::{
8 constants::{DEFAULT_MICROSOFT_REQUEST_SCHEMA, DEFAULT_MICROSOFT_RESPONSE_SCHEMA},
9 error::Result,
10};
11
12#[derive(Serialize, Deserialize, Debug)]
13#[serde(rename_all = "PascalCase")]
14pub struct Error {
15 code: usize,
16 message: String,
17 debug_data: String,
18}
19
20impl Error {
21 pub fn code(&self) -> usize {
22 self.code
23 }
24
25 pub fn message(&self) -> &str {
26 self.message.as_ref()
27 }
28
29 pub fn debug_data(&self) -> &str {
30 self.debug_data.as_ref()
31 }
32}
33
34#[derive(Serialize, Deserialize, Debug)]
35pub struct Autodiscover {
37 #[serde(rename(serialize = "@xmlns"), skip_deserializing)]
38 xmlns: String,
39 #[serde(rename = "$value")]
40 properties: Vec<ConfigProperty>,
41}
42
43impl Autodiscover {
44 pub fn create_request<E: Into<String>>(email_address: E) -> Self {
45 let req = Request::new(email_address.into());
46
47 let properties = vec![ConfigProperty::Request(req)];
48
49 Self {
50 properties,
51 xmlns: DEFAULT_MICROSOFT_REQUEST_SCHEMA.to_string(),
52 }
53 }
54
55 pub fn from_xml<R: Read>(xml: R) -> Result<Self> {
56 let config: Self = serde_xml_rs::from_reader(xml)?;
57
58 Ok(config)
61 }
62
63 fn into_response(self) -> Option<Response> {
64 for property in self.properties {
65 match property {
66 ConfigProperty::Response(response) => return Some(response),
67 _ => {}
68 }
69 }
70
71 None
72 }
73}
74
75#[derive(Serialize, Deserialize, Debug)]
76#[serde(rename_all = "PascalCase")]
77pub enum ConfigProperty {
78 Response(Response),
79 Request(Request),
80}
81
82#[derive(Serialize, Deserialize, Debug)]
83#[serde(rename_all = "PascalCase")]
84pub struct Request {
85 #[serde(rename = "$value")]
86 properties: Vec<RequestProperty>,
87}
88
89impl Request {
90 pub fn new(email_address: String) -> Self {
91 let properties = vec![
92 RequestProperty::EMailAddress(email_address),
93 RequestProperty::AcceptableResponseSchema(
94 DEFAULT_MICROSOFT_RESPONSE_SCHEMA.to_string(),
95 ),
96 ];
97
98 Self { properties }
99 }
100}
101
102#[derive(Serialize, Deserialize, Debug)]
103#[serde(rename_all = "PascalCase")]
104pub enum RequestProperty {
105 EMailAddress(String),
106 AcceptableResponseSchema(String),
107 LegacyDN(LegacyDN),
108}
109
110#[derive(Serialize, Deserialize, Debug)]
111#[serde(rename_all = "PascalCase")]
112pub struct Response {
113 #[serde(rename = "$value")]
114 properties: Vec<ResponseProperty>,
115}
116
117impl Response {
118 pub fn account(&self) -> Vec<&Account> {
119 let mut accounts = Vec::new();
120
121 for property in &self.properties {
122 match property {
123 ResponseProperty::Account(account) => accounts.push(account),
124 _ => {}
125 }
126 }
127
128 accounts
129 }
130
131 pub fn user(&self) -> Option<&User> {
132 for property in &self.properties {
133 match property {
134 ResponseProperty::User(user) => return Some(user),
135 _ => {}
136 }
137 }
138
139 None
140 }
141
142 pub fn error(&self) -> Option<&Error> {
143 for property in &self.properties {
144 match property {
145 ResponseProperty::Error(error) => return Some(error),
146 _ => {}
147 }
148 }
149
150 None
151 }
152}
153
154#[derive(Serialize, Deserialize, Debug)]
155#[serde(rename_all = "PascalCase")]
156pub enum ResponseProperty {
157 User(User),
158 Account(Account),
159 Error(Error),
160}
161
162#[derive(Serialize, Deserialize, Debug)]
163#[serde(rename_all = "PascalCase")]
164pub struct User {
165 #[serde(rename = "$value")]
166 properties: Vec<UserProperty>,
167}
168
169impl User {
170 pub fn display_name(&self) -> Option<&str> {
174 for property in &self.properties {
175 match property {
176 UserProperty::DisplayName(name) => return Some(name),
177 _ => {}
178 }
179 }
180
181 None
182 }
183
184 pub fn legacy_dn(&self) -> Option<&str> {
188 for property in &self.properties {
189 match property {
190 UserProperty::LegacyDN(name) => return Some(&name.0),
191 _ => {}
192 }
193 }
194
195 None
196 }
197
198 pub fn deployment_id(&self) -> Option<&str> {
202 for property in &self.properties {
203 match property {
204 UserProperty::DeploymentId(id) => return Some(id),
205 _ => {}
206 }
207 }
208
209 None
210 }
211
212 pub fn autodiscover_smtp_address(&self) -> Option<&str> {
216 for property in &self.properties {
217 match property {
218 UserProperty::AutoDiscoverSMTPAddress(addr) => return Some(addr),
219 _ => {}
220 }
221 }
222
223 None
224 }
225}
226
227#[derive(Serialize, Deserialize, Debug)]
228pub enum UserProperty {
229 DisplayName(String),
230 LegacyDN(LegacyDN),
231 DeploymentId(String),
232 AutoDiscoverSMTPAddress(String),
233}
234
235#[derive(Serialize, Deserialize, Debug)]
236pub struct LegacyDN(String);
237
238#[derive(Serialize, Deserialize, Debug)]
239#[serde(rename_all = "PascalCase")]
240pub struct Account {
241 #[serde(rename = "$value")]
242 properties: Vec<AccountProperty>,
243}
244
245impl Account {
246 pub fn account_type(&self) -> Option<&AccountType> {
250 for property in &self.properties {
251 match property {
252 AccountProperty::AccountType(r#type) => return Some(r#type),
253 _ => {}
254 }
255 }
256
257 None
258 }
259
260 pub fn action_type(&self) -> Option<&Action> {
264 for property in &self.properties {
265 match property {
266 AccountProperty::Action(action) => return Some(action),
267 _ => {}
268 }
269 }
270
271 None
272 }
273
274 pub fn microsoft_online(&self) -> bool {
278 for property in &self.properties {
279 match property {
280 AccountProperty::MicrosoftOnline(online) => return *online,
281 _ => {}
282 }
283 }
284
285 false
286 }
287
288 pub fn redirect_addr(&self) -> Option<&str> {
292 for property in &self.properties {
293 match property {
294 AccountProperty::RedirectAddr(addr) => return Some(addr),
295 _ => {}
296 }
297 }
298
299 None
300 }
301
302 pub fn redirect_url(&self) -> Option<&str> {
306 for property in &self.properties {
307 match property {
308 AccountProperty::RedirectUrl(url) => return Some(url),
309 _ => {}
310 }
311 }
312
313 None
314 }
315
316 pub fn image(&self) -> Option<&str> {
320 for property in &self.properties {
321 match property {
322 AccountProperty::Image(path) => return Some(path),
323 _ => {}
324 }
325 }
326
327 None
328 }
329
330 pub fn service_home(&self) -> Option<&str> {
334 for property in &self.properties {
335 match property {
336 AccountProperty::ServiceHome(url) => return Some(url),
337 _ => {}
338 }
339 }
340
341 None
342 }
343
344 pub fn protocol(&self) -> Option<&Protocol> {
348 for property in &self.properties {
349 match property {
350 AccountProperty::Protocol(proto) => return Some(proto),
351 _ => {}
352 }
353 }
354
355 None
356 }
357
358 pub fn public_folder_information(&self) -> Option<&PublicFolderInformation> {
362 for property in &self.properties {
363 match property {
364 AccountProperty::PublicFolderInformation(info) => return Some(info),
365 _ => {}
366 }
367 }
368
369 None
370 }
371
372 pub fn error(&self) -> Option<&Error> {
376 for property in &self.properties {
377 match property {
378 AccountProperty::Error(error) => return Some(error),
379 _ => {}
380 }
381 }
382
383 None
384 }
385}
386
387#[derive(Serialize, Deserialize, Debug)]
388#[serde(rename_all = "PascalCase")]
389pub enum AccountProperty {
390 AccountType(AccountType),
391 Action(Action),
392 MicrosoftOnline(bool),
393 RedirectUrl(String),
394 RedirectAddr(String),
395 Image(String),
396 ServiceHome(String),
397 Protocol(Protocol),
398 PublicFolderInformation(PublicFolderInformation),
399 Error(Error),
400}
401
402#[derive(Serialize, Deserialize, Debug)]
403#[serde(rename_all = "camelCase")]
404pub enum AccountType {
405 Email,
406}
407
408#[derive(Serialize, Deserialize, Debug)]
409#[serde(rename_all = "camelCase")]
410pub enum Action {
411 RedirectUrl,
412 RedirectAddr,
413 Settings,
414}
415
416#[derive(Serialize, Deserialize, Debug)]
417#[serde(rename_all = "PascalCase")]
418pub struct Protocol {
419 #[serde(rename = "$value")]
420 properties: Vec<ProtocolProperty>,
421}
422
423impl Protocol {
424 pub fn r#type(&self) -> Option<&Type> {
428 for property in &self.properties {
429 match property {
430 ProtocolProperty::Type(r#type) => return Some(r#type),
431 _ => {}
432 }
433 }
434
435 None
436 }
437
438 pub fn internal(&self) -> Option<&Connectivity> {
442 for property in &self.properties {
443 match property {
444 ProtocolProperty::Internal(internal) => return Some(internal),
445 _ => {}
446 }
447 }
448
449 None
450 }
451
452 pub fn external(&self) -> Option<&Connectivity> {
456 for property in &self.properties {
457 match property {
458 ProtocolProperty::External(external) => return Some(external),
459 _ => {}
460 }
461 }
462
463 None
464 }
465
466 pub fn ttl(&self) -> Duration {
470 for property in &self.properties {
471 match property {
472 ProtocolProperty::Ttl(ttl) => return Duration::from_secs(ttl * 60 * 60),
473 _ => {}
474 }
475 }
476
477 Duration::from_secs(60 * 60)
478 }
479
480 pub fn server(&self) -> Option<&str> {
484 for property in &self.properties {
485 match property {
486 ProtocolProperty::Server(server) => return Some(server),
487 _ => {}
488 }
489 }
490
491 None
492 }
493
494 pub fn server_dn(&self) -> Option<&str> {
498 for property in &self.properties {
499 match property {
500 ProtocolProperty::Server(server) => return Some(server),
501 _ => {}
502 }
503 }
504
505 None
506 }
507
508 pub fn server_version(&self) -> Option<&str> {
512 for property in &self.properties {
513 match property {
514 ProtocolProperty::ServerVersion(version) => return Some(version),
515 _ => {}
516 }
517 }
518
519 None
520 }
521
522 pub fn public_folder_server(&self) -> Option<&str> {
526 for property in &self.properties {
527 match property {
528 ProtocolProperty::PublicFolderServer(server) => return Some(server),
529 _ => {}
530 }
531 }
532
533 None
534 }
535
536 pub fn port(&self) -> Option<&u16> {
540 for property in &self.properties {
541 match property {
542 ProtocolProperty::Port(port) => return Some(port),
543 _ => {}
544 }
545 }
546
547 None
548 }
549
550 pub fn ews_url(&self) -> Option<&str> {
554 for property in &self.properties {
555 match property {
556 ProtocolProperty::EwsUrl(url) => return Some(url),
557 _ => {}
558 }
559 }
560
561 None
562 }
563
564 pub fn login_name(&self) -> Option<&str> {
568 for property in &self.properties {
569 match property {
570 ProtocolProperty::LoginName(name) => return Some(name),
571 _ => {}
572 }
573 }
574
575 None
576 }
577
578 pub fn domain_required(&self) -> Option<bool> {
582 for property in &self.properties {
583 match property {
584 ProtocolProperty::DomainRequired(required) => return Some(required.bool()),
585 _ => {}
586 }
587 }
588
589 None
590 }
591
592 pub fn domain_name(&self) -> Option<&str> {
596 for property in &self.properties {
597 match property {
598 ProtocolProperty::DomainName(name) => return Some(name),
599 _ => {}
600 }
601 }
602
603 None
604 }
605
606 pub fn spa(&self) -> bool {
610 for property in &self.properties {
611 match property {
612 ProtocolProperty::Spa(spa) => return spa.bool(),
613 _ => {}
614 }
615 }
616
617 true
618 }
619
620 pub fn auth_package(&self) -> Option<&AuthPackage> {
624 for property in &self.properties {
625 match property {
626 ProtocolProperty::AuthPackage(package) => return Some(package),
627 _ => {}
628 }
629 }
630
631 None
632 }
633
634 pub fn cert_principal_name(&self) -> &str {
638 for property in &self.properties {
639 match property {
640 ProtocolProperty::CertPrincipalName(name) => return name,
641 _ => {}
642 }
643 }
644
645 "msstd:SERVER"
646 }
647
648 pub fn ssl(&self) -> bool {
652 for property in &self.properties {
653 match property {
654 ProtocolProperty::Ssl(ssl) => return ssl.bool(),
655 _ => {}
656 }
657 }
658
659 true
660 }
661
662 pub fn use_pop_auth(&self) -> Option<bool> {
666 for property in &self.properties {
667 match property {
668 ProtocolProperty::UsePOPAuth(use_pop) => return Some(use_pop.bool()),
669 _ => {}
670 }
671 }
672
673 None
674 }
675
676 pub fn smtp_last(&self) -> bool {
680 for property in &self.properties {
681 match property {
682 ProtocolProperty::SMTPLast(last) => return last.bool(),
683 _ => {}
684 }
685 }
686
687 false
688 }
689
690 pub fn network_requirements(&self) -> Option<&NetworkRequirements> {
694 for property in &self.properties {
695 match property {
696 ProtocolProperty::NetworkRequirements(requirements) => return Some(requirements),
697 _ => {}
698 }
699 }
700
701 None
702 }
703
704 pub fn address_book(&self) -> Option<&AddressBook> {
708 for property in &self.properties {
709 match property {
710 ProtocolProperty::AddressBook(address_book) => return Some(address_book),
711 _ => {}
712 }
713 }
714
715 None
716 }
717
718 pub fn mail_store(&self) -> Option<&MailStore> {
722 for property in &self.properties {
723 match property {
724 ProtocolProperty::MailStore(store) => return Some(store),
725 _ => {}
726 }
727 }
728
729 None
730 }
731}
732
733#[derive(Serialize, Deserialize, Debug)]
734#[serde(rename_all = "PascalCase")]
735pub enum ProtocolProperty {
736 Type(Type),
737 Internal(Connectivity),
738 External(Connectivity),
739 #[serde(rename = "TTL")]
740 Ttl(u64),
741 Server(String),
742 ServerDN(String),
743 ServerVersion(String),
744 MdbDN(String),
745 PublicFolderServer(String),
746 Port(u16),
747 DirectoryPort(u16),
748 ReferralPort(u16),
749 ASUrl(String),
750 EwsUrl(String),
751 SharingUrl(String),
752 EmwsUrl(String),
753 OOFUrl(String),
754 OABUrl(String),
755 UMUrl(String),
756 EwsPartnerUrl(String),
757 LoginName(String),
758 DomainRequired(OnOff),
759 DomainName(String),
760 #[serde(rename = "SPA")]
761 Spa(OnOff),
762 AuthPackage(AuthPackage),
763 CertPrincipalName(String),
764 #[serde(rename = "SSL")]
765 Ssl(OnOff),
766 AuthRequired(OnOff),
767 UsePOPAuth(OnOff),
768 SMTPLast(OnOff),
769 NetworkRequirements(NetworkRequirements),
770 AddressBook(AddressBook),
771 MailStore(MailStore),
772 Encryption(Encryption),
773}
774
775#[derive(Serialize, Deserialize, Debug)]
776#[serde(rename_all = "UPPERCASE")]
777pub enum Type {
778 Smtp,
779 Imap,
780 Exch,
781 ExHttp,
782 Expr,
783 Web,
784}
785
786#[derive(Serialize, Deserialize, Debug)]
787#[serde(rename_all = "lowercase")]
788pub enum OnOff {
789 On,
790 Off,
791}
792
793impl OnOff {
794 pub fn bool(&self) -> bool {
795 match self {
796 OnOff::On => true,
797 OnOff::Off => false,
798 }
799 }
800}
801
802#[derive(Serialize, Deserialize, Debug)]
803#[serde(rename_all = "PascalCase")]
804pub struct Connectivity {
805 #[serde(rename = "OWAUrl")]
806 owa_url: String,
807 protocol: Protocol,
808}
809
810impl Connectivity {
811 pub fn owa_url(&self) -> &str {
815 self.owa_url.as_ref()
816 }
817
818 pub fn protocol(&self) -> &Protocol {
822 &self.protocol
823 }
824}
825
826#[derive(Serialize, Deserialize, Debug)]
827pub enum Encryption {
828 #[serde(rename = "TLS")]
829 Tls,
830 #[serde(rename = "SSL")]
831 Ssl,
832}
833
834#[derive(Serialize, Deserialize, Debug)]
835#[serde(rename_all = "lowercase")]
836pub enum AuthPackage {
837 Basic,
838 Kerb,
839 KerbNtlm,
840 Ntlm,
841 Certificate,
842 Negotiate,
843 Nego2,
844}
845
846#[derive(Serialize, Deserialize, Debug)]
847#[serde(rename_all = "PascalCase")]
848pub struct PublicFolderInformation {
849 smtp_address: String,
850}
851
852impl PublicFolderInformation {
853 pub fn smtp_address(&self) -> &str {
857 self.smtp_address.as_ref()
858 }
859}
860
861#[derive(Serialize, Deserialize, Debug)]
862#[serde(rename_all = "PascalCase")]
863pub struct AddressBook {
864 external_url: String,
865 interal_url: String,
866}
867
868#[derive(Serialize, Deserialize, Debug)]
869#[serde(rename_all = "PascalCase")]
870pub struct MailStore {
871 external_url: String,
872 interal_url: String,
873}
874
875#[derive(Serialize, Deserialize, Debug)]
876pub struct NetworkRequirements {
877 #[serde(rename = "Ipv4Start")]
878 pub ipv4_start: String,
879 #[serde(rename = "Ipv4End")]
880 pub ipv4_end: String,
881 #[serde(rename = "Ipv6Start")]
882 pub ipv6_start: String,
883 #[serde(rename = "Ipv6end")]
884 pub ipv6_end: String,
885}
886
887impl Into<AutodiscoverResult> for Autodiscover {
888 fn into(self) -> AutodiscoverResult {
889 if let Some(response) = self.into_response() {
890 for account in response.account() {
891 if let Some(action_type) = account.action_type() {
892 let redirect_type = match action_type {
893 Action::RedirectAddr => match account.redirect_addr() {
894 Some(addr) => Some(RedirectType::Email(addr.to_string())),
895 None => {
896 return AutodiscoverResult::error(
897 "Missing redirect address when it should be available",
898 )
899 }
900 },
901 Action::RedirectUrl => match account.redirect_url() {
902 Some(url) => Some(RedirectType::Url(url.to_string())),
903 None => {
904 return AutodiscoverResult::error(
905 "Missing redirect url when it should be available",
906 )
907 }
908 },
909 _ => None,
910 };
911
912 if let Some(redirect) = redirect_type {
913 return AutodiscoverResult::Redirect(redirect);
914 }
915 }
916
917 if let Some(error) = account.error() {
918 return AutodiscoverResult::Error(error.into());
919 }
920 }
921
922 if let Some(error) = response.error() {
923 return AutodiscoverResult::Error(error.into());
924 }
925
926 return AutodiscoverResult::Ok(AutodiscoverResponse::Pox(response));
927 }
928
929 AutodiscoverResult::error("Config did not contain a response value")
930 }
931}