有馬総一郎のブログ

(彼氏の事情)

2021年09月01日 23:36:21 JST - 7 minute read - Linux

OfflineIMAPとNeoMuttを合わせて使う 後半(成功例)

前回の続き。そして、完成したのがこれ。

完成した設定ファイル

$HOME/.offlineimaprc

[general]
# List of accounts to be synced, separated by a comma.
accounts = main
# Controls how many accounts may be synced simultaneously
maxsyncaccounts = 1
# Path to file with arbitrary Python code to be loaded
pythonfile = ~/.config/neomutt/offlineimap.py

metadata = ~/.offlineimap

# In the account identifier
[Account main]
# Identifier for the local repository; e.g. the maildir to be synced via IMAP.
localrepository = main-local
# Identifier for the remote repository; i.e. the actual IMAP, usually non-local.
remoterepository = main-remote
# Minutes between syncs
autorefresh = 0.5
# Quick-syncs do not update if the only changes were to IMAP flags.
# autorefresh=0.5 together with quick=10 yields
# 10 quick refreshes between each full refresh, with 0.5 minutes between every 
# refresh, regardless of type.
quick = 10

[Repository main-local]
# Currently, offlineimap only supports maildir and IMAP for local repositories.
type = Maildir
# Where should the mail be placed?
localfolders = ~/Maildir

# In the remote repository identifier
[Repository main-remote]
type = Gmail
remoteuser = username@gmail.com
# Decrypt and read the encrypted password
remotepasseval = get_pass()
nametrans = lambda foldername: re.sub('^\[Gmail\]/&MLQw33ux-', 'ゴミ箱',
                               re.sub('^\[Gmail\]/&MLkwvzD8TtgwTQ-', 'スター付き',
                               re.sub('^\[Gmail\]/&Tgtm\+DBN-', '下書き',
                               re.sub('^\[Gmail\]/&kAFP4W4IMH8w4TD8MOs-', '送信済みメール',
                               re.sub('^\[Gmail\]/&kc2JgQ-', '重要',
                               re.sub('^&ZwmZrH3PTgCQzg-', '有馬総一郎', foldername))))))
folderfilter = lambda foldername: foldername in \
        [ 'INBOX', \
          '[Gmail]/&MLQw33ux-', \
          '[Gmail]/&MLkwvzD8TtgwTQ-', \
          '[Gmail]/&Tgtm+DBN-', \
          '[Gmail]/&kAFP4W4IMH8w4TD8MOs-', \
          '[Gmail]/&kc2JgQ-', \
          '&ZwmZrH3PTgCQzg-' ]
# Necessary as of OfflineIMAP 6.5.4
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
# Instead of closing the connection once a sync is complete, offlineimap will
# send empty data to the server to hold the connection open. A value of 60
# attempts to hold the connection for a minute between syncs (both quick and
# autorefresh).This setting has no effect if autorefresh and holdconnectionopen
# are not both set.
keepalive = 60
# OfflineIMAP normally closes IMAP server connections between refreshes if
# the global option autorefresh is specified.  If you wish it to keep the
# connection open, set this to true. This setting has no effect if autorefresh
# is not set.
holdconnectionopen = yes

解説

ちゃんとしたドキュメントはこちら

また、/usr/share/offlineimap/offlineimap.confにサンプルが、一緒にインストールされているので、そこのコメント(解説)を見るのも良い。

間違ってるかも知れないが、私の理解は以下のとおり。

metadataはデフォルトだが、一応、後ですぐ分かるように記述した。

パスワード暗号化

パスワードの暗号化は手順どおりにやれば上手くいった。

  1. gpgでアプリパスワードだけが記述された暗号化ファイル mailpass.gpg を作成する
  2. それを読み込むpythonスクリプトファイル offlineimap.py を作成する
  3. .offlineimaprcに[general]セクションにpythonfile = ~/.config/neomutt/offlineimap.pyと2.で作成したファイルを設定する
  4. [Repository main-remote]セクションでremotepasseval = get_pass()と記述する

offlineimap.py

#! /usr/bin/env python2
from subprocess import check_output

def get_pass():
    return check_output("gpg -dq ~/.config/neomutt/mailpass.gpg", shell=True).strip("\n")

惜しむらくは、neomuttrcset imap_pass="gpg -dq ~/.config/neomutt/passwds.gpg |"という記述ができれば、neomuttofflineimapで別々のパスワード暗号化用意する必要なく使い回せられるのになぁ。できないのか、やり方がまずいのか、自分の場合はアプリパスワードは同じのを使っているが別々に用意した。

nametrans

初め~/Maildir配下のフォルダが文字化け(UTF-7)で作成されていたので、驚いた。

第247回 Offlineimap+Dovecotによる快適メール環境:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社には、こうあった

IMAPではフォルダ名に修正UTF-7というコーディングを使っています。上記の例にある&MFkweTBmMG4w4TD8MOs-は,「⁠すべてのメール」の修正UTF-7での表現です。

とな。

しかし、ここでラベルを英語にしてしまうのは負けな気がしたのでecho "ラベル名" | iconv -t UTF-7を使って、参照したいラベル名のUTF-7表記名を調べ、日本語名で表示されるように設定した。

先頭の"+“を”&“にして末尾に”-“をつけることで,OfflineImapでのフォルダ指定に使用できます。

