Daydreaming in Brookline, MA

emacs+notmuchでメールを読み書きする

1 はじめに

私はメーラーとしてgmailを使っていますが、gmailのUIにどうしても馴染めません。そこで、emacsでemailを読み書きできるようにしてみました。

メーラーは一番人気のmu4e、、、ではなく、次点の notmuch を選びました。notmuchがtramp-modeに対応していて、リモートのメールボックスにアクセスできることが決め手でした。notmuchの日本語情報は意外と少ないようなので、参考になれば幸いです。

2 インストール、設定

notmuchはそれだけではメールの読み書きができるようになりません。その他に以下のツールを使用します。

  • isync/mbsync - isyncがパッケージ名でmbsyncはコマンド名です。クラウドにあるgmailのデータをimap経由でローカルディレクトリに同期します
  • afew - メール取り込み時に、ルールに従って自動でタグを付けるヘルパーツールです。今回はインストールだけしました。おいおい拡充していくつもりです
  • smtp - メール送信はemacs標準のsmtpライブラリを使います。

2.1 構成

Macのemacs上でメールを読み書きすることが目的ですが、メールボックスをプレーンテキストでMac上にじか置きしたくないので、VirtualBoxのFedora 36 VMにメールを取り込み、そこにtrampを使ってリモートアクセスします。このため、まずはFedora上に一式環境を作り、その後Macのemacs設定をします。

trampを使ってリモートのメールボックスにアクセスすることは、複数のメールクライアント(eg, emacs+notmuch)から共通のメールボックスにアクセスしたい、というユースケースがありそうですね。

2.2 mbsync

パッケージマネージャーを使ってisyncをインストールします。

