l2dy / tmail

Files and instructions related to my mail setup.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Mail via Emacs

I began consuming mail with Emacs and Notmuch when I was but a young boy… it was the year 2018… a simpler time, to be sure.

Setting all of this up was relatively simple in the beginning. (You can see what I mean by checking previous revisions of this file.) In the intervening years things like multifactor authentication have taken the world by storm and made it nigh impossible to use IMAP to sync lovely mail messages. Those were the days…

DavMail

Possibly optional: I use Outlook at work, so if you’re not required to use MFA, you can probably skip this step.

Install DavMail. If you’re a Fedora user, you can install DavMail via COPR. Otherwise, choose from the following:

You’ll need to ”register an app” and copy the tenant and client IDs and change the following in ~/.davmail.properties:

davmail.oauth.clientId=yourappid
davmail.oauth.redirectUri=https://login.microsoftonline.com/common/oauth2/nativeclient

mbsync

To sync mail to a local maildir, install isync. You can use your favorite package manager, but it’s worth noting that I ended up building mbsync from source to remedy some problem I can’t remember.

If you’re a macOS user, you’ll need to download and install a CA bundle. mbsync relies on a CA certificate bundle as its trust store (as opposed to macOS using Keychain, which iSync doesn’t support). Download and install a CA cert bundle:

wget https://curl.haxx.se/download/curl-7.57.0.tar.gz
tar xvf curl-7.57.0.tar.gz
cd curl-7.57.0/lib
./mk-ca-bundle.pl
mv ca-bundle.crt ~

Now we can tell mbsync how to behave via ~/.mbsyncrc:

# [Global]

# Create missing mailboxes on producer/consumer.
Create Both
# Equivalent of --full on command line
Sync All
# Tells mbsync to save its state to consumer mailbox.
SyncState *
# Necessary to avoid duplicate files.
Expunge Both

# [IMAP Settings]

# Define an account
IMAPAccount my_cool_account_name
# The following Host/Port values are correct for DavMail.
# You'll need to change them if you're using IMAP with GMail or whatever.
Host localhost
Port 1143
User you@example.com
# This doesn't matter if you're using DavMail.
# Otherwise, it would be wise to use GPG instead of
# storing your password in plain text.
PassCmd "echo foo"
SSLType None
AuthMechs LOGIN
# You may need to point to a different cert bundle if not using Fedora.
# If you're using macOS, use the path to the cert bundle created above.
CertificateFile /etc/ssl/certs/ca-bundle.crt
# Set IMAP timeout to avoid timeout while syncing big files.
Timeout 300

# [Stores]

# Define remote store and tell it to use account defined
# above.
IMAPStore my_cool_account_name-remote
Account my_cool_account_name

# Define local store to sync with remote.
MaildirStore my_cool_account_name-local
# Not sure - docs recommend it.
Subfolders Verbatim
# Path to local store.
Path ~/mail/
# Tell mbsync where to put INBOX.
Inbox ~/mail/inbox

# [Channels]

# Define work-inbox channel; remote as producer, local as consumer; syncs
# INBOX.

Channel my_cool_account_name-inbox
Master :my_cool_account_name-remote:
Slave :my_cool_account_name-local:
Patterns "INBOX"

# As above but sync "Sent Items"
# Repeat this for every folder you'd like to sync

Channel my_cool_account_name-sent
Master :my_cool_account_name-remote:"Sent"
Slave :my_cool_account_name-local:"Sent Items"
Patterns "Sent"

# [Groups]

# Finally, group all channels together

Group my_cool_account_name
Channel my_cool_account_name-inbox
Channel my_cool_account_name-sent

Running mbsync -a should now work.

NOTE: If you’re having trouble, try running mbsync with -v (with more “v”s to increase verbosity). Some problems may be related to the Patterns directive above. To troubleshoot, try listing remote IMAP folders using the following:

openssl s_client -connect imap.example.com:993 -crlf

See RFC3501 section 6.1 for IMAP commands.