$ echo "有馬総一郎" | iconv -t UTF-7
+ZwmZrH3PTgCQzg
$ echo "+ZwmZrH3PTgCQzg" | iconv -f UTF-7
有馬総一郎

-fで元のテキスト表示できる。re.subは正規表現なので[とか+もエスケープの必要がある。

folderfilter

folderfilterは正規表現で記述する必要はない。フォルダ名は、そのまま記述するだけ。サブラベルは親ラベル/サブラベルと指定すれば良い。はずなんだけど、一点、解決できなかったのがドット.を含むラベル(フォルダ)がどうしても望む形でダウンロードできなかった。

親ラベル/S.Eというラベルだったのだけど、何故かS/Eとしないと、そのフォルダ名を指定したことにならなかった。しかも、これだとS親フォルダの下に子フォルダとしてEが作成されてしまった。

 [DRYRUN] Creating folder S[main-remote]
 [DRYRUN] Creating folder S/E[main-remote]

なら、フォルダ名をre.sub('^S/E', 'S_E',と変更しようとしても

ERROR: INFINITE FOLDER CREATION DETECTED! Folder 'S.E' (repository 'main-local') would be created as folder 'S/E' (repository 'main-remote'). The latter becomes 'S_E' in return, leading to infinite folder creation cycles.

とエラーメッセージが表示されて解決できなかった。負けを認め、おとなしくドット.は使わないことにした。

と、かなりここらへんの動きが複雑なので、いきなりofflineimapをするのではなくofflineimap --dry-runと試行運転するのが良い。

時間がめちゃくちゃかかる

で、参考にしたサイトのあちこちで、全てのメールの同期は止めて必要なものだけ同期するようにした方が良いとある。実際、9万件のメールを全て同期するのは 三日以上 かかった。

neomuttrc

offlineimapが完了したらいよいよそれをneomuttで読み込ませるわけだが、ここで詰まった。

ArchWikiには mutt 用に自動的にメールボックスを作成 として

$HOME/.offlineimaprc には[mbnames]セクションの記述が必要とある。

[mbnames]
enabled = yes
filename = ~/.mutt/mailboxes
header = "mailboxes "
peritem = "+%(accountname)s/%(foldername)s"
sep = " "
footer = "\n"

そして、neomutt側でも以下を追記するような例が書かれている。 $HOME/.config/neomutt/neomuttrc

# IMAP: offlineimap
set folder = "~/Mail"
source ~/.mutt/mailboxes
set spoolfile = "+account/INBOX"
set record = "+account/Sent\ Items"
set postponed = "+account/Drafts"

しかし、offlineimapを実行してもfilenameで指定したファイルが作成されない。なので、neomuttsource ~/.mutt/mailboxesの部分でエラーとなる。

mbnamesの意味として

Mutt cannot be simply pointed to an IMAP or maildir directory and be expected to guess which subdirectories happen to be the mailboxes, yet offlineimap can generate a muttrc fragment containing the mailboxes that it syncs.
MuttはIMAPまたはmaildirディレクトリを単純に指すことはできず、どのサブディレクトリがメールボックスであるかを推測することは期待できませんが、offlineimapは同期するメールボックスを含むmuttrcフラグメントを生成できます。

みたいなことがドキュメントには書いてある。しかし、結果として自分の場合は[mbnames]セクションの記述は不要で、source ~/.mutt/mailboxesの部分も必要なかった。

$HOME/.config/neomutt/neomuttrc

set mbox_type       = "Maildir"                            # mbox=複数のメッセージが連結した形式で保存
                                                           # maildir=mail1通がファイル1個
set folder          = $HOME/Maildir                        # 受信メールの読み込み先(993番ポート=SSL対応)
#set folder         = "imaps://imap.gmail.com:993"
set spoolfile       = +INBOX
set postponed       = "+下書き"
#set record          = "+送信済みメール"
unset signature                                            # 署名なし
mailboxes =INBOX \
="ゴミ箱" \
="スター付き" \
="下書き" \
="送信済みメール" \
="重要" \
="有馬総一郎"
#set imap_check_subscribed                                 # 全てのサブフォルダを列挙

[mbnames]セクションがどういう時に必要なのか分からないが、変更部分としては以上となる。folderの設定をofflineimaplocalfoldersと同じに。mailboxesは、同期したフォルダを設定した。

set recordを設定していないのは、[Gmail]の送信済みメールに追加させると、二重になってしまったから。恐らくDraftsだと平気だと思われる。

unset signatureは、今回の件と関係ないが、設定なしとしないと~/.signatureファイルがないと叱られたので。

端末起動時に立ち上げる

デーモンとしてバックグラウンドでofflineimapを起動、同期させるやりかたも ArchWikiに載っている。恐らくはその通りにやれば、まずは成功するだろう1。自分はi3設定ファイルにexec --no-startup-id offlineimapと記述して済ませてしまったが。

実際速くなったのか?

ということで何とか、手間暇かけてneomuttofflineimapを組み合わせて使っているが、正直、開始が2、3秒。ラベルの変更時間が短縮されたというぐらいかなぁ。正直、頑張った分だけ報われたと思えないが、速くはなったので良し!


  1. 自分の場合は、何故かFailed to enable unit: File default.target: Identifier removedとエラーが出たのでデーモンによる同期は諦めた。 ↩︎