myl-discovery 0.5.2__py3-none-any.whl → 0.5.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: myl-discovery
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: email autodiscovery
5
5
  Author-email: Philipp Schmitt <philipp@schmitt.co>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -689,130 +689,92 @@ Requires-Dist: requests (==2.31.0)
689
689
  Requires-Dist: rich (==13.4.2)
690
690
  Requires-Dist: xmltodict (==0.13.0)
691
691
 
692
- # myl-discovery
692
+ # 📩 myl-discovery
693
693
 
694
- Email autoconfig library
694
+ myl-discovery is a Python library designed to detect email settings of a given
695
+ email address or domain.
695
696
 
696
- ## Installation
697
+ ## 📥 Installation
697
698
 
698
- ```shell
699
+ To install myl-discovery, run the following command:
700
+
701
+ ```bash
699
702
  pip install myl-discovery
700
703
  ```
701
704
 
702
- ## Usage
705
+ ## 📖 Usage
706
+
707
+ After installing the package, you can use the `autodiscover` function to
708
+ discover the email settings for a domain. Here's an example:
703
709
 
704
710
  ```python
705
711
  from myldiscovery import autodiscover
706
- autodiscover("me@example.com")
707
- # {'imap': {'server': 'mail.example.com', 'port': 993, 'starttls': False},
708
- # 'smtp': {'server': 'mail.example.com', 'port': 587, 'starttls': False}}
709
- ```
710
-
711
712
 
712
- ## Development
713
+ settings = autodiscover("yourdomain.com") # or me@yourdomain.com
714
+ print(settings)
713
715
 
714
- ### Autodiscovery
715
-
716
- #### autoconfig
717
-
718
- ```shell
719
- curl -L https://mail.example.com/mail/config-v1.1.xml
716
+ # For Exchange autodiscovery you need to provide credentials
717
+ settings = autodiscover(
718
+ 'me@yourdomain.com',
719
+ username='WORKGROUP\me',
720
+ password='mypassword1234'
721
+ )
720
722
  ```
721
723
 
722
- Response:
723
-
724
- ```xml
725
- <?xml version="1.0" encoding="UTF-8"?>
726
- <clientConfig version="1.1">
727
- <emailProvider id="example.com">
728
- <domain>example.com</domain>
724
+ ## 📄 Output
729
725
 
730
- <displayName>example.com Email</displayName>
731
- <displayShortName>%EMAILLOCALPART%</displayShortName>
732
- <incomingServer type="imap">
733
- <hostname>mail.example.com</hostname>
734
- <port>143</port>
735
- <socketType>STARTTLS</socketType>
736
- <authentication>password-cleartext</authentication>
737
- <username>%EMAILADDRESS%</username>
738
- </incomingServer>
739
- <outgoingServer type="smtp">
740
- <hostname>mail.example.com</hostname>
741
- <port>587</port>
742
- <socketType>STARTTLS</socketType>
743
- <authentication>password-cleartext</authentication>
744
- <username>%EMAILADDRESS%</username>
745
- </outgoingServer>
746
- <documentation url="https://autodiscover.example.com">
747
- <descr lang="en">Generic settings page</descr>
748
- <descr lang="fr">Paramètres généraux</descr>
749
- <descr lang="es">Configuraciones genéricas</descr>
750
- <descr lang="de">Allgemeine Beschreibung der Einstellungen</descr>
751
- <descr lang="ru">Страница общих настроек</descr>
752
- </documentation>
753
- </emailProvider>
754
- </clientConfig>
755
- ```
726
+ The `autodiscover` function returns a dictionary with the detected settings.
727
+ The dictionary contains two keys, `imap` and `smtp`, each containing a
728
+ dictionary with the keys `server`, `port`, and `starttls`.
756
729
 
757
- ### autodiscover
730
+ Here's an example:
758
731
 
759
- ```shell
760
- curl -L mail.example.com/autodiscover/autodiscover.xml
732
+ ```json
733
+ {
734
+ "imap": {
735
+ "server": "imap.yourdomain.com",
736
+ "port": 993,
737
+ "starttls": false
738
+ },
739
+ "smtp": {
740
+ "server": "smtp.yourdomain.com",
741
+ "port": 587,
742
+ "starttls": true
743
+ }
744
+ }
761
745
  ```
762
746
 