notmuch

Install notmuch via your favorite package manager.

Now, run notmuch setup and follow the prompts. Afterwards, run notmuch new to populate the notmuch database.

msmtp

Now we need something to send mail.

Install msmtp using your favorite package manager.

Create ~/.msmtprc and add the following:

# Set default values for all following accounts.
defaults
# If you're using DavMail, set auth to "plain" and tls to "off".
auth           on
tls            on
# Use the path from above or the CA cert bundle created earlier if you're using macOS.
tls_trust_file /Users/<name>/ca-bundle.crt
logfile        ~/.msmtp.log

account        <pick_a_name>
# If you're using something other than DavMail set the following accordingly.
host           localhost
port           1025
from           <name>@example.com
user           <name>
# If you're using DavMail, the following doesn't matter.
passwordeval   "echo `gpg -q --for-your-eyes-only --no-tty -d ~/.passwd/minort.gpg`"

# Set a default account
account default : work

Test your configuration with the following:

$ msmtp --account=<account> -Sd

afew

I use afew for automatic mail tagging/sorting. My configuration file is huge and probably needs updating, but I’ll provide a simplified version below.

# This is the default filter chain
[KillThreadsFilter]
[ArchiveSentMailsFilter]
[ListMailsFilter]

# [Filter.]
# message =
# query =
# tags =

# You can use regex capture groups for tag assignment using HeaderMatchingFilter.
# This is an example of a Nagios filter:
[HeaderMatchingFilter.1]
header = Subject
pattern = [*]{2}\s(?P<type>[A-Z]+)\s--\sProject:\s(?P<project>[a-z1-9]+),\s(?P<host>[^/]+)/.*\sis\s(?P<state>\b[A-Z]+\b)
tags = +{type};+{project};+{host};+{state};+nagios;+updates;-new

[Filter.1]
message = A cool message
query = 'subject:"/your kewl/"' AND from:bestfriend@example.com
tags = +best-friend;-new

[InboxFilter]

[MailMover]
folders = Inbox "Sent Items" Junk
rename = True
# This rule moves mail tagged with "junk" to a Junk folder.
Inbox = 'tag:junk':Junk

muchsync

I sync mail on a remote server that’s always connected to the internet and sync mail to my desktop using muchsync.

cron/systemd timer

I use the following script to sync mail using a systemd timer:

#! /bin/bash

vpn_status=$(nmcli connection show vpn | \
	       grep -i vpn.vpn-state | \
	       awk '{print $5}')

if [[ $vpn_status =~ "connected" ]]; then
  /usr/local/bin/muchsync -v example.com -v
  /usr/bin/emacsclient -e '(tm/notmuch-notify "3mins")'
fi

exit 0

Here’s the function definition for tm/notmuch-notify:

