PCQRSCANER/venv/Lib/site-packages/imapclient/config.py

203 lines
6.1 KiB
Python
Raw Normal View History

2019-12-22 21:51:47 +01:00
# Copyright (c) 2015, Menno Smits
# Released subject to the New BSD License
# Please see http://en.wikipedia.org/wiki/BSD_licenses
from __future__ import unicode_literals
import json
from os import environ, path
import ssl
from six import iteritems
from six.moves.configparser import SafeConfigParser, NoOptionError
from six.moves.urllib.request import urlopen
from six.moves.urllib.parse import urlencode
import imapclient
def getenv(name, default):
return environ.get("imapclient_"+name, default)
def get_config_defaults():
return dict(
username=getenv("username", None),
password=getenv("password", None),
ssl=True,
ssl_check_hostname=True,
ssl_verify_cert=True,
ssl_ca_file=None,
timeout=None,
starttls=False,
stream=False,
oauth2=False,
oauth2_client_id=getenv("oauth2_client_id", None),
oauth2_client_secret=getenv("oauth2_client_secret", None),
oauth2_refresh_token=getenv("oauth2_refresh_token", None),
expect_failure=None,
)
def parse_config_file(filename):
"""Parse INI files containing IMAP connection details.
Used by livetest.py and interact.py
"""
parser = SafeConfigParser(get_string_config_defaults())
with open(filename, 'r') as fh:
parser.readfp(fh)
conf = _read_config_section(parser, "DEFAULT")
if conf.expect_failure:
raise ValueError("expect_failure should not be set for the DEFAULT section")
conf.alternates = {}
for section in parser.sections():
conf.alternates[section] = _read_config_section(parser, section)
return conf
def get_string_config_defaults():
out = {}
for k, v in iteritems(get_config_defaults()):
if v is True:
v = 'true'
elif v is False:
v = 'false'
out[k] = v
return out
def _read_config_section(parser, section):
get = lambda name: parser.get(section, name)
getboolean = lambda name: parser.getboolean(section, name)
def get_allowing_none(name, typefunc):
try:
v = parser.get(section, name)
except NoOptionError:
return None
if not v:
return None
return typefunc(v)
def getint(name):
return get_allowing_none(name, int)
def getfloat(name):
return get_allowing_none(name, float)
ssl_ca_file = get('ssl_ca_file')
if ssl_ca_file:
ssl_ca_file = path.expanduser(ssl_ca_file)
return Bunch(
host=get('host'),
port=getint('port'),
ssl=getboolean('ssl'),
starttls=getboolean('starttls'),
ssl_check_hostname=getboolean('ssl_check_hostname'),
ssl_verify_cert=getboolean('ssl_verify_cert'),
ssl_ca_file=ssl_ca_file,
timeout=getfloat('timeout'),
stream=getboolean('stream'),
username=get('username'),
password=get('password'),
oauth2=getboolean('oauth2'),
oauth2_client_id=get('oauth2_client_id'),
oauth2_client_secret=get('oauth2_client_secret'),
oauth2_refresh_token=get('oauth2_refresh_token'),
expect_failure=get('expect_failure')
)
OAUTH2_REFRESH_URLS = {
'imap.gmail.com': 'https://accounts.google.com/o/oauth2/token',
'imap.mail.yahoo.com': "https://api.login.yahoo.com/oauth2/get_token",
}
def refresh_oauth2_token(hostname, client_id, client_secret, refresh_token):
url = OAUTH2_REFRESH_URLS.get(hostname)
if not url:
raise ValueError("don't know where to refresh OAUTH2 token for %r" % hostname)
post = dict(client_id=client_id.encode('ascii'),
client_secret=client_secret.encode('ascii'),
refresh_token=refresh_token.encode('ascii'),
grant_type=b'refresh_token')
response = urlopen(url, urlencode(post).encode('ascii')).read()
return json.loads(response.decode('ascii'))['access_token']
# Tokens are expensive to refresh so use the same one for the duration of the process.
_oauth2_cache = {}
def get_oauth2_token(hostname, client_id, client_secret, refresh_token):
cache_key = (hostname, client_id, client_secret, refresh_token)
token = _oauth2_cache.get(cache_key)
if token:
return token
token = refresh_oauth2_token(hostname, client_id, client_secret, refresh_token)
_oauth2_cache[cache_key] = token
return token
def create_client_from_config(conf, login=True):
assert conf.host, "missing host"
ssl_context = None
if conf.ssl:
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = conf.ssl_check_hostname
if not conf.ssl_verify_cert:
ssl_context.verify_mode = ssl.CERT_NONE
if conf.ssl_ca_file:
ssl_context.load_verify_locations(cafile=conf.ssl_ca_file)
client = imapclient.IMAPClient(conf.host, port=conf.port,
ssl=conf.ssl,
ssl_context=ssl_context,
stream=conf.stream,
timeout=conf.timeout)
if not login:
return client
try:
if conf.starttls:
client.starttls()
if conf.oauth2:
assert conf.oauth2_client_id, "missing oauth2 id"
assert conf.oauth2_client_secret, "missing oauth2 secret"
assert conf.oauth2_refresh_token, "missing oauth2 refresh token"
access_token = get_oauth2_token(conf.host,
conf.oauth2_client_id,
conf.oauth2_client_secret,
conf.oauth2_refresh_token)
client.oauth2_login(conf.username, access_token)
elif not conf.stream:
assert conf.username, "missing username"
assert conf.password, "missing password"
client.login(conf.username, conf.password)
return client
except:
client.shutdown()
raise
class Bunch(dict):
def __getattr__(self, k):
try:
return self[k]
except KeyError:
raise AttributeError
def __setattr__(self, k, v):
self[k] = v