2020-12-17 Rmail

January 2015 I decided I had enough. I downloaded all my mail from Gmail and deleted everything on the server. I kept using it however. I justed wanted a “clean email day”.

Over the years, I’ve started trying to keep the principles of data parsimony in mind:

Data is toxic. It’s a bit like being your own record keeper. You know the ones: the narcissists that keep records of every conversation you’ve ever held so that they can prove you wrong instead of treating you like a human being. That’s why I don’t want to keep it. I don’t want to treat others like this. If somebody has a bad day and sends me an angry email, I hope to delete it like all the other emails, and years later I won’t be able to find it anymore, I won’t remind myself of the words they said, I’ll hopefully be able to forgive, and I’m setting myself up to forget.

So, I stopped using the Gmail web client. Google the data kraken, Google the spearhead of surveilance capitalism. I don’t want to live in front of the camera with algorithms watching my every move, using the data they gather about me to manipulate me into voting for this or for that, or to buy this or that. “And lead us not into temptation, but deliver us from evil.” And remember, “Don’t be evil” was a part of Google’s Code of Conduct from 2000 to 2018. They removed it.

Currently, I’m using Migadu as my email provider. For a while I tried to use Gnus (which is part of Emacs) to read mail, with the IMAP backend. Sadly I ran into problems with disappearing mail and had to stop. I was a big Gnus fan back in the days. Time to let go. I also tried using aerc and that works pretty well. It’s not super easy to setup with Emacs but it works. Except it crashes when I get encrypted mail, and that’s not good. And in order to build it from source, I need a new Go development environment. The one that comes with my distro is not good enough. How annoying. So I’ve been using Evolution, particularly when using encrypted mail. But these graphical user interfaces drive me nuts. Tiny target areas to click a gazillion times to get anything done. Aggravating!

Recently, @cadadr wrote a blog post explaining his Rmail setup. Rmail is this stupid simple mbox based mail client. I used it for a tiny bit as I got into Emacs back in the nineties before moving on to Gnus. A few years later, I tried to make some changes to Rmail (back then it used the BABYL format which was similar but incompatible to the mbox format; and I wanted to add MIME handling). Anyway, I failed and abandoned the code. Yikes! I did not have good Rmail memories.

But Göktuğ’s blog post was like a siren song: I have good key bindings for Rmail, it said. You don’t need to fiddle with postfix or sendmail, it whispered. I can search your mails, it sang. I had to try it. And I loved his introduction: “This blog post instead is about how I deal with a humane level of email messaging, with a local-first approach, using a smallish variety of software, which you can just set up and forget about.” This is beautiful!

The result is part of my Emacs config files. I skipped the procmail setup. I made some other changes. But essentially, it’s the same thing. Instead of “pass” I’m using “gpg” to decrypt my email passwords (“passwordeval gpg --no-tty --quiet --decrypt ~/.config/mpop/password.gpg”). And slowly I’m adding more. Today I added code to move mails to a trash mbox instead of expunging it because today was the first time I got email with an important piece of information that I deleted before recognising later in the day that I needed it: and there was no mail trash to look at. It was simply gone. Eventually I shall write some code that expunges old mail from the trash file. I’m giving myself thiry days. 😆

Here’s what I use for working with aerc. It’s a new “eml-mode” allows me to use ‘C-c C-c’ to send email instead of having to use ‘C-x #’. That’s simply cumbersome to use and ‘C-c C-c’ is used everywhere else…

;; I use aerc to read mail and emacsclient as my mail editor

