Offlineimap

From wikinotes

Offlineimap is a program that allows you to sync between an external imap server, and a local maildir folder. This allows you to read your local inbox locally, and quickly (even if you do not have the internet).


Documentation

commented offlineimap.conf https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap.conf
official docs https://www.offlineimap.org/documentation.html
man page https://www.offlineimap.org/doc/offlineimap.html
github https://github.com/OfflineIMAP/offlineimap3

Locations

~/.offlineimaprc offlineimap config file
~/.offlineimap.py python functions/classes defined here can be accessed in .offlineimaprc (for nametrans, folderfilter, ... )

Install

sudo pacman -S offlineimap python2-pysqlite

Usage

offlineimap               # sync all mailboxes
offlineimap -a junk,work  # sync only junk,work mailboxes
offlineimap --info        # test/debug folderfilter/nametrans settings without copying data!

You'll likely want to add it to your crontab, or create a systemd timer for it (better).

# offlineimap can be run from the commandline,
# but you'll likely want to stick it in your crontab

*/5      *        *           *        *                             offlineimap -q					## quick sync
*/30     *        *           *        *                             offlineimap						## in-depth sync

Configuration

General

This section has general settings shared by all mailboxes.

# ~/.offlineimaprc
# vim: ft=dosini

[general]
ui         = ttyui
pythonfile = ~/.offlineimap.py     # any functions/classes defined here can be called in .offlineimaprc
accounts   = work, junk, pers, mfw

[mbnames]
filename = ~/.mutt/mailboxes
header   = "mailboxes "
peritem  = "+%(accountname)s/%(foldername)s"
sep      = "/"
footer   = "\n"

Accounts

Here we'll configure the email accounts that get managed by offlineimap.

The basic concept for offlineimap is defining two repositories, then connecting them in an account.
Everything is then synchronized between the two accounts.
You may define as many accounts or repositories as you'd like.

Login Auth

# ~/.offlineimaprc

# ..general settings..

[Account work]
localrepository  = work-local
remoterepository = work-remote

[Repository work-local]
type           = Maildir
localfolders   = ~/.mail/work
#folderfilter  = lambda foldername: foldername not in ('[Gmail].All Mail', '[Gmail].Sent Mail', '[Gmail].Trash')
#folderfilter  = lambda folder: folder in ('INBOX','Drafts','Sent')
maxconnections = 3

[Repository work-remote]
ssl            = yes
type           = Gmail
remoteuser     = willjpittman@gmail.com
remotepass     = <password>  # OR remotepasseval (python from ~/.offlineimap.py)
realdelete     = no
maxconnections = 3
sslcacertfile  = /etc/ssl/certs/ca-certificates.crt

Mailctl GMail OAUTH

mailctl can obtain and renew OAUTH2 tokens for other programs.

NOTE:

I also needed to modify /usr/lib/python3.10/site-packages/offlineimap/repository/IMAP.py to convert bytestrings to strs.

def getoauth2_client_id(self):
    # ...
    if isinstance(client_id, bytes):
        client_id = client_id.decode()

def getoauth2_client_secret(self):
    # ...
    if isinstance(client_secret, bytes):
        client_secret = client_secret.decode()


# ~/.offlineimaprc
[general]
pythonfile = ~/.offlineimap.py


[Repository foo-remote]
# ...

auth_mechanisms = XOAUTH2
oauth2_client_id = <YOUR_OAUTH2_CLIENT_ID>
oauth2_client_secret = <YOUR_OAUTH2_CLIENT_SECRET>
oauth2_request_url = https://accounts.google.com/o/oauth2/token
oauth2_access_token_eval = get_token("<YOUR_EMAIL_ADDRESS_FOR_THIS_ACCOUNT>")
# ~/.offlineimap.py

import subprocess

def get_token(email_address):
    cmd = subprocess.run(["mailctl", "access", "%s" % email_address], capture_output=True)
    return cmd.stdout.decode()

Gmail OAUTH2

NOTE:

this uses an OAUTH2 token, but does not renew it (bad)

Generate OAUTH2 Token


See gmail
Follow Authentication/OAUTH2 instructions to retrieve refresh token.


Download a copy of oauth2 script



~/.offlineimaprc


# ... identical to LOGIN auth settings


# plaintext within file
[Repository work-remote]
auth_mechanisms = XOAUTH2
oauth2_client_id = <your-clientid>
oauth2_client_secret = <your-secrete>
oauth2_request_url = https://accounts.google.com/o/oauth2/token
oauth2_refresh_token = <your-refresh-token>

# eval command to get files
#oauth2_client_id_eval = get_client_id("accountname")
#oauth2_client_secret_eval = get_client_secret("accountname")
#oauth2_refresh_token_eval = get_refresh_token("accountname")


pythonfile

The pythonfile = option allows you to define python callables
you can invoke to obtain configuration values.

# ~/.offlineimaprc

[general]
pythonfile = ~/.offlineimap.py


[Repository pers-remote]
# ...
remotepasseval = 'get_password()'
#!/usr/bin/env python
# ~/.offlineimap.py

def get_password():
    return 'password'