def test_imap(user): credentials = get_credentials() conn = IMAPClient('imap.googlemail.com', use_uid=True, ssl=True) # conn.debug = 4 conn.oauth2_login(user, credentials.access_token) # status, labels = conn.list() folders = conn.list_folders() try: all_box = next(box for (flags, _, box) in folders if '\All' in flags) except StopIteration: raise Error('all message box not found') logging.debug('All message box is {}'.format(all_box)) conn.select_folder(all_box) # Once authenticated everything from the impalib.IMAP4_SSL class will # work as per usual without any modification to your code. # typ, msgnums = conn.search('X-GM-RAW vget') tid = int('14095f27c538b207', 16) # msgs = conn.search('X-GM-THRID {}'.format(tid)) msgs = conn.search('X-GM-RAW uniquetokenXXX') print msgs
def connect_account(self, email, pw, imap_endpoint, account_id=None): """Provide a connection to a IMAP account. Raises ------ socket.error If we cannot connect to the IMAP host. IMAPClient.error If the credentials are invalid. """ host, port = imap_endpoint try: conn = IMAPClient(host, port=port, use_uid=True, ssl=True) except IMAPClient.AbortError as e: log.error('account_connect_failed', account_id=account_id, email=email, host=host, port=port, error="[ALERT] Can't connect to host - may be transient") raise TransientConnectionError(str(e)) except(IMAPClient.Error, gaierror, socket_error) as e: log.error('account_connect_failed', account_id=account_id, email=email, host=host, port=port, error='[ALERT] (Failure): {0}'.format(str(e))) raise ConnectionError(str(e)) conn.debug = False try: conn.oauth2_login(email, pw) except IMAPClient.AbortError as e: log.error('account_verify_failed', account_id=account_id, email=email, host=host, port=port, error="[ALERT] Can't connect to host - may be transient") raise TransientConnectionError(str(e)) except IMAPClient.Error as e: log.error('IMAP Login error during connection. ' 'Account: {}, error: {}'.format(email, e), account_id=account_id) if str(e) == '[ALERT] Invalid credentials (Failure)' or \ str(e) == '[AUTHENTICATIONFAILED] OAuth authentication failed.': raise ValidationError(str(e)) else: raise ConnectionError(str(e)) except SSLError as e: log.error('account_verify_failed', account_id=account_id, email=email, host=host, port=port, error='[ALERT] (Failure) SSL Connection error') raise ConnectionError(str(e)) return conn
def connect_account(account): """Provide a connection to a IMAP account. Raises ------ socket.error If we cannot connect to the IMAP host. IMAPClient.error If the credentials are invalid. """ info = provider_info(account.provider) host = info['imap'] try: conn = IMAPClient(host, use_uid=True, ssl=True) except IMAPClient.AbortError as e: log.error('account_connect_failed', email=account.email_address, host=host, error=("[ALERT] Can't connect to host - may be transient")) raise TransientConnectionError(str(e)) except(IMAPClient.Error, gaierror, socket_error) as e: log.error('account_connect_failed', email=account.email_address, host=host, error='[ALERT] (Failure): {0}'.format(str(e))) raise ConnectionError(str(e)) conn.debug = False try: conn.oauth2_login(account.email_address, account.access_token) except IMAPClient.AbortError as e: log.error('account_verify_failed', email=account.email_address, host=host, error="[ALERT] Can't connect to host - may be transient") raise TransientConnectionError(str(e)) except IMAPClient.Error as e: log.error('IMAP Login error during refresh auth token. ' 'Account: {}, error: {}'.format(account.email_address, e)) if str(e) == '[ALERT] Invalid credentials (Failure)': # Access token could've expired try: conn.oauth2_login(account.email_address, account.renew_access_token()) except IMAPClient.Error as e: raise ValidationError(str(e)) else: raise ConnectionError(str(e)) except SSLError as e: log.error('account_verify_failed', email=account.email_address, host=host, error='[ALERT] (Failure) SSL Connection error') raise ConnectionError(str(e)) return conn
def connect_account(account): """Provide a connection to a IMAP account. Raises ------ socket.error If we cannot connect to the IMAP host. IMAPClient.error If the credentials are invalid. """ info = provider_info(account.provider) host = info["imap"] try: conn = IMAPClient(host, use_uid=True, ssl=True) except IMAPClient.Error as e: log.error('account_connect_failed', host=host, error="[ALERT] Can't connect to host (Failure)") raise ConnectionError(str(e)) except gaierror as e: log.error('account_connect_failed', host=host, error="[ALERT] Name resolution faiure (Failure)") raise ConnectionError(str(e)) except socket_error as e: log.error('account_connect_failed', host=host, error="[ALERT] Socket connection failure (Failure)") raise ConnectionError(str(e)) conn.debug = False try: conn.oauth2_login(account.email_address, account.access_token) except IMAPClient.Error as e: log.error("IMAP Login error, refresh auth token for: {}" .format(account.email_address)) log.error("Error was: {}".format(e)) if str(e) == '[ALERT] Invalid credentials (Failure)': # maybe the access token expired? try: conn.oauth2_login(account.email_address, account.renew_access_token()) except IMAPClient.Error as e: raise ValidationError() else: raise ValidationError() except SSLError as e: log.error('account_verify_failed', email=account.email_address, host=host, error="[ALERT] SSL Connection error (Failure)") raise ConnectionError(str(e)) return conn
def authenticate(imap_account): # type: (ImapAccount) -> t.Dict[t.AnyStr, t.Any] """Authenticate an imap_account from the database Returns a dictionary with the following keys status is whether authentication succeeded imap is the authorized ImapClient if status is true Args: imap_account (ImapAccount): ImapAccount stored in the database Returns: t.Dict: dictionary of information about the authentication """ res = {'status' : False, 'imap_error': False, 'imap_log': "", 'imap': None} # create an imap client imap_client = IMAPClient(imap_account.host, use_uid=True) try: if imap_account.is_oauth: # TODO if access_token is expired, then get a new token imap_client.oauth2_login(imap_account.email, imap_account.access_token) else: password = decrypt_plain_password(imap_account.password) imap_client.login(imap_account.email, password) res['imap'] = imap_client res['status'] = True except IMAPClient.Error, e: try: logger.debug('try to renew token') if imap_account.is_oauth: oauth = GoogleOauth2() response = oauth.RefreshToken(imap_account.refresh_token) imap_client.oauth2_login(imap_account.email, response['access_token']) imap_account.access_token = response['access_token'] imap_account.save() res['imap'] = imap_client res['status'] = True else: # TODO this is not DRY and not useful error messages logger.error("cannot renew token for non-oauth account") res['code'] = "Can't authenticate your email" except IMAPClient.Error, e: logger.exception("IMAPClient.Error - failed to authenticate email") res['imap_error'] = e res['code'] = "Can't authenticate your email"
def slurp_namespace(self, namespace, account, db): info = provider_info(account.provider) imap = IMAPClient(info['imap'], use_uid=True, ssl=True) imap.debug = self.args.debug_imap if info['auth'] == 'oauth2': imap.oauth2_login(account.email_address, account.access_token) elif info['auth'] == 'password': imap.login(account.email_address, account.password) else: raise NotImplementedError( "auth mechanism {0!r} not implemented; provider: {1!r}".format( info['auth'], account.provider)) slurp_imap_namespace_gmail(imap, namespace=namespace, account=account, db=db)
def slurp_namespace(self, namespace, account, db): info = account.provider_info host, port = account.imap_endpoint imap = IMAPClient(host, port=port, use_uid=True, ssl=True) imap.debug = self.args.debug_imap if info["auth"] == "oauth2": imap.oauth2_login(account.email_address, account.access_token) elif info["auth"] == "password": imap.login(account.email_address, account.password) else: raise NotImplementedError( "auth mechanism {0!r} not implemented; provider: {1!r}".format(info["auth"], account.provider) ) slurp_imap_namespace_gmail(imap, namespace=namespace, account=account, db=db)
def _new_connection(self): try: conn = IMAPClient(self.imap_host, use_uid=True, ssl=True) except IMAPClient.Error as e: raise socket.error(str(e)) conn.debug = False try: conn.oauth2_login(self.email_address, self.o_access_token) except IMAPClient.Error as e: if str(e) == "[ALERT] Invalid credentials (Failure)": self._set_account_info() conn.oauth2_login(self.email_address, self.o_access_token) return conn
def slurp_namespace(self, namespace, account, db): info = account.provider_info host, port = account.imap_endpoint imap = IMAPClient(host, port=port, use_uid=True, ssl=True) imap.debug = self.args.debug_imap if info['auth'] == 'oauth2': imap.oauth2_login(account.email_address, account.access_token) elif info['auth'] == 'password': imap.login(account.email_address, account.password) else: raise NotImplementedError( "auth mechanism {0!r} not implemented; provider: {1!r}".format( info['auth'], account.provider)) slurp_imap_namespace_gmail(imap, namespace=namespace, account=account, db=db)
def verify_gmail_account(account): try: conn = IMAPClient(IMAP_HOSTS['Gmail'], use_uid=True, ssl=True) except IMAPClient.Error as e: raise socket.error(str(e)) conn.debug = False try: conn.oauth2_login(account.email_address, account.o_access_token) except IMAPClient.Error as e: if str(e) == '[ALERT] Invalid credentials (Failure)': # maybe refresh the access token with session_scope() as db_session: account = verify_imap_account(db_session, account) conn.oauth2_login(account.email_address, account.o_access_token) return conn
def connect(self, **connect_args) -> IMAPClient: info = self._get_server_info(**connect_args) self.logger.info('Connecting to {}'.format(info.server)) context = None if info.ssl: import ssl context = ssl.create_default_context() context.load_cert_chain(certfile=info.certfile, keyfile=info.keyfile) client = IMAPClient(host=info.server, port=info.port, ssl=info.ssl, ssl_context=context) if info.password: client.login(info.username, info.password) elif info.access_token: client.oauth2_login(info.username, access_token=info.access_token, mech=info.oauth_mechanism, vendor=info.oauth_vendor) return client
def verify_account(account): try: conn = IMAPClient(IMAP_HOST, use_uid=True, ssl=True) except IMAPClient.Error as e: raise socket.error(str(e)) conn.debug = False try: conn.oauth2_login(account.email_address, account.access_token) except IMAPClient.Error as e: log.info("IMAP Login error, refresh auth token for: {}" .format(account.email_address)) if str(e) == '[ALERT] Invalid credentials (Failure)': # maybe the access token expired? conn.oauth2_login(account.email_address, account.renew_access_token()) return conn
def verify_gmail_account(account): try: conn = IMAPClient(IMAP_HOST, use_uid=True, ssl=True) except IMAPClient.Error as e: raise socket.error(str(e)) conn.debug = False try: conn.oauth2_login(account.email_address, account.o_access_token) except IMAPClient.Error as e: if str(e) == '[ALERT] Invalid credentials (Failure)': # maybe refresh the access token with session_scope() as db_session: account = verify_imap_account(db_session, account) conn.oauth2_login(account.email_address, account.o_access_token) return conn
def make_imap(self): server_string = ( f'{self.config.username}@{self.config.host}:{self.config.port} ' f'(ssl={self.config.ssl})' ) self.config.log('debug', f'Connecting to IMAP server: {server_string}') ssl_context = ssl.create_default_context() if self.config.ssl_verify_hostname is False: self.config.log('warning', 'Disabling SSL hostname verification!') ssl_context.check_hostname = False imap = IMAPClient( self.config.host, port=self.config.port, ssl=self.config.ssl, ssl_context=ssl_context, timeout=self.config.timeout, use_uid=True, ) imap.normalise_times = False if self.config.oauth_provider: try: imap.oauth2_login(self.config.username, self.config.get_oauth_access_token()) except LoginError as e: # TODO: Tidy this up/move it if 'AUTHENTICATIONFAILED' in f'{e}': self.config.log('info', 'Refreshing OAuth acccess token') invalidate_access_token(self.config.oauth_refresh_token) imap.oauth2_login(self.config.username, self.config.get_oauth_access_token()) else: imap.login(self.config.username, self.config.password) if self._selected_folder: imap.select_folder(self._selected_folder) # Ensure the IMAP object has capabilities cached as this is used internally # within imapclient. imap.capabilities() self._imap = imap self.config.log('info', f'Connected to IMAP server: {server_string}')
def connect_account(self, account): """Returns an authenticated IMAP connection for the given account. Raises ------ ValidationError If fetching an access token failed because the refresh token we have is invalid (i.e., if the user has revoked access). ConnectionError If another error occurred when fetching an access token. imapclient.IMAPClient.Error, socket.error If errors occurred establishing the connection or logging in. """ host, port = account.imap_endpoint try: conn = IMAPClient(host, port=port, use_uid=True, ssl=True) except (IMAPClient.Error, socket.error) as exc: log.error('Error instantiating IMAP connection', account_id=account.id, email=account.email_address, imap_host=host, imap_port=port, error=exc) raise try: # Raises ValidationError if the refresh token we have is invalid. token = token_manager.get_token(account) conn.oauth2_login(account.email_address, token) except IMAPClient.Error as exc: log.error('Error during IMAP XOAUTH2 login', account_id=account.id, email=account.email_address, host=host, port=port, error=exc) raise return conn
class Connection(object): _server = None _credentials = None _username = None _folder = None _imapobj = None """ #From oauth2gmail.py ----------------------- class GMailOAuth2Mixin(object): _credentials = None _username = None @staticmethod def refresh_credentials(credentials): authorized_http = credentials.authorize(http) for n in range(5): try: credentials.refresh(authorized_http) break except AccessTokenRefreshError: sleep((2 ** n) + random.randint(0, 1000) / 1000) return credentials @staticmethod def generate_xoauth2_string(username, access_token): return 'user=%s\1auth=Bearer %s\1\1' % (username, access_token) def _get_oauth_string(self, username, credentials): if credentials.invalid or credentials.access_token_expired or credentials.access_token is None: credentials = self.refresh_credentials(credentials) self._credentials = credentials self._username = username auth_string = self.generate_xoauth2_string(username=username, access_token=credentials.access_token) return auth_string class GMail_IMAP(imaplib.IMAP4_SSL, GMailOAuth2Mixin): def __init__(self, host=IMAP_HOST, port=IMAP_PORT, **kwargs): imaplib.IMAP4_SSL.__init__(self, host, port, **kwargs) def login_oauth2(self, username, credentials): auth_string = self._get_oauth_string(username, credentials) self.authenticate("XOAUTH2", lambda x: auth_string) return self._credentials #--------------------- """ #From: https://gist.github.com/miohtama/5389146 @staticmethod def _get_decoded_email_body(msg): """ Decode email body. Detect character set if the header is not set. We try to get text/plain, but if there is not one then fallback to text/html. :param message_body: Raw 7-bit message body input e.g. from imaplib. Double encoded in quoted-printable and latin-1 :return: Message body as unicode string """ text = "" if msg.is_multipart(): html = None for part in msg.get_payload(): print "%s, %s" % (part.get_content_type(), part.get_content_charset()) if part.get_content_charset() is None: # We cannot know the character set, so return decoded "something" text = part.get_payload(decode=True) continue charset = part.get_content_charset() if part.get_content_type() == 'text/plain': text = unicode(part.get_payload(decode=True), str(charset), "ignore").encode('utf8', 'replace') if part.get_content_type() == 'text/html': html = unicode(part.get_payload(decode=True), str(charset), "ignore").encode('utf8', 'replace') if text is not None: return text.strip() else: return html.strip() else: text = unicode(msg.get_payload(decode=True), msg.get_content_charset(), 'ignore').encode('utf8', 'replace') return text.strip() def _check_credentials(self, credentials): if credentials.invalid or credentials.access_token_expired or credentials.access_token is None: authorized_http = credentials.authorize(http) for n in range(5): try: print "Refreshing\n" credentials.refresh(authorized_http) break except AccessTokenRefreshError: print "Refresh failed: " + str(n) + "\n" sleep((2 ** n) + random.randint(0, 1000) / 1000) return credentials def __init__(self, user, credentials): #Should be changed to take user instead of username for consitency #Authenticate using oauth2gmail functionality #self._imapobj = self.GMail_IMAP('imap.gmail.com') #self._credentials = self._imapobj.login_oauth2(username, credentials) self._credentials = self._check_credentials(credentials) self._username = user.gmailuserinfo.gmail_username self._folder = user.gmailuserinfo.all_folder self._server = IMAPClient(IMAP_HOST, use_uid=True, ssl=True) #Maybe patch line 159 of imapclient.py from: #auth_string = lambda x: 'user=%s\1auth=Bearer %s\1\1' % (user, access_token) #to an adaptation from the following from oauth2gmail: #self.docmd("AUTH", "XOAUTH2 %s" % base64.b64encode(auth_string)) self._server.oauth2_login(self._username, self._credentials.access_token) self._server.select_folder(self._folder) def create(self, mail): #Don't support creating of gmails pass def update(self, mail_id, mail): serverTags = self._server.get_gmail_labels(mail_id)[mail_id] serverSpecialTags = list(filter(lambda x: x.startswith("\\"), serverTags)) tags = [tag['tag'].decode('utf-8') for tag in mail.content['tags']] #tags = mail.content['tags'] tags.extend(serverSpecialTags) self._server.set_gmail_labels(mail_id, tags) def read(self, mail_id): result = self._server.fetch(mail_id, ['RFC822', 'X-GM-LABELS']) #message = result[message_id]['RFC822'] message = email.message_from_string(result[mail_id]['RFC822']) body = self._get_decoded_email_body(message) serverTags = result[mail_id]['X-GM-LABELS'] noSpecialTags = filter(lambda x: not(x.startswith("\\")), serverTags) tags = [{'tag': tag.encode('utf-8')} for tag in noSpecialTags] mail = Mail(subject=message['Subject'], body=body, sender=message['From'], tags=tags) return mail def read_updated(self, mail_id, updated_since=None): if updated_since: result = self._server.fetch(mail_id, ['RFC822', 'X-GM-LABELS', 'MODSEQ'], ['CHANGEDSINCE ' + str(updated_since)]) else: result = self._server.fetch(mail_id, ['RFC822', 'X-GM-LABELS', 'MODSEQ']) result_content = result.get(mail_id) if result_content: message = email.message_from_string(result_content['RFC822']) body = self._get_decoded_email_body(message) serverTags = result_content['X-GM-LABELS'] noSpecialTags = filter(lambda x: not(x.startswith("\\")), serverTags) tags = [{'tag': tag.encode('utf-8')} for tag in noSpecialTags] last_update = result_content['MODSEQ'][0] #Strangely returns tuple so have to index the first (and only during initial testing) part mail = Mail(subject=message['Subject'], body=body, sender=message['From'], tags=tags) return mail, last_update else: return None def find(self, query): messages = self._server.gmail_search(query) return messages def find_updated(self, query, updated_since=None): if updated_since: messages = self._server.search(['MODSEQ ' + str(updated_since) , 'X-GM-RAW ' + query]) else: messages = self._server.search(['X-GM-RAW ' + query]) return messages def find_updated_excepted(self, query, updated_since=None, excepted_ids=[]): messages = self.find_updated(query, updated_since) filtered_messages = [ i for i in messages if i not in excepted_ids] return filtered_messages def latest_update_status(self): status = self._server.folder_status(self._folder, ('HIGHESTMODSEQ')) return status['HIGHESTMODSEQ']
from __future__ import unicode_literals from imapclient import IMAPClient # Populate these with actual values OAUTH2_USER = '******' OAUTH2_ACCESS_TOKEN = '...' HOST = 'imap.host.com' URL = "https://somedomain.com/someuser/imap/" ssl = True server = IMAPClient(HOST, use_uid=True, ssl=ssl) resp = server.oauth2_login(URL, OAUTH2_USER, OAUTH2_ACCESS_TOKEN) print(resp) select_info = server.select_folder('INBOX') print(select_info) server.logout()
def login_imap(email, password, host, is_oauth): """This function is called only once per each user when they first attempt to login to YoUPS. check if we are able to login to the user's imap using given credientials. if we can, encrypt and store credientials on our DB. Args: email (string): user's email address password (string): if is_oauth True, then it contains oauth token. Otherwise, it is plain password host (string): IMAP host address is_oauth (boolean): if the user is using oauth or not """ logger.info('adding new account %s' % email) res = {'status': False} try: imap = IMAPClient(host, use_uid=True) refresh_token = '' access_token = '' if is_oauth: # TODO If this imap account is already mapped with this account, bypass the login. oauth = GoogleOauth2() response = oauth.generate_oauth2_token(password) refresh_token = response['refresh_token'] access_token = response['access_token'] imap.oauth2_login(email, access_token) else: imap.login(email, password) # encrypt password then save aes = AES.new(IMAP_SECRET, AES.MODE_CBC, 'This is an IV456') # padding password padding = random.choice(string.letters) while padding == password[-1]: padding = random.choice(string.letters) continue extra = len(password) % 16 if extra > 0: password = password + (padding * (16 - extra)) password = aes.encrypt(password) imapAccount = ImapAccount.objects.filter(email=email) if not imapAccount.exists(): imapAccount = ImapAccount(email=email, password=base64.b64encode(password), host=host) imapAccount.host = host # = imapAccount else: imapAccount = imapAccount[0] imapAccount.password = base64.b64encode(password) res['imap_code'] = "" # TODO PLEASE REMOVE THIS WOW res['imap_log'] = imapAccount.execution_log if is_oauth: imapAccount.is_oauth = is_oauth imapAccount.access_token = access_token imapAccount.refresh_token = refresh_token imapAccount.is_gmail = imap.has_capability('X-GM-EXT-1') imapAccount.save() res['status'] = True logger.info("added new account %s" % imapAccount.email) except IMAPClient.Error, e: res['code'] = e
def test_imap(user): credentials = decorator.credentials if credentials.access_token_expired: logging.debug('Refreshing...') credentials.refresh(httplib2.Http()) conn = IMAPClient('imap.gmail.com', use_uid=True, ssl=True) conn.debug = 4 conn.oauth2_login(user, credentials.access_token) # status, labels = conn.list() folders = conn.list_folders() try: all_box = next(box for (flags, _, box) in folders if '\All' in flags) except StopIteration: raise Error('all message box not found') logging.debug('All message box is {}'.format(all_box)) conn.select_folder(all_box) # Once authenticated everything from the impalib.IMAP4_SSL class will # work as per usual without any modification to your code. # typ, msgnums = conn.search('X-GM-RAW vget') tid = int('14095f27c538b207', 16) # msgs = conn.search('X-GM-THRID {}'.format(tid)) msgs = conn.search('X-GM-RAW uniquetokenXXX') logging.info(msgs) logging.info(conn.fetch(msgs, 'X-GM-MSGID')) msg = conn.fetch(msgs, 'RFC822') logging.info(msg) return msg