Setting up multiple gmail accounts in Emacs

Table of Contents

Get a dummy password from Gmail

For your Gmail account do the following things first:

  1. Enable 2 step authentication
  2. Get an app password - This will help you to access your account without going through 2 step authentication. This can be deleted and regenerated when you think your account has been compromised.

Let's assume that the password generated was thisismysecretpw.

Generate GPG keys

This will be used to encrypt files containing sensitive information like authinfo.gpg which will be used to store the app password.

gpg --gen-key

Adding information to authinfo.gpg

If you have gpg keys then Emacs will take care of encryption and decryption when you open any file with .gpg extension. Add the following lines to your ~/authinfo.gpg file.

machine smtp.gmail.com login mailid@gmail.com port 25 password thisismysecretpw
machine imap.gmail.com login mailid@gmail.com port 993 password thisismysecretpw

The first line will be used to send mail and the second will be used to fetch new mail. If you have more accounts, just add similar lines for them too.

Fetching using mbsync

First install the package from official repos or build from source.

Create .mbsyncrc

IMAPAccount gmail-mailid
Host imap.gmail.com
User mailid@gmail.com
PassCmd "gpg --for-your-eyes-only --no-tty -qd ~/.authinfo.gpg | grep imap.gmail.com | grep mailid | awk '{ for (i=1; i<NF; i++) { if ($i == \"password\") print $(i+1) } }'"
# To store the password in an encrypted file use PassCmd instead of Pass
# PassCmd "gpg2 -q --for-your-eyes-only --no-tty -d ~/.mailpass.gpg"

# Use SSL
SSLType IMAPS
# The following line should work. If get certificate errors, uncomment the two
# following lines and read the "Troubleshooting" section.
CertificateFile /etc/ssl/certs/ca-certificates.crt
#CertificateFile ~/.cert/imap.gmail.com.pem
#CertificateFile ~/.cert/Equifax_Secure_CA.pem

IMAPStore gmail-mailid-remote
Account gmail-mailid

MaildirStore gmail-mailid-local
Subfolders Verbatim
# The trailing "/" is important
Path ~/.mail/gmail/mailid/
Inbox ~/.mail/gmail/mailid/inbox

Channel gmail-mailid-default
Master :gmail-mailid-remote:
Slave :gmail-mailid-local:Inbox

Channel gmail-mailid-sent
Master :gmail-mailid-remote:"[Gmail]/Sent Mail"
slave  :gmail-mailid-local:Sent

Channel gmail-mailid-trash
Master :gmail-mailid-remote:"[Gmail]/Trash"
slave  :gmail-mailid-local:Trash

Channel gmail-mailid-archive
Master :gmail-mailid-remote:"[Gmail]/All Mail"
slave  :gmail-mailid-local:All

Channel gmail-mailid-junk
Master :gmail-mailid-remote:"[Gmail]/Spam"
slave  :gmail-mailid-local:Junk

# Automatically create missing mailboxes, both locally and on the server
Create Both
# Automatically delete messages on either side if they are found deleted on the other
Expunge Both
# Save the synchronization state files in the relevant directory
SyncState *

If you have multiple accounts the above block can be repeated by just replacing all mailid instances with your second mail id.

Fetch your mail

To fetch all your mail just run

mbsync -a

Automate fetching (For Linux users)

Create ~/.config/systemd/user/mbsync.service with the following contents:

[Unit]
Description=Mailbox synchronization service

[Service]
Type=oneshot
ExecStart=/usr/bin/mbsync -Va
ExecStartPost=/usr/bin/notmuch new

Create ~/.config/systemd/user/mbsync.timer with the following contents:

[Unit]
Description=Mailbox synchronization timer

[Timer]
OnBootSec=2m
OnUnitActiveSec=3m
Unit=mbsync.service

[Install]
WantedBy=timers.target

Start and enable(will automatically run it after you log in) the service:

systemctl --user start mbsync.timer
systemctl --user enable mbsync.timer

notmuch to search your mails

Set it up

notmuch

When asked for mail directory, mention ~/.mail which I used in my ~/.mbsyncrc. This directory has all my accounts so notmuch can index all of it.

Index new mail

notmuch new

Emacs configuration

Default user name to use when sending mail using C-x m:

(setq
 user-mail-address "mailid@gmail.com"
 user-full-name "First Last")