763
- Response:
764
-
765
- ```xml
766
- <?xml version="1.0" encoding="utf-8" ?>
767
- <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
768
- <Response xmlns="http://schemas.microsoft.com/exchange/autodiscover/responseschema/2006">
769
- <User>
770
- <DisplayName>example.com Email</DisplayName>
771
- </User>
772
- <Account>
773
- <AccountType>email</AccountType>
774
- <Action>settings</Action>
775
- <ServiceHome>https://autodiscover.example.com</ServiceHome>
776
-
777
- <Protocol>
778
- <Type>IMAP</Type>
779
- <TTL>1</TTL>
780
-
781
- <Server>mail.example.com</Server>
782
- <Port>143</Port>
747
+ ## 🧩 Autodiscover Functions
783
748
 
784
- <LoginName></LoginName>
749
+ myl-discovery exposes several functions to discover email settings:
785
750
 
786
- <DomainRequired>on</DomainRequired>
787
- <DomainName>example.com</DomainName>
751
+ - `autodiscover`: This function wraps the below function do automatically detect
752
+ the right settings. (See Autodiscover strategy for more information)
753
+ - `autodiscover_srv`: This function attempts to resolve SRV records for
754
+ the domain to discover IMAP and SMTP servers.
755
+ - `autodiscover_exchange`: This function attempts to use the Exchange
756
+ Autodiscover service to discover email settings. It requires a username and
757
+ password.
758
+ - `autodiscover_autoconfig`: This function attempts to fetch and parse an
759
+ autoconfig XML file from a URL specified in the domain's TXT records.
760
+ - `autodiscover_port_scan`: This function performs a port scan on the domain
761
+ to discover open IMAP and SMTP ports.
788
762
 
789
- <SPA>off</SPA>
790
- <Encryption>TLS</Encryption>
791
- <AuthRequired>on</AuthRequired>
792
- </Protocol>
793
- </Account>
794
- <Account>
795
- <AccountType>email</AccountType>
796
- <Action>settings</Action>
797
- <ServiceHome>https://autodiscover.example.com</ServiceHome>
763
+ ## 🧠 Autodiscover Strategy
798
764
 
799
- <Protocol>
800
- <Type>SMTP</Type>
801
- <TTL>1</TTL>
765
+ The `autodiscover` function uses the following strategy to discover
766
+ email settings:
802
767
 
803
- <Server>mail.example.com</Server>
804
- <Port>587</Port>
768
+ 1. It first attempts to use `autodiscover_autoconfig` to discover settings
769
+ from an autoconfig/autodiscover URL specified in the domain's TXT records.
770
+ 2. If that fails, it attempts to use `autodiscover_srv` to discover settings
771
+ from the domain's SRV records.
772
+ 3. If that fails and a password is provided, it attempts to use
773
+ `autodiscover_exchange` to discover settings using the
774
+ Exchange Autodiscover service (only if credentials were provided)
775
+ 4. If all else fails, it uses `autodiscover_port_scan` to discover settings by
776
+ performing a port scan on the domain.
805
777
 
806
- <LoginName></LoginName>
807
-
808
- <DomainRequired>on</DomainRequired>
809
- <DomainName>example.com</DomainName>
810
-
811
- <SPA>off</SPA>
812
- <Encryption>TLS</Encryption>
813
- <AuthRequired>on</AuthRequired>
814
- </Protocol>
815
- </Account></Response>
816
- </Autodiscover>
817
- ```
778
+ ## 📜 License
818
779
 