(defun tm/notmuch-notify (time-range)
  "Generate desktop notifcations for new mail received in TIME-RANGE.

This function utilizes `notmuch-call-notmuch-sexp' to fetch the
latest messages tagged inbox and send a notification to the
desktop.  TIME-RANGE should be the beginning of an Xapian date
range.  For example, an input of \"20mins\" translates to
\"date:20mins..\"."
  (let* ((latest-messages
          (apply #'notmuch-call-notmuch-sexp `("search"
					       "--format=sexp"
					       "--format-version=4"
					       "--sort=newest-first"
					       "tag:inbox"
					       ,(format "date:%s.." time-range))))
         (who)
         (when)
         (what)
         (mail-message)
         (body))
    (mapcar (lambda (mail-message)
              (setq when (plist-get mail-message :date_relative))
              (setq who (if (string-match-p "|"
                                            (plist-get mail-message :authors))
                            (progn (string-match "[[:space:],]\\{0,2\\}\\([a-zA-z[:space:]]+\\)|"
                                                 (plist-get mail-message
                                                            :authors))
                                   (match-string 1 (plist-get mail-message
                                                              :authors)))
                          (plist-get mail-message :authors)))
              (setq what (plist-get mail-message :subject))
              (setq body (format "<b>%s</b>\n<b>%s</b>\n\n%s" when who what))
              (async-start
               `(lambda ()
                  (require 'notifications)
                  (notifications-notify :title "New message(s)!\n"
                                        :body ,body
                                        :app-name "notmuchmail"))
               'ignore))
            latest-messages)))

emacs

Put this in your init.el:

(use-package notmuch
  :config
  (setq message-send-mail-function 'async-smtpmail-send-it
        ;; If you use Fedora (or CentOS), it's likely that the
        ;; alternatives program takes care of this for you.
        sendmail-program "/usr/local/bin/msmtp"
        user-mail-address "me@example.com"
        smtpmail-smtp-user "me@example.com"
        ;; Choose the appropriate value based on your mail provider;
        ;; the following is correct for Davmail running locally.
        smtpmail-smtp-service 1025
        smtpmail-smtp-server "localhost"
        ;; If you use multiple mail accounts, you'll need the
        ;; following.
        message-sendmail-extra-arguments '("--read-envelope-from")))

That should be enough to do the very basics.

See M-x customize-group notmuch for more customization options.

Spam

The following is old and I haven’t used it in a couple of years. It may or may not work but I’ll keep it for posterity.

Spam Filtering

I’ve searched for ways to do this and found many suggestions. I happened upon this post on a notmuch list and this github project. I attempted to get notspam to work but had no luck. Instead of struggling to get it to behave, I instead wrote a Python script to do it (found in .scripts).

Setup Instructions

bogofilter

I decided to go with bogofilter for spam filtering. After much gnashing of teeth, I deduced the following information regarding installation:

First, you’ll need to install the correct version of Berkeley-DB; brew installs Berkeley DB version 6 as a dependency, but I’m not sure it actually works.

When running bogofilter -s < /dev/null, I got an error that read:

(null)
Can't open file 'wordlist.db' in directory '/Users/tminor/.bogofilter'.
error #22 - Invalid argument.

Make sure that the database version this program is linked against
can handle the format of the data base file (after updates in particular).

I found information in several places insinuating that only Berkeley DB version 4 is supported (bogofilter last appears to have been updated in 2013).

So. Install Berkeley DB version 4:

$ brew install berkeley-db@4

Then download bogofilter’s latest source files:

$ wget https://downloads.sourceforge.net/project/bogofilter/bogofilter-1.2.4/bogofilter-1.2.4.tar.bz2
$ tar -vxjf bogofilter-1.2.4.tar.bz2
$ cd bogofilter-1.2.4

Follow the INSTALL instructions. (I didn’t have to make any changes; it Just Worked [TM].)

You should get a return about the number of messages trained.

Training

To train bogofilter, I used the following steps:

In GMail, search for “label:promotions”, and apply a new filter; in my case, I added a label called “SpamTraining”. When mbsync grabs mail, it will create a new folder by that name under the maildir.

Tag all mail in that directory with notmuch:

$ notmuch tag +spam -- path:"gmail/SpamTraining/cur"

Now, use notmuch to train bogofilter:

# some spam
$ notmuch search --output=files tag:spam | xargs bogofilter -svB
# and some ham
$ notmuch search --output=files NOT tag:spam NOT path:gmail/SpamTraining/new | xargs bogofilter -nvB

After bogofilter has been trained, feel free to remove the spam samples from your machine.

Training with a .mbox file

Alternatively, you can export the mail using Google Takeout, which you can find here. You’ll have to follow the above steps and select the label as the desired target for downloading.

This is probably a good strategy for downloading samples for future training in case you need to retrain. I’m guessing the same procedure could be followed for training ham, but you’d have to figure out how to find ham and apply labels in GMail first. I found that -{label:promotions and label:social} worked reasonably well as a ham search query, but it still captured some stuff I didn’t care to have in my inbox.

About

Files and instructions related to my mail setup.


Languages

Language:Python 83.9%Language:Shell 16.1%