myl-discovery 0.5.9__py3-none-any.whl → 0.6.0__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.
- {myl_discovery-0.5.9.dist-info → myl_discovery-0.6.0.dist-info}/METADATA +14 -3
- myl_discovery-0.6.0.dist-info/RECORD +10 -0
- {myl_discovery-0.5.9.dist-info → myl_discovery-0.6.0.dist-info}/WHEEL +1 -1
- myldiscovery/discovery.py +75 -21
- myldiscovery/main.py +1 -1
- myl_discovery-0.5.9.dist-info/RECORD +0 -10
- {myl_discovery-0.5.9.dist-info → myl_discovery-0.6.0.dist-info}/LICENSE +0 -0
- {myl_discovery-0.5.9.dist-info → myl_discovery-0.6.0.dist-info}/entry_points.txt +0 -0
- {myl_discovery-0.5.9.dist-info → myl_discovery-0.6.0.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: myl-discovery
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.6.0
|
4
4
|
Summary: email autodiscovery
|
5
5
|
Author-email: Philipp Schmitt <philipp@schmitt.co>
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
@@ -678,6 +678,10 @@ License: GNU GENERAL PUBLIC LICENSE
|
|
678
678
|
Public License instead of this License. But first, please read
|
679
679
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
680
680
|
|
681
|
+
Project-URL: homepage, https://github.com/pschmitt/myl-discovery
|
682
|
+
Project-URL: documentation, https://github.com/pschmitt/myl-discovery/blob/head/readme.md
|
683
|
+
Project-URL: repository, https://github.com/pschmitt/myl-discovery
|
684
|
+
Project-URL: issues, https://github.com/pschmitt/myl-discovery/issues
|
681
685
|
Keywords: email-discovery,autodiscover,autoconfig,smtp,imap,email,discovery
|
682
686
|
Classifier: Programming Language :: Python :: 3
|
683
687
|
Requires-Python: >=3.8
|
@@ -734,12 +738,14 @@ Here's an example:
|
|
734
738
|
"imap": {
|
735
739
|
"server": "imap.yourdomain.com",
|
736
740
|
"port": 993,
|
737
|
-
"starttls": false
|
741
|
+
"starttls": false,
|
742
|
+
"ssl": false
|
738
743
|
},
|
739
744
|
"smtp": {
|
740
745
|
"server": "smtp.yourdomain.com",
|
741
746
|
"port": 587,
|
742
|
-
"starttls": true
|
747
|
+
"starttls": true,
|
748
|
+
"ssl": false
|
743
749
|
}
|
744
750
|
}
|
745
751
|
```
|
@@ -818,3 +824,8 @@ $ myl-discovery user01@gmail.com
|
|
818
824
|
## 📜 License
|
819
825
|
|
820
826
|
myl-discovery is licensed under the [GNU General Public License v3.0](LICENSE).
|
827
|
+
|
828
|
+
## 📑 Upstream docs
|
829
|
+
|
830
|
+
- https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
|
831
|
+
- https://datatracker.ietf.org/doc/html/rfc6186
|
@@ -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=eSvTFEwzJU6qf3PLhXxn1xZs9SJlPdAn5SJUMYpmcco,9195
|
4
|
+
myldiscovery/main.py,sha256=BQlblFEsPx3-JSXImWc1i2SsRED2IiEWD3cEPTszwac,2034
|
5
|
+
myl_discovery-0.6.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
6
|
+
myl_discovery-0.6.0.dist-info/METADATA,sha256=Nu0m2olCS_9eAMuuJ2ZyDcVDzoF8kUxAq9xi0ZWjGkc,45161
|
7
|
+
myl_discovery-0.6.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
8
|
+
myl_discovery-0.6.0.dist-info/entry_points.txt,sha256=nyyAyvgvu6iO9mPEA6uVrPfd0lIrUyo9AQWeH2asEY0,52
|
9
|
+
myl_discovery-0.6.0.dist-info/top_level.txt,sha256=v_h72JexaacqBNY6iOMD9PpGg8lnGoL-pkmUIzxdiVU,13
|
10
|
+
myl_discovery-0.6.0.dist-info/RECORD,,
|
myldiscovery/discovery.py
CHANGED
@@ -75,6 +75,7 @@ def autodiscover_txt(domain):
|
|
75
75
|
LOGGER.warning("Failed to resolve TXT record")
|
76
76
|
|
77
77
|
|
78
|
+
# https://wiki.mozilla.org/Thunderbird:Autoconfiguration:ConfigFileFormat
|
78
79
|
def parse_autoconfig(content):
|
79
80
|
data = xmltodict.parse(content)
|
80
81
|
|
@@ -100,11 +101,13 @@ def parse_autoconfig(content):
|
|
100
101
|
"server": imap.get("hostname"),
|
101
102
|
"port": int(imap.get("port")),
|
102
103
|
"starttls": imap.get("socketType") == "STARTTLS",
|
104
|
+
"ssl": imap.get("socketType") == "SSL",
|
103
105
|
},
|
104
106
|
"smtp": {
|
105
107
|
"server": smtp.get("hostname"),
|
106
108
|
"port": int(smtp.get("port")),
|
107
109
|
"starttls": smtp.get("socketType") == "STARTTLS",
|
110
|
+
"ssl": smtp.get("socketType") == "SSL",
|
108
111
|
},
|
109
112
|
}
|
110
113
|
|
@@ -140,19 +143,41 @@ def parse_autodiscover(content):
|
|
140
143
|
"server": imap.get("Server"),
|
141
144
|
"port": int(imap.get("Port")),
|
142
145
|
"starttls": imap.get("Encryption", "").lower() == "tls",
|
146
|
+
# FIXME Is that really the expected value for SSL?
|
147
|
+
"ssl": imap.get("Encryption", "").lower() == "ssl",
|
143
148
|
},
|
144
149
|
"smtp": {
|
145
150
|
"server": smtp.get("Server"),
|
146
151
|
"port": int(smtp.get("Port")),
|
147
152
|
"starttls": smtp.get("Encryption", "").lower() == "tls",
|
153
|
+
# FIXME Is that really the expected value for SSL?
|
154
|
+
"ssl": smtp.get("Encryption", "").lower() == "ssl",
|
148
155
|
},
|
149
156
|
}
|
150
157
|
|
151
158
|
|
159
|
+
# https://datatracker.ietf.org/doc/html/rfc6186
|
152
160
|
def autodiscover_srv(domain):
|
153
161
|
try:
|
162
|
+
# Start by looking for IMAPS and SUBMISSIONS (ie SSL)
|
163
|
+
imap_ssl = True
|
164
|
+
smtp_ssl = True
|
154
165
|
imap = resolve_srv(f"_imaps._tcp.{domain}")
|
155
|
-
smtp = resolve_srv(f"
|
166
|
+
smtp = resolve_srv(f"_submissions._tcp.{domain}")
|
167
|
+
|
168
|
+
if not imap:
|
169
|
+
LOGGER.warning("No imaps SRV found, trying imap (starttls)")
|
170
|
+
imap_ssl = False
|
171
|
+
imap = resolve_srv(f"_imap._tcp.{domain}")
|
172
|
+
if not smtp:
|
173
|
+
LOGGER.warning(
|
174
|
+
"No submissions SRV found, trying submission (starttls)"
|
175
|
+
)
|
176
|
+
imap_ssl = False
|
177
|
+
smtp = resolve_srv(f"_submission._tcp.{domain}")
|
178
|
+
|
179
|
+
imap_starttls = not imap_ssl
|
180
|
+
smtp_starttls = not smtp_ssl
|
156
181
|
|
157
182
|
assert imap is not None
|
158
183
|
assert smtp is not None
|
@@ -161,31 +186,36 @@ def autodiscover_srv(domain):
|
|
161
186
|
"imap": {
|
162
187
|
"server": imap[0].get("hostname"),
|
163
188
|
"port": int(imap[0].get("port")),
|
164
|
-
|
165
|
-
|
166
|
-
# 143 -> starttls
|
167
|
-
# 993 -> no
|
168
|
-
"starttls": False,
|
189
|
+
"starttls": imap_starttls,
|
190
|
+
"ssl": imap_ssl,
|
169
191
|
},
|
170
192
|
"smtp": {
|
171
193
|
"server": smtp[0].get("hostname"),
|
172
194
|
"port": int(smtp[0].get("port")),
|
173
|
-
|
174
|
-
|
175
|
-
# 465 -> starttls
|
176
|
-
# 587 -> no
|
177
|
-
"starttls": False,
|
195
|
+
"starttls": smtp_starttls,
|
196
|
+
"ssl": smtp_ssl,
|
178
197
|
},
|
179
198
|
}
|
180
199
|
except Exception as e:
|
181
200
|
LOGGER.warning(f"Failed to resolve SRV records: {e}")
|
182
201
|
|
183
202
|
|
184
|
-
def port_check(host, port):
|
203
|
+
def port_check(host, port, timeout=5.0):
|
185
204
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
186
|
-
|
187
|
-
|
188
|
-
|
205
|
+
sock.settimeout(float(timeout))
|
206
|
+
try:
|
207
|
+
result = sock.connect_ex((host, port))
|
208
|
+
return result == 0
|
209
|
+
except socket.timeout:
|
210
|
+
LOGGER.warning(
|
211
|
+
f"Connection to {host}:{port} timed out after {timeout} seconds."
|
212
|
+
)
|
213
|
+
return False
|
214
|
+
except socket.error as e:
|
215
|
+
LOGGER.warning(f"Socket error occurred: {e}")
|
216
|
+
return False
|
217
|
+
finally:
|
218
|
+
sock.close()
|
189
219
|
|
190
220
|
|
191
221
|
def check_email_ports(host):
|
@@ -212,32 +242,40 @@ def autodiscover_port_scan(server):
|
|
212
242
|
portscan = check_email_ports(server)
|
213
243
|
LOGGER.info(f"Port scan results: {portscan}")
|
214
244
|
|
215
|
-
imap_port = imap_starttls = None
|
245
|
+
imap_port = imap_starttls = imap_ssl = None
|
216
246
|
if portscan.get(993):
|
217
247
|
imap_port = 993
|
218
|
-
|
248
|
+
imap_ssl = True
|
219
249
|
elif portscan.get(143):
|
220
250
|
imap_port = 143
|
221
|
-
|
251
|
+
imap_ssl = False
|
222
252
|
|
223
|
-
smtp_port = smtp_starttls = None
|
253
|
+
smtp_port = smtp_starttls = smtp_ssl = None
|
224
254
|
if portscan.get(465):
|
225
255
|
smtp_port = 465
|
226
|
-
|
256
|
+
smtp_ssl = True
|
227
257
|
elif portscan.get(587):
|
228
258
|
smtp_port = 587
|
229
|
-
|
259
|
+
smtp_ssl = False
|
260
|
+
elif portscan.get(25):
|
261
|
+
smtp_port = 25
|
262
|
+
smtp_ssl = False
|
263
|
+
|
264
|
+
imap_starttls = not imap_ssl if imap_ssl is not None else None
|
265
|
+
smtp_starttls = not smtp_ssl if smtp_ssl is not None else None
|
230
266
|
|
231
267
|
return {
|
232
268
|
"imap": {
|
233
269
|
"server": server,
|
234
270
|
"port": imap_port,
|
235
271
|
"starttls": imap_starttls,
|
272
|
+
"ssl": imap_ssl,
|
236
273
|
},
|
237
274
|
"smtp": {
|
238
275
|
"server": server,
|
239
276
|
"port": smtp_port,
|
240
277
|
"starttls": smtp_starttls,
|
278
|
+
"ssl": smtp_ssl,
|
241
279
|
},
|
242
280
|
}
|
243
281
|
|
@@ -263,6 +301,22 @@ def autodiscover(email_addr, username=None, password=None):
|
|
263
301
|
domain = email_addr.split("@")[-1]
|
264
302
|
if not domain:
|
265
303
|
raise ValueError(f"Invalid email address {email_addr}")
|
304
|
+
if domain == "gmail.com":
|
305
|
+
LOGGER.debug("Gmail detected, skipping autodiscover")
|
306
|
+
return {
|
307
|
+
"imap": {
|
308
|
+
"server": "imap.gmail.com",
|
309
|
+
"port": 993,
|
310
|
+
"starttls": False,
|
311
|
+
"ssl": True,
|
312
|
+
},
|
313
|
+
"smtp": {
|
314
|
+
"server": "smtp.gmail.com",
|
315
|
+
"port": 465,
|
316
|
+
"starttls": False,
|
317
|
+
"ssl": True,
|
318
|
+
},
|
319
|
+
}
|
266
320
|
|
267
321
|
res = autodiscover_autoconfig(domain)
|
268
322
|
|
myldiscovery/main.py
CHANGED
@@ -1,10 +0,0 @@
|
|
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=wuo-kxoqsgX0r1ebrr86P4WNJJOhpemR7w5JT4F-ecw,2023
|
5
|
-
myl_discovery-0.5.9.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
6
|
-
myl_discovery-0.5.9.dist-info/METADATA,sha256=2dbX7hlBDwKjkUdKU1TE6J1QXh3SsmRTLdlioWx8oLk,44687
|
7
|
-
myl_discovery-0.5.9.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
8
|
-
myl_discovery-0.5.9.dist-info/entry_points.txt,sha256=nyyAyvgvu6iO9mPEA6uVrPfd0lIrUyo9AQWeH2asEY0,52
|
9
|
-
myl_discovery-0.5.9.dist-info/top_level.txt,sha256=v_h72JexaacqBNY6iOMD9PpGg8lnGoL-pkmUIzxdiVU,13
|
10
|
-
myl_discovery-0.5.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|