780
+ myl-discovery is licensed under the [GNU General Public License v3.0](LICENSE).
@@ -0,0 +1,10 @@
1
+ myldiscovery/__init__.py,sha256=L_XVC06ZVdjhnV5up1MBnywTuGUIyjt8PUoQDUOzfAk,381
2
+ myldiscovery/__main__.py,sha256=5BjNuyet8AY-POwoF5rGt722rHQ7tJ0Vf0UFUfzzi-I,58
3
+ myldiscovery/discovery.py,sha256=YnEJ54CdwaA6PluMSC0ba7uGhoM__-ejtPTCawa9cto,7358
4
+ myldiscovery/main.py,sha256=dBvJULlnULiJOCtios2hF1A2lNu6EWMKpTSjC9fs2Ss,2016
5
+ myl_discovery-0.5.4.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
+ myl_discovery-0.5.4.dist-info/METADATA,sha256=Bj7FHNAlBwBWSDj3JF6-enWBXe-dpzbszClTWe9n7tk,43775
7
+ myl_discovery-0.5.4.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
8
+ myl_discovery-0.5.4.dist-info/entry_points.txt,sha256=nyyAyvgvu6iO9mPEA6uVrPfd0lIrUyo9AQWeH2asEY0,52
9
+ myl_discovery-0.5.4.dist-info/top_level.txt,sha256=v_h72JexaacqBNY6iOMD9PpGg8lnGoL-pkmUIzxdiVU,13
10
+ myl_discovery-0.5.4.dist-info/RECORD,,
myldiscovery/__init__.py CHANGED
@@ -1,4 +1,19 @@
1
- from .discovery import autodiscover, autodiscover_exchange
1
+ from .discovery import (
2
+ autodiscover,
3
+ autodiscover_autoconfig,
4
+ autodiscover_exchange,
5
+ autodiscover_port_scan,
6
+ autodiscover_srv,
7
+ autodiscover_txt,
8
+ )
2
9
  from .main import main
3
10
 
4
- __all__ = ["main", "autodiscover", "autodiscover_exchange"]
11
+ __all__ = [
12
+ "main",
13
+ "autodiscover",
14
+ "autodiscover_autoconfig",
15
+ "autodiscover_exchange",
16
+ "autodiscover_port_scan",
17
+ "autodiscover_srv",
18
+ "autodiscover_txt",
19
+ ]
myldiscovery/discovery.py CHANGED
@@ -66,10 +66,13 @@ def resolve_srv(domain):
66
66
 
67
67
 
68
68
  def autodiscover_txt(domain):
69
- res = resolve_txt(domain, criteria="^mailconf=")
70
- if not res:
71
- return
72
- return res.split("=")[1]
69
+ try:
70
+ res = resolve_txt(domain, criteria="^mailconf=")
71
+ if not res:
72
+ return
73
+ return res.split("=")[1]
74
+ except Exception:
75
+ LOGGER.warning("Failed to resolve TXT record")
73
76
 
74
77
 
75
78
  def parse_autoconfig(content):
@@ -147,29 +150,35 @@ def parse_autodiscover(content):
147
150
 
148
151
 
149
152
  def autodiscover_srv(domain):
150
- imap = resolve_srv(f"_imaps._tcp.{domain}")
151
- smtp = resolve_srv(f"_submission._tcp.{domain}")
152
-
153
- return {
154
- "imap": {
155
- "server": imap[0].get("hostname"),
156
- "port": int(imap[0].get("port")),
157
- # FIXME We might want to "smartly" guess if starttls should be
158
- # enabled or not, depending on the port:
159
- # 143 -> starttls
160
- # 993 -> no
161
- "starttls": False,
162
- },
163
- "smtp": {
164
- "server": smtp[0].get("hostname"),
165
- "port": int(smtp[0].get("port")),
166
- # FIXME We might want to "smartly" guess if starttls should be
167
- # enabled or not, depending on the port:
168
- # 465 -> starttls
169
- # 587 -> no
170
- "starttls": False,
171
- },
172
- }
153
+ try:
154
+ imap = resolve_srv(f"_imaps._tcp.{domain}")
155
+ smtp = resolve_srv(f"_submission._tcp.{domain}")
156
+
157
+ assert imap is not None
158
+ assert smtp is not None
159
+
160
+ return {
161
+ "imap": {
162
+ "server": imap[0].get("hostname"),
163
+ "port": int(imap[0].get("port")),
164
+ # FIXME We might want to "smartly" guess if starttls should be
165
+ # enabled or not, depending on the port:
166
+ # 143 -> starttls
167
+ # 993 -> no
168
+ "starttls": False,
169
+ },
170
+ "smtp": {
171
+ "server": smtp[0].get("hostname"),
172
+ "port": int(smtp[0].get("port")),
173
+ # FIXME We might want to "smartly" guess if starttls should be
174
+ # enabled or not, depending on the port:
175
+ # 465 -> starttls
176
+ # 587 -> no
177
+ "starttls": False,
178
+ },
179
+ }
180
+ except Exception as e:
181
+ LOGGER.warning(f"Failed to resolve SRV records: {e}")
173
182
 
174
183
 
175
184
  def port_check(host, port):
@@ -187,14 +196,19 @@ def check_email_ports(host):
187
196
 
188
197
 
189
198
  def autodiscover_exchange(email, password, username=None):
