|
|
|
@ -0,0 +1,200 @@
|
|
|
|
|
#+HUGO_BASE_DIR: ./
|
|
|
|
|
#+HUGO_AUTO_SET_LASTMOD: t
|
|
|
|
|
#+HTML_HEAD: <style>html{max-width: 45em; font-family:serif;}</style>
|
|
|
|
|
|
|
|
|
|
* Meta :@Meta:
|
|
|
|
|
* Technology :@Technology:
|
|
|
|
|
** TODO A perfect email setup (for me)
|
|
|
|
|
I've never been satisfied with any of the email clients most people use. I've
|
|
|
|
|
tried [[https://www.thunderbird.net/en-GB/][Thunderbird]], [[https://wiki.gnome.org/Apps/Evolution][Evolution]], [[https://getmailspring.com/][Mailspring]], [[https://support.apple.com/mail][Mail.app]], [[https://roundcube.net/][Roundcube]], [[https://sogo.nu/][SOGo]], [[https://wiki.gnome.org/Apps/Geary][Geary]], and
|
|
|
|
|
likely a lot more. /None/ of them handle multiple accounts particularly well
|
|
|
|
|
because all of the emails to one account are bound within it. You can make a new
|
|
|
|
|
folder somewhere called =TODO= and move all of your actionable emails to that
|
|
|
|
|
folder. When you go to move actionable emails from /another/ account into that
|
|
|
|
|
folder, you'll likely find that the client just doesn't let you. If it does,
|
|
|
|
|
when you reply, it will likely be sent from the wrong account. This is a
|
|
|
|
|
limitation of the IMAP protocol; everything is /managed/ locally but changes are
|
|
|
|
|
pushed to the remote server and mixing things the way I want leads to broken
|
|
|
|
|
setups.
|
|
|
|
|
|
|
|
|
|
Before I go any further, these are characteristics of my ideal email tool.
|
|
|
|
|
|
|
|
|
|
+ Support for multiple accounts (obviously)
|
|
|
|
|
+ /Native desktop application/ (*not* [[https://github.com/electron/electron][Electron]] 🤮)
|
|
|
|
|
+ Has stellar keyboard shortcuts
|
|
|
|
|
+ Doesn't require internet connectivity (other than downloading of course)
|
|
|
|
|
+ Organisation can be done with tags
|
|
|
|
|
|
|
|
|
|
*** Why tags?
|
|
|
|
|
Because they're better. Hierarchies are useful for prose and code but not for
|
|
|
|
|
files, emails, notes, or anything where an item may fit in multiple categories.
|
|
|
|
|
Imagine you get an email from your Computer Science professor that includes test
|
|
|
|
|
dates, homework, and information about another assignment then asks every
|
|
|
|
|
student to reply with something they learned from the previous class. In a
|
|
|
|
|
hierarchy, the best place for this might just be a =TODO= folder even though it
|
|
|
|
|
would also fit under =School=, =CS=, =Scheduling=, =To read=, and =Homework=. Maybe you
|
|
|
|
|
have a few minutes and want to clear out some emails that don't require any
|
|
|
|
|
interaction. That would be a good time to open =To read=, get that email out of
|
|
|
|
|
the way, and remove the =To read= tag. It will still show up under the other tags
|
|
|
|
|
so you can find it later and take the time to fully answer the professor's
|
|
|
|
|
question, add those dates to your calendar, or add the homework assignments to
|
|
|
|
|
your TODO list. Hierarchies can be very cumbersome to work with, especially when
|
|
|
|
|
one folder ends up getting all the data. Tags ensure that you only see what you
|
|
|
|
|
want when you want it. Tags are more efficient and they will remain my
|
|
|
|
|
organisation system of choice.
|
|
|
|
|
|
|
|
|
|
*** The tools
|
|
|
|
|
In short:
|
|
|
|
|
+ [[https://www.offlineimap.org/][OfflineIMAP]] - download emails
|
|
|
|
|
+ [[https://afew.readthedocs.io/en/latest/][=afew=]] - initial tagging script for…
|
|
|
|
|
+ [[https://notmuchmail.org/][=notmuch=]] - /the/ tool for tag-based organisation
|
|
|
|
|
+ [[https://neomutt.org/][NeoMutt]] - interact with tags, emails, compose, reply, etc.
|
|
|
|
|
+ [[https://marlam.de/msmtp/][=msmtp=]] - SMTP client for relaying outbound mail to the SMTP server
|
|
|
|
|
|
|
|
|
|
Yes, it's a lot. Yes, it's time-consuming to set up. Yes, it's worth it.
|
|
|
|
|
|
|
|
|
|
*** OfflineIMAP
|
|
|
|
|
As I said above, IMAP is limiting so some other tool or protocol is necessary
|
|
|
|
|
for organisation. There's an awesome piece of software called [[https://www.offlineimap.org/][OfflineIMAP]] which
|
|
|
|
|
is built for exactly this purpose. Its configuration can be a bit daunting if
|
|
|
|
|
you have as many accounts as I do (17) but it's not /terrible/.
|
|
|
|
|
|
|
|
|
|
**** =[general]=
|
|
|
|
|
#+BEGIN_SRC conf
|
|
|
|
|
[general]
|
|
|
|
|
metadata = ~/.offlineimap
|
|
|
|
|
accounts = use_exa
|
|
|
|
|
maxsyncaccounts = 1
|
|
|
|
|
ui = basic
|
|
|
|
|
ignore-readonly = no
|
|
|
|
|
pythonfile = ~/.offlineimap.py
|
|
|
|
|
socktimeout = 60
|
|
|
|
|
fsync = true
|
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
|
|
The first big option is =accounts=; it tells OfflineIMAP what to actually sync.
|
|
|
|
|
What to put there will be defined further down but =use_exa= is just filler text.
|
|
|
|
|
The example account is =user@example.com= and I shortened that to =use_exa=.
|
|
|
|
|
=maxsyncaccounts= is also fairly important as it tells OfflineIMAP to only pull
|
|
|
|
|
emails from one account at a time. This is certainly slower than multiple but
|
|
|
|
|
it's also safer because we'll be running this in the background and don't want
|
|
|
|
|
many OfflineIMAP processes running concurrently and interfering with each other.
|
|
|
|
|
=pythonfile= will be discussed later.
|
|
|
|
|
|
|
|
|
|
**** =[Account]=
|
|
|
|
|
#+BEGIN_SRC conf
|
|
|
|
|
[Account use_exa]
|
|
|
|
|
localrepository = use_exa-local
|
|
|
|
|
remoterepository = use_exa-remote
|
|
|
|
|
quick = 10
|
|
|
|
|
utf8foldernames = yes
|
|
|
|
|
postsynchook = notmuch new
|
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
|
|
In the first block, =localrepository= and =remoterepository= tell OfflineIMAP where
|
|
|
|
|
to look for your emails. =use_exa-local= is an arbitrary naming scheme I use to
|
|
|
|
|
differentiate between the various local and remote accounts. It can easily be
|
|
|
|
|
swapped with something else.
|
|
|
|
|
|
|
|
|
|
**** =[Repository]=
|
|
|
|
|
|
|
|
|
|
#+BEGIN_SRC conf
|
|
|
|
|
[Repository use_exa-local]
|
|
|
|
|
type = Maildir
|
|
|
|
|
localfolders = ~/mail/use_exa
|
|
|
|
|
sync_deletes = yes
|
|
|
|
|
|
|
|
|
|
[Repository use_exa-remote]
|
|
|
|
|
type = IMAP
|
|
|
|
|
remotehost = imap.example.com
|
|
|
|
|
starttls = yes
|
|
|
|
|
ssl = no
|
|
|
|
|
remoteport = 143
|
|
|
|
|
remoteuser = user@example.com
|
|
|
|
|
remotepasseval = get_pass("use_exa")
|
|
|
|
|
auth_mechanisms = GSSAPI, XOAUTH2, CRAM-MD5, PLAIN, LOGIN
|
|
|
|
|
maxconnections = 1
|
|
|
|
|
createfolders = True
|
|
|
|
|
sync_deletes = yes
|
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
|
|
The repository sections describe how the emails are stored or retrieved. In the
|
|
|
|
|
=local= block, you'll notice that the type is =Maildir=. In this format, each email
|
|
|
|
|
is given a unique filename and stored in a hierachy of folders within your
|
|
|
|
|
account. This is very often how your emails are stored on your provider's mail
|
|
|
|
|
server.
|
|
|
|
|
|
|
|
|
|
=pythonfile= is used here to authenticate with the remote server. This can be
|
|
|
|
|
complicated and depends /entirely/ on how you manage your passwords. I personally
|
|
|
|
|
use [[https://keepassxc.org/][KeePassXC]] and love it. When I set OfflineIMAP up, however, it didn't have
|
|
|
|
|
=libsecret= compatibility. This would have made setup significantly easier but, as
|
|
|
|
|
it already just works™, I don't really see a reason to change it.
|
|
|
|
|
|
|
|
|
|
This new feature allows =libresecret=-based applications to query KeePassXC for
|
|
|
|
|
your passwords or store them there on your behalf. CLI/TUI applications that
|
|
|
|
|
need a secure mechanism for background authentication can use =secret-tool lookup
|
|
|
|
|
Title "TITLE_OF_PASSWORD"= as the password command. See [[https://github.com/keepassxreboot/keepassxc/pull/2726][the pull request]] for more
|
|
|
|
|
details. Because this wasn't a feature when I first set it up, I put my
|
|
|
|
|
passwords in plaintext files and encrypted them with the GPG key stored on my
|
|
|
|
|
YubiKey. As long as my key is plugged in, OfflineIMAP can authenticate and
|
|
|
|
|
download all my emails just fine. The process for using a GPG /not/ stored on a
|
|
|
|
|
hardware token is pretty much the same and I'll talk about that process instead.
|
|
|
|
|
|
|
|
|
|
These are the contents of my =~/.offlineimap.py=.
|
|
|
|
|
|
|
|
|
|
#+BEGIN_SRC python
|
|
|
|
|
#! /usr/bin/env python2
|
|
|
|
|
from subprocess import check_output
|
|
|
|
|
def get_pass(account):
|
|
|
|
|
return check_output(["gpg", "-dq", f" ~/.mail_pass/{account}.gpg"]).strip("\n")
|
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
|
|
This runs =gpg -dq ~/.mail_pass/use_exa.gpg= then strips the newline character
|
|
|
|
|
before returning it to OfflineIMAP. =-d= tells GPG that you're passing it a file
|
|
|
|
|
you want decrypted and =-q= tells it not to give any output other than the file's
|
|
|
|
|
contents. For a setup that works with this Python script, put your passwords in
|
|
|
|
|
plaintext files with the account name as the file name (e.g. =use_exa=). You'll
|
|
|
|
|
then encrypt it with =gpg -er <YOUR_KEY_ID> use_exa=. Running =gpg -dq use_exa.gpg=
|
|
|
|
|
should display your password. Repeat for every account and store the resulting
|
|
|
|
|
files in =~/.mail_pass/=.
|
|
|
|
|
|
|
|
|
|
The other option, =sync_deletes=, is whether or not to delete remote emails that
|
|
|
|
|
have been deleted locally. I enabled that because I want to have easy control
|
|
|
|
|
over how much remote storage is used.
|
|
|
|
|
|
|
|
|
|
Here's the next block again so you don't have to scroll up:
|
|
|
|
|
|
|
|
|
|
#+BEGIN_SRC conf
|
|
|
|
|
[Repository use_exa-remote]
|
|
|
|
|
type = IMAP
|
|
|
|
|
remotehost = imap.example.com
|
|
|
|
|
starttls = yes
|
|
|
|
|
ssl = no
|
|
|
|
|
remoteport = 143
|
|
|
|
|
remoteuser = user@example.com
|
|
|
|
|
remotepasseval = get_pass("use_exa")
|
|
|
|
|
auth_mechanisms = GSSAPI, XOAUTH2, CRAM-MD5, PLAIN, LOGIN
|
|
|
|
|
maxconnections = 1
|
|
|
|
|
createfolders = True
|
|
|
|
|
sync_deletes = yes
|
|
|
|
|
#+END_SRC
|
|
|
|
|
|
|
|
|
|
This one's pretty self-explanatory. =type=, =remotehost=, =starttls=, =ssl=, and
|
|
|
|
|
=remoteport= should all be somewhere in your provider's documentation. =remoteuser=
|
|
|
|
|
is your email address and =remotepasseval= is the function that will return your
|
|
|
|
|
password and allow OfflineIMAP to authenticate. You'll want enter the name of
|
|
|
|
|
your password file without the =.gpg= extension; the script takes care of adding
|
|
|
|
|
that. :eave =auth_mechanisms= alone and the same for =maxconnections= unless you
|
|
|
|
|
know your provider won't ratelimit you or something for opening multiple
|
|
|
|
|
connections. =sync_deletes= is the same as in the previous block.
|
|
|
|
|
|
|
|
|
|
Copy those three blocks for as many accounts as you want emails downloaded from.
|
|
|
|
|
I have 510 lines just for =Account= and =Repository= blocks.
|
|
|
|
|
|
|
|
|
|
*** =afew=
|
|
|
|
|
*** =notmuch=
|
|
|
|
|
*** NeoMutt
|
|
|
|
|
*** =msmtp=
|
|
|
|
|
* Music :@Music:
|
|
|
|
|
* Pipe Smoking :@Pipe__Smoking:
|
|
|
|
|
* Dungeons & Dragons :@Dungeons__and__Dragons:
|