Make sure Emacs always uses ~/.authinfo.gpg:

(setq auth-sources (list (expand-file-name "~/.authinfo.gpg")))

I have used leaf instead of use-package as the author claims to get it merged into core Emacs some day. Don't worry, its lightweight and makes it easy to port existing use-package configuration blocks.

(leaf message
  :hook (message-send-hook . compro/message/change-smtp-settings)
  :bind ((message-mode-map
          ("C-c <C-tab>" . compro/message/toggle-from-header)))
  :preface
  (defun compro/message/toggle-from-header ()
    "Toggle between two mail address that I have."
    (interactive)
    (save-excursion
      (save-restriction
        (message-narrow-to-headers)
        (while (not (looking-at "From: "))
          (message-next-header))
        (let ((from (message-fetch-field "from")))
          (search-forward from)
          (if (string-match-p (regexp-quote "mailid@gmail.com") from)
              (replace-match "First2 Last2 <mailid2@gmail.com>")
            (replace-match "First Last <mailid@gmail.com>"))))))
  (defun compro/message/change-smtp-settings ()
    "Change any SMTP details according to the current From line.
For me it was only `smtpmail-smtp-user'. Other cases include
`smtpmail-default-smtp-server' or `smtpmail-smtp-service' which
is not configured in my implementation.

This function needs to be added to `message-send-hook' to change
details before sending the message."
    (save-excursion
      (save-restriction
        (message-narrow-to-headers)
        (let* ((from-field (message-fetch-field "from"))
               (from-mailid
                (when from-field
                  (cadr (mail-extract-address-components
                         (message-fetch-field "from"))))))
          (if from-mailid
              (setq smtpmail-smtp-user from-mailid)
            (error "Can't fetch `From' field from message headers"))))))
  :init
  (setq message-alternative-emails (regexp-opt '("mailid@gmail.com"
                                                 "mailid2@gmail.com"))
        message-send-mail-function 'smtpmail-send-it)
  )
(leaf smtpmail
  :init
  (setq smtpmail-smtp-service 25
        smtpmail-default-smtp-server "smtp.gmail.com"))

While composing a message(using C-x m) just press C-c C-tab to toggle between mail ids. This can be changed in the compro/message/toggle-from-header function.

Notmuch configuration is a bit challenging as it doesn't provide a standard way to delete a message.

(leaf notmuch :ensure t
  :bind ((notmuch-search-mode-map
          ("d" . compro/notmuch/tag-as-deleted)
          ("<delchar>" . compro/notmuch/tag-as-deleted)
          ("u" . compro/notmuch/remove-deleted-tag)
          ("D" . compro/notmuch/remove-deleted-tag)
          ("f" . compro/notmuch/tag-as-flagged)
          ("F" . compro/notmuch/remove-flagged-tag)))
  :init
  (fset 'compro/notmuch/tag-as-deleted
        (kmacro-lambda-form [?+ ?d ?e ?l ?e ?t ?e ?d return] 0 "%d"))
  (fset 'compro/notmuch/remove-deleted-tag
        (kmacro-lambda-form [?- ?d ?e ?l ?e ?t ?e ?d return] 0 "%d"))
  (fset 'compro/notmuch/tag-as-flagged
        (kmacro-lambda-form [?+ ?f ?l ?a ?g ?g ?e ?d return] 0 "%d"))
  (fset 'compro/notmuch/remove-flagged-tag
        (kmacro-lambda-form [?- ?f ?l ?a ?g ?g ?e ?d return] 0 "%d")))

To delete a message first tag them using d then run:

rm -f `notmuch search --output=files --exclude=false tag:deleted`

You can add this command to mbsync.service (we created initially) to automatically delete tagged mails before re-indexing every 3 minutes. So, the line:

ExecStartPost=/bin/sh -c "/usr/bin/notmuch search --output=files --exclude=false tag:deleted | /usr/bin/xargs /bin/rm -f"

should be added below:

ExecStartPost=/usr/bin/notmuch new

I recommend not deleting mail automatically as it might be a disaster when you tag the wrong mail. You can instead delete them when you have time to manually run the command.

To start searching you can use M-x notmuch :) That's all.

Date: 2019-07-14

Author: Compro Prasad

Created: 2019-07-15 Mon 20:46

Validate