190
- if not username:
191
- username = email
192
- creds = Credentials(username=username, password=password)
193
- account = Account(
194
- primary_smtp_address=email, credentials=creds, autodiscover=True
195
- )
196
- server = account.protocol.server
197
-
199
+ try:
200
+ if not username:
201
+ username = email
202
+ creds = Credentials(username=username, password=password)
203
+ account = Account(
204
+ primary_smtp_address=email, credentials=creds, autodiscover=True
205
+ )
206
+ return autodiscover_port_scan(account.protocol.server)
207
+ except Exception as e:
208
+ LOGGER.warning(f"Failed to autodiscover Exchange: {e}")
209
+
210
+
211
+ def autodiscover_port_scan(server):
198
212
  portscan = check_email_ports(server)
199
213
  LOGGER.info(f"Port scan results: {portscan}")
200
214
 
@@ -228,27 +242,12 @@ def autodiscover_exchange(email, password, username=None):
228
242
  }
229
243
 
230
244
 
231
- def autodiscover(email_addr, srv_only=False, username=None, password=None):
232
- domain = email_addr.split("@")[-1]
233
- if not domain:
234
- raise ValueError(f"Invalid email address {email_addr}")
235
-
236
- if srv_only:
237
- return autodiscover_srv(domain)
238
-
245
+ def autodiscover_autoconfig(domain):
239
246
  autoconfig = autodiscover_txt(domain)
240
247
 
241
248
  if not autoconfig:
242
- try:
243
- return autodiscover_srv(domain)
244
- except Exception:
245
- LOGGER.warning("Failed to autodiscover using SRV records")
246
- if password:
247
- LOGGER.info("Trying autodiscover using Exchange credentials")
248
- return autodiscover_exchange(
249
- email=email_addr, username=username, password=password
250
- )
251
- return
249
+ LOGGER.warning("Failed to autodiscover using TXT records")
250
+ return
252
251
 
253
252
  res = requests.get(autoconfig)
254
253
  res.raise_for_status()
@@ -258,3 +257,25 @@ def autodiscover(email_addr, srv_only=False, username=None, password=None):
258
257
  except Exception:
259
258
  LOGGER.warning("Failed to parse autoconfig, trying autodiscover")
260
259
  return parse_autodiscover(res.text)
260
+
261
+
262
+ def autodiscover(email_addr, username=None, password=None):
263
+ domain = email_addr.split("@")[-1]
264
+ if not domain:
265
+ raise ValueError(f"Invalid email address {email_addr}")
266
+
267
+ res = autodiscover_autoconfig(domain)
268
+
269
+ if not res:
270
+ res = autodiscover_srv(domain)
271
+
272
+ if not res and password:
273
+ res = autodiscover_srv(domain)
274
+ autodiscover_exchange(
275
+ email=email_addr, username=username, password=password
276
+ )
277
+
278
+ if not res:
279
+ res = autodiscover_port_scan(domain)
280
+
281
+ return res
@@ -1,10 +0,0 @@
1
- myldiscovery/__init__.py,sha256=uYjN41LLdBJtkSU1JOn_Ryi6jxiN0D99WPVrhIvQt-Q,143
2
- myldiscovery/__main__.py,sha256=5BjNuyet8AY-POwoF5rGt722rHQ7tJ0Vf0UFUfzzi-I,58
3
- myldiscovery/discovery.py,sha256=wuV_9-Hmomt-g_iIJ2mqecR1gYXsbabgklcZSdNO820,6833
4
- myldiscovery/main.py,sha256=dBvJULlnULiJOCtios2hF1A2lNu6EWMKpTSjC9fs2Ss,2016
5
- myl_discovery-0.5.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
- myl_discovery-0.5.2.dist-info/METADATA,sha256=LlbCj0P2mWvjqUx73LtYl9o-k5aHtEROeU6bhyMKDSk,44965
7
- myl_discovery-0.5.2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
8
- myl_discovery-0.5.2.dist-info/entry_points.txt,sha256=nyyAyvgvu6iO9mPEA6uVrPfd0lIrUyo9AQWeH2asEY0,52
9
- myl_discovery-0.5.2.dist-info/top_level.txt,sha256=v_h72JexaacqBNY6iOMD9PpGg8lnGoL-pkmUIzxdiVU,13
10
- myl_discovery-0.5.2.dist-info/RECORD,,