(define-derived-mode eml-mode mail-mode "Email"
  "Write emails via emacsclient."
  (when (buffer-file-name)
    (set (make-local-variable 'server-temp-file-regexp) ""))
  (local-set-key (kbd "C-c C-c") 'server-edit)
  (auto-fill-mode 1))

(add-to-list 'auto-mode-alist '("\\.eml\\'" . eml-mode))

And here’s the Rmail section where I set up the directories and files (the trash file is not standard). “mpop” delivers mail into the “inbox”, and Rmail then moves it from there into the “current” file. If I decide to keep it, I’ll move it into the “archive” file.

(setq rmail-primary-inbox-list '("/home/alex/mail/inbox"); where mail comes from, delivered by mpop
      rmail-file-name "/home/alex/mail/current"; where I read mail
      rmail-secondary-file-directory "/home/alex/mail"; where the other folders are
      rmail-secondary-file-regexp "spam\\|sent\\|archive$"; the interesting other folders
      rmail-default-file "/home/alex/mail/archive"; where mail I want to keep goes
      ;; I don't differentiate between spam and trash anymore because
      ;; I'm relying on my provider's spam filter. I'm not running a
      ;; local spam filter.
      rmail-trash-file "/home/alex/mail/trash")

I like the brutalist-dark theme, and have made some changes over there:

 '(message-header-cc ((t (:inherit bold))))
 '(message-header-name ((t (:inherit default))))
 '(message-header-newsgroup ((t (:inherit default))))
 '(message-header-other ((t (:inherit default))))
 '(message-header-subject ((t (:inherit bold))))
 '(message-header-to ((t (:inherit bold))))
 '(message-header-xheader ((t (:inherit default))))
 '(message-cited-text-1 ((t (:inherit default))))
 '(message-cited-text-2 ((t (:inherit default))))
 '(message-cited-text-3 ((t (:inherit default))))
 '(message-cited-text-4 ((t (:inherit default)))))

And I also needed to change one of these variables because unread mail was using font-lock-type-face, which is underlined in my programming setup. So I wanted something different. I decided to copy the whole assignment as I don’t want to load Rmail when I start Emacs and I was too lazy to write the eval-after-load invocation.

(defvar rmail-summary-font-lock-keywords
  '(("^.....D.*" . font-lock-string-face)			; Deleted.
    ("^.....-.*" . 'bold)				        ; Unread.
    ;; Neither of the below will be highlighted if either of the above are:
    ("^.....[^D-] \\(......\\)" 1 font-lock-keyword-face)	; Date.
    ("{ \\([^\n}]+\\) }" 1 font-lock-comment-face))		; Labels.
  "Additional expressions to highlight in Rmail Summary mode.")

Well, the joy was short because soon after there it is. The rmail-mode-map isn’t defined so if I want to change the key maps, I have to delay this.

(eval-after-load "rmail"
     (define-key rmail-mode-map "<" 'asc:fetch-mail)
     (define-key rmail-mode-map "A" 'asc:archive-mail)
     (define-key rmail-mode-map "d" 'asc:trash-mail)
     (define-key rmail-mode-map "$" 'asc:trash-mail)
     (define-key rmail-mode-map "S" 'mairix-search)
     (define-key rmail-mode-map (kbd "<tab>") 'asc:rmail-forward-link-or-button)
     (define-key rmail-mode-map (kbd "<backtab>") 'asc:rmail-backward-link-or-button)))

The same is true for Message mode, which is used to write mails.

(eval-after-load "message"
     (define-key message-mode-map (kbd "C-c C-c") 'message-send)))

And the summary buffer:

;; Redefining quit
(eval-after-load "rmailsum"
     (define-key rmail-summary-mode-map "q" 'bury-buffer)))

Global stuff is OK:

(global-set-key (kbd "C-c <") 'asc:fetch-mail)

Then some functions to fetch mail, and to move mail.

(defun asc:fetch-mail ()
  "Run mail retrieval scripts."
   :name "fetch-mail" :buffer (get-buffer-create "*Fetch Mail*")
   :command (list "mpop" "-Q" "-a")
   (lambda (process event)
     (unless (process-live-p process)
       (when (zerop (process-exit-status process))
         (if (> (file-attribute-size (file-attributes (car rmail-primary-inbox-list))) 0)
	     (message "New mail!")
	   (message "No new mail.")))))))

(defun asc:archive-mail ()
  "It will move the current message to ‘rmail-default-file’
This command will not run unless in an RMAIL buffer visiting
  (unless (string= (buffer-file-name) rmail-file-name)
     "This is not your default RMAIL file"))
  (rmail-output rmail-default-file)

(defun asc:trash-mail ()
  "It will move the current message to ‘rmail-trash-file’
If you're currently visiting the trash file, then it will be
  (unless (string= (buffer-file-name) rmail-trash-file)
    (rmail-output rmail-trash-file))

I did not know about mairix, the tool used for searching mail. The way it is used is that you run a search and it writes a temporary mbox for you, which is then opened with Rmail. You’re basically reading a copy of the emails matching your search terms. I love this.

(setq mairix-file-path "/home/alex/mail/mairix"
      mairix-search-file "search")

(autoload 'mairix-search "mairix" "Call Mairix with SEARCH.")

Not sure I actually need these. Stuff to navigate not just to the buttons (to hide and show the various parts in multipart emails) but also to the links.

(require 'cl-lib)

(defun asc:rmail-forward-link-or-button (p)
  "Navigate both links and buttons in Rmail in a ring.
This replaces the use of ‘forward-button’ which only traverses
buttons and skips over links."
  (interactive (list (point)))
  (let (positions)
    (dolist (overlay (overlays-in (point-min) (point-max)))
      (when (memq (car (overlay-properties overlay))
                  '(goto-address button))
        (pushnew (overlay-start overlay) positions)))
    (setq positions (sort positions #'<))
    (if (>= p (car (last positions)))
        (goto-char (first positions))
      (goto-char (find-if (lambda (x) (> x p)) positions)))))

(defun asc:rmail-backward-link-or-button (p)
  "Navigate both links and buttons in Rmail in a ring.
This replaces the use of ‘forward-button’ which only traverses
buttons and skips over links.
This is the reverse counterpart of
  (interactive (list (point)))
  (let (positions)
    (dolist (overlay (overlays-in (point-min) (point-max)))
      (when (memq (car (overlay-properties overlay))
                  '(goto-address button))
        (pushnew (overlay-start overlay) positions)))
    (setq positions (sort positions #'<))
    (if (<= p (first positions))
        (goto-char (car (last positions)))
      (goto-char (find-if (lambda (x) (< x p)) positions :from-end t)))))

And here’s sending mail. We need the FCC header to keep a copy of the mails we send. It’s yet another mbox file. Easy! And I want to hide the “In-Reply-To” header.

(setq message-send-mail-function 'message-send-mail-with-sendmail
      message-sendmail-f-is-evil t
      message-sendmail-envelope-from 'header
      sendmail-program "msmtp"
      message-default-headers "Fcc: /home/alex/mail/sent"
      message-auto-save-directory "/home/alex/mail/drafts"
      message-confirm-send t
      message-hidden-headers '("^References:" "^X-Draft-From:" "^In-Reply-To:"))

(add-hook 'message-sent-hook 'bury-buffer)

Receiving mail uses mpop (meaning that the mails are also removed from the server!) and sending mail uses msmtp. None of that complex local mail infrastructure setup, as I said. Those things you can leave like your distro set them up, locally. Don’t touch them! I like it.

As for the setup of mpop and msmtp, I use the following for “~/.config/mpop/config”:

tls on
tls_starttls off
keep off
account default
host pop.migadu.com
port 995
user alex@alexschroeder.ch
delivery mbox "/home/alex/mail/inbox"
passwordeval gpg --no-tty --quiet --decrypt ~/.config/mpop/password.gpg

Note the “password.gpg” file containing nothing but the mail password, encrypted for my GPG key.

And this is “~/.config/msmtp/config”:

auth on
tls on
tls_starttls off
protocol smtp
account default
host smtp.migadu.com
port 465
from alex@alexschroeder.ch
user alex@alexschroeder.ch
passwordeval gpg --no-tty --quiet --decrypt ~/.config/mpop/password.gpg

Anyway, thanks again, Göktuğ. 😃


Hi! Glad it was useful to you!!

– Göktuğ “the other guy who uses Rmail” Kayaalp 2020-12-19 18:08 UTC

Actually Using Emacs Rmail, by Muto.

– Alex 2021-04-03 22:37 UTC

In my ~/mail directory I have a Makefile with the following:

	mail-expire 30 archive sent
	mail-expire --delete 30 trash

The mail-expire tool will take all the mails older than 30 days from the archive and sent files and move them to timestamped files such as archive.Mar_2021.gz and sent.Mar_2021.gz (I hate the naming convention and might have to change that, haha!), and it deletes all messages older than 30 days from trash.

– Alex 2021-04-10 06:40 UTC

Please make sure you contribute only your own work, or work licensed under the GNU Free Documentation License. Note: in order to facilitate peer review and fight vandalism, we will store your IP number for a number of days. See Privacy Policy for more information. See Info for text formatting rules. You can edit the comment page if you need to fix typos. You can subscribe to new comments by email without leaving a comment.

To save this page you must answer this question:

Just say HELLO