sudo dnf install isync
  1. <2023-06-29 Thu>追記

    AlmaLinux 9ではdnf installできませんでした。ので、公式のソースから入れます。

    tar xzf isync-1.4.4.tar.gz
    
    • INSTALLファイルを見てインストール
    ./configure
    make
    sudo make install
    

    なお、後から気がついたのですが、epelには isync がおいてありました。最初にepelの設定をしておけばよかったです。。。

    今回は使いませんが、macの場合は以下です。

    brew install isync
    

    次に、googleのimapサーバーが信頼できることを証明するgmail certificateが必要になるので、取得してファイルにセーブします。このファイルは、後でmbsyncのconfigファイルで指定します。 https://apple.stackexchange.com/questions/256898/ssl-error-with-mbsync

    openssl s_client -connect imap.gmail.com:993 -showcerts 2>&1 < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | head -n 27 > gmail.crt
    sudo mv gmail.crt /etc/ssl/certs/
    

    上記は、 imap.gmail.com:993 がアドバタイズしているcertから、サーバーcertのみを切り出しています。追記: Macから実行したときは head -n 31 でした。どうして???

    そして、googleのappパスワード(16文字)を このリンク を参考に生成、値を控えておきます。googleのimapサーバーにアクセスするためには、普段使っているgoogleパスワードは使えないので。

    最後に、mbsyncのconfigファイル ~/.mbsyncrc を作成します。これなしではmbsyncは動きません。configファイルを理解するためには、 man mbsync に一通り目を通しておくことをおすすめします。

    # 参考: https://troglobit.com/post/2021-01-13-my-email-setup/
    # Name Account
    IMAPAccount gmail
    Host imap.gmail.com
    User <your_gmail>@gmail.com
    Pass "<16文字のappパスワード>"
    SSLType IMAPS
    SSLVersion TLSv1.2
    # AuthMechs PLAIN  # エラーが出るようになったのでコメントアウトします(6/29/2023)
    CertificateFile /etc/ssl/certs/gmail.crt
    #PipelineDepth 1  # デバッグ時にコメントアウトします
    
    IMAPStore gmail-remote
    Account gmail
    
    MaildirStore gmail-local
    SubFolders Verbatim
    # The trailing "/" is important
    Path ~/mail/gmail/
    Inbox ~/mail/gmail/Inbox
    
    Channel sync-gmail-default
    Far :gmail-remote:
    Near :gmail-local:
    # Select some mailboxes to sync
    Patterns "INBOX"
    
    Channel sync-gmail-sent
    Far :gmail-remote:"[Gmail]/Sent Mail"
    Near :gmail-local:sent
    Create Near
    SyncState *
    MaxMessages 1000
    ExpireUnread yes
    
    Channel sync-gmail-trash
    Far :gmail-remote:"[Gmail]/Trash"
    Near :gmail-local:trash
    Create Near
    SyncState *
    
    # Get all the channels together into a group.
    Group gmail
    Channel sync-gmail-default
    Channel sync-gmail-sent
    Channel sync-gmail-trash
    

    早速、実行してみます。

    mbsync -a
    

    しばらく動いた後で、下記のエラーで止まってしまいました。

    IMAP error: unexpected BYE response: Invalid character in literal
    Socket error: secure read from imap.gmail.com (172.253.62.109:993): error:0A000126:SSL routines::unexpected eof while reading
    

    最初のエラーは、正しくフォーマットされていないメールがある、というもの。

    PipelineDepth 1
    

    の行をコメントアウトして多重度を1にし、デバッグしやすくします。

    デバッグモードで再度mbsync実行します。

    mbsync -a -V -DCmMNs &> mbsync_log.txt
    

    エラーの元となったメールがヘッダー共に表示されたので、メッセージIDでgmailを検索します。

    rfc822msgid:<メッセージID>
    

    2015年(!)の某テニススクールからのメールでした。ここのスクールは、私宛リマインダーメールのfrom行を私のメールアドレスに設定したり、よくgmailから怪しいメール認定されているところです(迷惑)。確かに、ここならあり得る。。。

    当該メールを完全に削除して、 mbsync -a を再度実行してもエラーは消えません。おかしいな。。。 さんざんネットを検索、トラブルシュートしたところ、既に取り込み済みの同メールがひっかかっていることに気が付きました。一通りローカルのメールフォルダを空にして mbsync -a で全取り込み。やっと最初のエラーがでなくなりました。

    unexpected eofのソケットエラーは相変わらず出ていますが、全て取り込み終わり、ログアウトしてから出ているようなので、無視しても良さそうです。とりあえず放置します。これで、gmailのメールをFedora VMに持ってくることができるようになりました。

    なお、ここでは比較的さらっと問題解決したように書きましたが、実際には2週間ほどはまり、ギブアップ直前まで行きました。やみくもにエラーメッセージを検索するだけでは限界がありますね。。。

    参考

2.3 notmuch

mbsyncでメールのデータを取り込んだので、notmuchでそれらのデータベースを作ってアクセス可能にします。

Fedora上でインストールと初期化します。

sudo dnf install notmuch
notmuch setup  # 名前やメールアドレスの情報を聞かれます
notmuch new

emacsの設定をします。 ~/.emacs.d/init.el に以下を追記します。

;; ====== email ============================================

(use-package notmuch
  :commands notmuch-hello
  :bind (("C-c m" . notmuch-hello)))

いつものように、 use-packageがうまくnotmuchパッケージをダウンロードしてくれず、 M-x list-packages から手動で notmuch をインストールしました。

emacs上で M-x notmuch とするとメールが読めるようになりました。ものすごく原始的な感じです。。

2.4 afew

自動タグ付けツールです。とりあえずインストールだけします。

sudo dnf install python-notmuch
cd ~/py
python -m venv --system-site-packages .venv
source .venv/bin/activate
pip install afew

2.5 smtp

ここまででメールは読めるようになりましたが、メールを出すにはsmtpの設定が必要です。 ~/.emacs.d/init.el に追記します。

(setq user-full-name "<your fullname>")
(setq user-mail-address "<yourgmail>@gmail.com")
(setq message-directory "~/mail"
      notmuch-fcc-dirs nil
      read-mail-command (quote notmuch)
      message-citation-line-format "On %a, %b %d, %Y at %R, %f wrote:"
      message-citation-line-function 'message-insert-formatted-citation-line
      )

