OfflineIMAP / offlineimap

Read/sync your IMAP mailboxes (python2) [LEGACY: move to offlineimap3]
http://www.offlineimap.org
Other
1.78k stars 360 forks source link

Python functions not evaluated for assigning oauth2_access_token or oauth2_refresh_token #709

Closed robstewart57 closed 2 years ago

robstewart57 commented 2 years ago

General informations

Configuration file offlineimaprc

pythonfile = myofflineimap.py
remotepasseval = get_pass("MyAccount")
oauth2_access_token = get_token()
oauth2_refresh_token = get_token()

pythonfile (if any)

def get_pass(account):
    return check_output("pass Mail/" + account, shell=True).rstrip()

def get_token():
    return check_output("~/sw/oauth2ms/oauth2ms --no-browser", shell=True).rstrip()

Logs, error

The error is that the output from the oauth2ms --no-browser command (which returns an OAuth token) is not assigned to oauth2_access_token or oauth2_refresh_token in the offlineimap configuration file. After doing some digging in IMAP.py in the offlineimap3 implementation, it appears that simply the string "get_token()" is assigned to oauth2_access_token and oauth2_refresh_token.

So it appears that the get_token() function is not evaluated to a string, simply the string "get_token()" is assigned in the configuration file.

However the get_pass(account) function is evaluated to a password string, as expected, and assigned to remotepasseval in the offlineimap configuration file.

Possible bug

The code logic for getpassword is a bit different to the functions for gettings the oauth information.

passwd = self.getconf('remotepasseval', None)
        if passwd is not None:
            return self.localeval.eval(passwd).encode('UTF-8')

https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap/repository/IMAP.py#L375

However getoauth2_access_token is:

def getoauth2_access_token(self):
        access_token = self.getconf('oauth2_access_token', None)
        if access_token is None:
            access_token = self.localeval.eval(
                self.getconf('oauth2_access_token_eval', "None")
            )
            if access_token is not None:
                access_token = access_token.strip("\n")
        return access_token

However if access_token is None will not be true because access_token will be the string "get_token()". Should this function not be the opposite? e.g.

        access_token = self.getconf('oauth2_access_token', None)
        if access_token is not None:
            access_token = self.localeval.eval(
                self.getconf('oauth2_access_token', None)
            )

        if isinstance(access_token, bytes):
            access_token = access_token.decode(encoding='utf-8')
        elif isinstance(access_token, str):
            access_token = access_token

        return access_token

The same suggestion for the getoauth2_refresh_token function: https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap/repository/IMAP.py#L308

This potential bug might be the root cause for the issue https://github.com/OfflineIMAP/offlineimap/issues/667 .

robstewart57 commented 2 years ago

I think I can close this. I hadn't spotted in the offlineimap.conf sample file this value:

oauth2_access_token_eval = get_access_token("accountname")

So rather than using oauth2_access_token I should have used oauth2_access_token_eval. This is unlike (i.e. the source of my confusion) remotepasseval which supports either a string directly in the .conf file or a Python function name, defined elsewhere in the pythonfile, to be evaluated.