(setq message-send-mail-function 'smtpmail-send-it
      smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil))
      smtpmail-auth-credentials '(("smtp.gmail.com" 587 "<yourgmail>@gmail.com" nil))
      smtpmail-default-smtp-server "smtp.gmail.com"
      smtpmail-smtp-server "smtp.gmail.com"
      smtpmail-smtp-service 587
      starttls-use-gnutls t
      )

いよいよメールを送るテストをします。emacs から M-x notmuch > m (send email)としてメールを書き、 C-c C-c で送ります。すると、imap.gmail.comの認証のために以下を聞かれるので入力します。

username: <full gmail address>
password: <app password>
Save auth info to file ~/.authinfo? y

app passwordはgoogleが用意している仕組みで、純正gmailクライアント以外からgoogleのSMTPサーバーを使う際に使う、16文字の特別なパスワードです。これはgoogleのアカウントページ上で作成します。 https://myaccount.google.com/ > Security > Signing in to Google > App passwords。

上記パスワードには、通常のgoogleアカウントパスワードでなく、このappパスワードを使わないと 535 5.7.8 エラーが出ます。参考

Sending failed: 535-5.7.8 Username and Password not accepted.  Learn more at 535 5.7.8  https://support.google.com...<snip>

無事にFedora VM上のemacsでメールが送付できました。

2.6 Macからリモートのメールボックスを使う

やっと、本来やりたかったMac上の設定に入ります。リモート(と言ってもMacホスト上で動くVMですが)のメールボックスをtrampを使ってアクセスします。基本は: Using notmuch remotely に従って設定します。人気のmu4eでなくnotmuchを選んだのは、最初からこれを使うつもりだったからです。mu4eはtrampと相性が悪い(動かない)という書き込みを目にしていたので。。。

Mac(client)にnotmuchインストールします。

brew install notmuch

Mac上でssh設定します。 ~/.ssh/config に以下を追記:

Host notmuch
    HostName 127.0.0.1
    Port 2223
    User <remote_username>
    ControlMaster auto
    ControlPath ~/.ssh/master-%h@%p:%r
    ControlPersist 15m

注意: Fedora VMは、localhost (127.0.0.1)のポート 2223にマップしてあります。

ここで、いったんMacからリモートにパスワードなしでsshできるか確認しておいてください。

Mac上で ~/bin ディレクトリが無ければ作って、そこに remote-notmuch.sh として以下をセーブし、実行可能に(chmod +x)します。

#!/bin/bash
printf -v ARGS "%q " "$@"
exec ssh notmuch notmuch ${ARGS}

~/.emacs.d/init.el 追記:

(use-package notmuch
  :commands notmuch-hello
  :bind (("C-c m" . notmuch-hello)))
(setq notmuch-command "~/bin/remote-notmuch.sh")

(setq message-send-mail-function 'smtpmail-send-it
      smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil))
      smtpmail-auth-credentials '(("smtp.gmail.com" 587 "<yourgmail>@gmail.com" nil))
      smtpmail-default-smtp-server "smtp.gmail.com"
      smtpmail-smtp-server "smtp.gmail.com"
      smtpmail-smtp-service 587
      starttls-use-gnutls t
      )

4行目でnotmuch-commandに上で作成したbashスクリプトを指定している以外はそのままFedora emacsのinit.elからコピペしています。

それでは、試してみます。Macのemacsから M-x notmuch で、何の問題もなくFedora上に取り込んであるメールが見られました。さすが trampさん、すごいです! メールを閲覧している時に r で返信できます。返事を書いて C-c C-c で送信すると、これもうまくいってしまいました。

  1. <2022-09-15 Thu>追記

    notmuch setupしていないんじゃない? のようなエラーがでてM-x notmuchに失敗した場合は、リモートにパスワードなしで問題なくsshできるか確認してみてください。これが原因で長いことはまりましたが、おそらく初回ssh接続時にknown_hostsに登録するための確認プロンプトが出ると失敗するようです。

3 終わりに

今回は、Mac上のemacs+notmuch から、Linux VM上に同期したgmailのメールを読み書きする構成を試してみました。タグ付けなどまだ使いこなしていませんが、曲りなりにemacsでメールの読み書きができるようになって満足しています。