Mail 環境の設定

Related Index Debian

_始めに

メールに関するアレコレ. ネットワーク接続を意識したくないので, 送受信用のサーバを手元に置いて MUA はなるべくここを叩くようにしている.

_送信設定: Exim4

smarthost での送信だけなら Exim4 が軽量で楽。 他にも幾つか選択肢はある。気になるなら Debian Wiki: DefaultMTA あたり参照のこと。 欲しい機能は

なので、軽いしとりあえず Exim4 をそのまま使っている。

$ sudo -s
# dpkg-reconfigure -plow exim4-config
  メール設定の一般的なタイプ: 2. スマートホストでメール送信; SMTP または fetchmail で受信する
  システムメール名: localhost
  入力側 SMTP 接続をリスンする IP アドレス: 127.0.0.1
  メールを受け取るその他の宛先: [空白]
  メールをリレーするマシン: [空白]
  送出スマートホストの IP アドレスまたはホスト名: スマートホスト用の送信サーバ::587
  送出するメールでローカルメール名を隠しますか? no
  DNS クエリの数を最小限に留めますか (ダイヤルオンデマンド)? no
  ローカルメールの配送方式: 1. /var/mail/ 内の mbox 形式
  設定を小さなファイルに分割しますか?: no
  root と postmaster のメール受信者: 適当に設定

そのあと, /etc/exim4/passwd.client 内で, スマートホストの設定

スマートホスト送信サーバ:[アカウント]:[パスワード]

そして必要なら /etc/exim4/email-addresses に置換するリストを適当に記述. 最低限 root と ユーザ宛(uid=1000) のメール送信先は設定しておく.

$ sudo -s
# upate-exim4.conf
# sudo /etc/init.d/exim4 restart

あとは適当にテストとかしてみると良い.

(2012/07/30) このままだと message-id が “乱数列@hostname” になるので /etc/exim4/exim4.conf.template あたりに

message_id_header_domain = ドメイン名
message_id_header_text   = ホスト名

を追加して update-exim4.conf を走らせておくと良い。

ローカル配送先を適宜設定したい場合には

MAILDIR_HOME_MAILDIR_LOCATION = $home/Maildir/INBOX

_送信設定: msmtp

Wanderlust からメールを送る時には, msmtp を使っている.

sendmail コマンド互換かつ enverope-from に応じて送信に利用する SMTP サーバを切り替えられる, というのが良い. 本当は Gmail に SMTP サーバを登録しておいて, exim4 の smarthost で Gmail に丸投げしたかったのだけれど, Gmail のメールサーバは Reply-To を上書きしたりして行儀が良くない.

msmtp 自体の設定はいたって普通だが, queue が面倒? 結局 Emacs からは sendmail として

#!/bin/sh
ARGS="$*"
msmtp-enqueue.sh $ARGS 1>/dev/null
if [ x"$(LANG=C nmcli n connectivity)" = x"full" ]; then
    msmtp-runqueue.sh 2>&1 1>/dev/null
fi
exit 0

を叩くことで, ネットワーク接続がある場合には即座に送信, 無い場合には queuing, という風に動作を切り替えている.

あとは NetworkManager の dispatcher として msmtp-runqueue を走らせれば良い.

_受信

受信/閲覧は

としている.

最近の MUA は多かれ少なかれ IMAP 接続でも手元にメールを持ってくるので, このメールの重複をなんとかしたいのだけれども, 例えば Wanderlust なんかで Maildir を直接見にいくと Emacs の反応があまり芳しくない.

以前 第247回 Offlineimap+Dovecotによる快適メール環境:Ubuntu Weekly Recipe なんて記事を書いた。 その時は offlineimap を使っていたのだけれども

ということで、2015 年から isync: free IMAP and MailDir mailbox synchronizer に移行した。

_mbsync/isync

isync/mbsync はリモートの IMAP サーバとローカルの Maildir を同期するソフトウェア。

_インストール

実行バイナリは isync もしくは mbsync 。以前の名前が isync なので、Debian のパッケージ名はそのままである。

$ sudo apt-get install isync

apt 万歳

_設定

設定ファイルは ~/.mbsyncrc. 設定例は以下:

今のところ特に使用に際して困った事はおきていない。

IMAPAccount mobile
Host mobile.example.com
User TheMobileUserAccount
PassCmd "echo ${PASSWORD:-$(gpg --no-tty -qd ~/.authinfo.gpg | sed -n 's,^machine mobile.example.com .*password \\\([^ ]*\\) port.*,\\1,p')}"
Port 993
UseIMAPS yes
RequireSSL yes
UseTLSv1.2 yes
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPAccount work
Host work.example.com
User TheWorkUserAccount
PassCmd "echo ${PASSWORD:-$(gpg --no-tty -qd ~/.authinfo.gpg | sed -n 's,^machine work.example.com .*password \\\([^ ]*\\) port.*,\\1,p')}"
Port 993
UseIMAPS yes
RequireSSL yes
UseTLSv1.2 yes
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPAccount gmail
Host imap.gmail.com
User GmailMailAddress
PassCmd "echo ${PASSWORD:-$(gpg --no-tty -qd ~/.authinfo.gpg | sed -n 's,^machine imap.gmail.com .*password \\\([^ ]*\\) port.*,\\1,p')}"
Port 993
UseIMAPS yes
RequireSSL yes
UseTLSv1.2 yes
CertificateFile /etc/ssl/certs/ca-certificates.crt

IMAPStore work-remote
Account work
UseNamespace no

IMAPStore work-remote
Account work

IMAPStore gmail-remote
Account gmail
UseNamespace yes

MaildirStore local
Path ~/Maildir/
Inbox ~/Maildir/INBOX

MaildirStore work-local
Path ~/Maildir/
Inbox ~/Maildir/INBOX
Flatten .

Channel mobile
Master :mobile-remote:
Slave :local:INBOX.mobile.
Patterns INBOX Junk Archives Sent Drafts Trash
Create Both
Expunge Both
Sync all
SyncState *

Channel gmail
Master :gmail-remote:
Slave :local:INBOX.Gmail.
Patterns INBOX
Create Both
Expunge Both
Sync all
SyncState *

Channel gmail-junk
Master ":gmail-remote:[Gmail]/&j,dg0TDhMPww6w-"
Slave :local:INBOX.Gmail.Junk
Patterns *
Create Both
Expunge Both
Sync all
SyncState *

Channel work
Master :work-remote:
Slave :work-local:
Patterns * !*mobile* !*Gmail*
Create Both
Expunge Both
Sync all
SyncState *

上手く設定できているなら

mbsync -l mobile

などとして、同期する Maildir を確認すると良い。 リモートとローカルでどういうマッピングがされているかわかるだろう。 25万通(6.5G)の同期にだいたい 3 時間ぐらい。 回線速度に依存するだろうけれど、offlineimap より目に見えて速い(気がする)。 また、IMAP Pipeline で処理されているので、サーバ上の IMAP connection の数は 1 つのみだった。

_cron での動作

認証に GPG を使っており、認証に Gnome Keyring を用いているので 必要な環境変数を読み込んでから実行するようにする. 先ず ~/bin/export_x_info.sh を以下の内容で用意:

#!/bin/bash
sleep 5
# Export the dbus session address on startup so it can be used by cron
touch $HOME/.Xdbus
chmod 600 $HOME/.Xdbus
env | grep DBUS_SESSION_BUS_ADDRESS > $HOME/.Xdbus
env | grep GPG_AGENT_INFO >> $HOME/.Xdbus
echo 'export DBUS_SESSION_BUS_ADDRESS' >> $HOME/.Xdbus
echo 'export GPG_AGENT_INFO' >> $HOME/.Xdbus

~/.config/autostart 以下に適当な名前で

[Desktop Entry]
Type=Application
Exec=/home/uwabami/.mua/export_x_info.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=Xdbus infomation exporter
Comment=Xdbus infomation exporter

を用意しておく.

あとは cron 実行時に ~/.Xdbus を include するようにしておけば, 必要に応じて GnomeKeyring による認証が行なわれる. cron で実行されるスクリプトは, 例えば

#!/bin/sh
# -*- mode:sh; coding: utf-8-unix; indent-tabs-mode: nil -*-
################################################################################
# for personal settings
PID=~/Maildir/mbsync.pid
CONF=~/.mbsyncrc
# for gnome-keyring:
. ${HOME}/.Xdbus
# check network is avaliable
nm-online -x  2>/dev/null || exit 0
# check another offlineimap
timelimit -T 300 -t 300 mbsync -c $CONF -a 2>&1 || exit 0

とか。mbsync 自体の timeout や PID 管理ができないので、 timelinit コマンドで処理をするようにしている。 実際は、二回目以降の同期は(回線速度に依存するだろうけれど) 20 秒ぐらい。

_dovecot

_インストール

最低限 IMAP での接続ができれば良いので

% sudo apt-get install dovecot-imapd

apt 万歳

_設定

最低限の設定として、

ための設定を行なう

以下、設定ファイル中のコメント行を除いた部分だけを記載しているので注意 (コメント消すなんてとんでもない!)。

先ず待受サーバ。 どの IP アドレスで待ち受けるかは /etc/dovecot/dovecot.conf にあるので

...
listen = 127.0.0.1
...

あたりを修正しておく。

認証は /etc/dovecot/conf.d/10-auth.conf 内で

disable_plain_text_auth = no
auth_mechanisms = plain
!include auth-system.conf.ext

を有効に。include されている auth-system.conf.ext の中身は

passdb {
  driver = pam
}
userdb {
  driver = passwd
}

だけが有効。これで pam 経由でパスワードログインするようになる.

次に Maildir の設定。 /etc/dovecot/conf.d/10-mail.conf 内で

mail_location = maildir:%h/Maildir:INBOX=%h/Maildir/INBOX:LAYOUT=fs
namespace inbox {
  inbox = yes
}

あたりを修正しておく。

最後に IMAP over SSL の設定。 /etc/dovecot/conf.d/10-ssl.conf で証明書の設定をする:

ssl_cert = </etc/ssl/private/ssl-cert-dovecot.pem
ssl_key = </etc/ssl/private/ssl-cert-dovecot.key
ssl_protocols = !SSLv2 !SSLv3

その後に /etc/dovecot/conf.d/10-master.conf 内の imap-login 部分を修正

service imap-login {
  inet_listener imap {
    port = 0
    ssl = no
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

これで再起動すると /etc/ssl/private に置いた SSL 証明書を使って 127.0.1.1:993 で IMAP サーバが起動する。Listen を 127.0.0.1 に絞るなら SSL でのアクセスは不要かもしれない。

_以下, obsolete

検索は mu (maildir-utils) を使うようになったし, メールの同期には mbsync を使うようになったので, 以下は昔のお話.

_検索: Solr

Dovecot には FTS(Full Text Search) 用の plugin が幾つかある。 ここでは Solr を試してみる

とりあえず jetty で solar を動かすことに:

% sudo aptitude -R solr-jetty dovecot-solr

Jetty の起動設定は /etc/default/jetty8 にあるので

NO_START=0
JETTY_HOST=localhost
JETTY_PORT=8089

としておく。dovecot 用の scheme.xml は dovecot-solr をインストールすると /usr/share/dovecot/solr-scheme.xml にインストールされるので、これを /etc/solr/conf/scheme.xml にコピー

% cd /etc/solr/conf
% sudo cp scheme.xml scheme.xml.bak
% sudo cp /usr/share/dovecot/solr-schemex.ml scheme.xml

その後で /etc/dovecot/conf.d/10-imap.conf 内の mail_plugins 行に

mail_plugins = fts fts_solr

を追加しておく。 IMAP でしか使わないのであれば 20-imap.conf に指定すれば良いのだが、この場合は doveadm fts rescan -u USERNAME ができない。 最後に /etc/dovecot/conf.d/90-plugin.conf

plugin {
  fts_autoindex = yes
  fts = solr
  fts_solr = break-imap-search url=http://localhost:8089/solr/
}

としておく。これで jetty8 および dovecot を再起動すると、検索が効く様になる…?

_検索: Solr で日本語

scheme.xml の修正が必要。 とりあえず

してみた.

diff は以下:

--- solr-schema.xml 2015-05-10 20:52:33.375358006 +0900
+++ schema.xml  2015-05-10 20:37:37.358195292 +0900
@@ -13,6 +13,62 @@
     <fieldType name="long" class="solr.TrieLongField" />
     <fieldType name="boolean" class="solr.BoolField" />

+    <!-- CJK bigram (see text_ja for a Japanese configuration using morphological analysis) -->
+    <fieldType name="text_cjk" class="solr.TextField" positionIncrementGap="100">
+      <analyzer>
+        <tokenizer class="solr.StandardTokenizerFactory"/>
+        <!-- normalize width before bigram, as e.g. half-width dakuten combine  -->
+        <filter class="solr.CJKWidthFilterFactory"/>
+        <!-- for any non-CJK -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+        <filter class="solr.CJKBigramFilterFactory"/>
+      </analyzer>
+    </fieldType>
+    <fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
+      <analyzer>
+      <!-- Kuromoji Japanese morphological analyzer/tokenizer (JapaneseTokenizer)
+
+           Kuromoji has a search mode (default) that does segmentation useful for search.  A heuristic
+           is used to segment compounds into its parts and the compound itself is kept as synonym.
+
+           Valid values for attribute mode are:
+              normal: regular segmentation
+              search: segmentation useful for search with synonyms compounds (default)
+            extended: same as search mode, but unigrams unknown words (experimental)
+
+           For some applications it might be good to use search mode for indexing and normal mode for
+           queries to reduce recall and prevent parts of compounds from being matched and highlighted.
+           Use <analyzer type="index"> and <analyzer type="query"> for this and mode normal in query.
+
+           Kuromoji also has a convenient user dictionary feature that allows overriding the statistical
+           model with your own entries for segmentation, part-of-speech tags and readings without a need
+           to specify weights.  Notice that user dictionaries have not been subject to extensive testing.
+
+           User dictionary attributes are:
+                     userDictionary: user dictionary filename
+             userDictionaryEncoding: user dictionary encoding (default is UTF-8)
+
+           See lang/userdict_ja.txt for a sample user dictionary file.
+
+           See http://wiki.apache.org/solr/JapaneseLanguageSupport for more on Japanese language support.
+        -->
+        <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/>
+        <!--<tokenizer class="solr.JapaneseTokenizerFactory" mode="search" userDictionary="lang/userdict_ja.txt"/>-->
+        <!-- Reduces inflected verbs and adjectives to their base/dictionary forms (辞書形) -->
+        <filter class="solr.JapaneseBaseFormFilterFactory"/>
+        <!-- Removes tokens with certain part-of-speech tags -->
+        <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" enablePositionIncrements="true"/>
+        <!-- Normalizes full-width romaji to half-width and half-width kana to full-width (Unicode NFKC subset) -->
+        <filter class="solr.CJKWidthFilterFactory"/>
+        <!-- Removes common tokens typically not useful for search, but have a negative effect on ranking -->
+        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" enablePositionIncrements="true" />
+        <!-- Normalizes common katakana spelling variations by removing any last long sound character (U+30FC) -->
+        <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
+        <!-- Lower-cases romaji characters -->
+        <filter class="solr.LowerCaseFilterFactory"/>
+      </analyzer>
+    </fieldType>
+
     <fieldType name="text" class="solr.TextField" positionIncrementGap="100">
       <analyzer type="index">
         <tokenizer class="solr.StandardTokenizerFactory"/>
@@ -43,14 +99,14 @@
    <field name="box" type="string" indexed="true" stored="true" required="true" />
    <field name="user" type="string" indexed="true" stored="true" required="true" />

-   <field name="hdr" type="text" indexed="true" stored="false" />
-   <field name="body" type="text" indexed="true" stored="false" />
+   <field name="hdr" type="text_cjk" indexed="true" stored="false" />
+   <field name="body" type="text_cjk" indexed="true" stored="false" />

    <field name="from" type="text" indexed="true" stored="false" />
    <field name="to" type="text" indexed="true" stored="false" />
    <field name="cc" type="text" indexed="true" stored="false" />
    <field name="bcc" type="text" indexed="true" stored="false" />
-   <field name="subject" type="text" indexed="true" stored="false" />
+   <field name="subject" type="text_cjk" indexed="true" stored="false" />

    <!-- Used by Solr internally: -->
    <field name="_version_" type="long" indexed="true" stored="true"/>

_offlineimap

(2015/05/10) 以前使っていた時のメモ。もう使わないけれど、たまに検索でココを見に来ている人がいるみたいなので、残しておくことに。 OfflineIMAP は,ネットワーク上にあるIMAPサーバと手元のMaildir/IMAPサーバの同期を取るためのソフトウェア.

_インストール

apt万歳, ってことで.

$ sudo apt-get install offlineimap
_設定

現状, 以下の通り:

# -*- mode: conf; coding: utf-8-unix; indent-tabs-mode: nil -*-
# offlineimaprc
#
# Copyright(C) 2011 Youhei SASAKI All rights reserved.
# $Lastupdate: 2014-07-26 18:25:07$
#
# Author: Youhei SASAKI <uwabami@gfd-dennou.org>
# License: WTFPL
#
# Code:
[general]
# メタデータの格納先
metadata = ~/Mail/offlineimap
# 補助関数が定義されたファイルの置き場所
pythonfile = ~/.mua/offlineimap_utils.py
# 同期するサーバの設定
accounts = Main, Gmail
# 同期するアカウントの数
maxsyncaccounts = 2
# UI の設定. cron で同期する場合には quiet の方が良い
ui = basic
socktimeout = 600
# 高速化(?)
fsync = false

# Gmail 用の設定: ここで設定する名前は accounts に揃える
[Account Gmail]
localrepository = LocalGmail
remoterepository = RemoteGmail
maxsize = 2000000000
status_backend = sqlite
# quick = 1 だとフラグは同期されない
# '[Gmail]/全てのメール' を同期するなら quick = 1 の方が良い?
quick = 0

[Repository LocalGmail]
# Gmail の手元の設定
type = Maildir
# Maildir の格納場所
localfolders = ~/Mail/imap/[Gmail]
restoreatime = no
# IMAP のセパレータの変換: Mailbox の "." が "/" となり,
# ディレクトリが作成される
sep = /

[Repository RemoteGmail]
# Gmail との接続設定: type = IMAP でも良い?
type = Gmail
# remote{pass,user}eval は offlineimap_utils.py で定義した関数
remoteusereval = get_username("imap.gmail.com")
remotepasseval = get_password("imap.gmail.com")
maxsize = 2000000000
realdelete = no
# 同時接続数. 並列実行可能なので適宜
# サーバに怒られない程度に
maxconnections = 5
# remote -> local 時のフォルダ名変換(正規表現)
nametrans = lambda foldername: re.sub('^INBOX','', (re.sub('^\[Gmail\]/','', foldername)))
# 同期するフォルダの設定: Gmail 側で IMAP で表示するラベル設定しておく, でも良いかも.
folderfilter = lambda foldername: foldername in [
             'INBOX',                   # -> '~/Mail/imap/[Gmail]' になる
             '[Gmail]/&j,dg0TDhMPww6w-' # -> '~/Mail/imap/[Gmail]/&j,dg0TDhMPww6w-' になる
             ]
sslcacertfile = /etc/ssl/certs/ca-certificates.crt

# Main サーバの設定: ここで設定する名前は [general] accounts に揃える
[Account Main]
localrepository = LocalMain
remoterepository = RemoteMain
status_backend = sqlite
maxsize = 2000000000
quick = 0

[Repository LocalMain]
# 手元の設定
type = Maildir
# Maildir の格納場所
localfolders = ~/Mail/imap
restoreatime = no
# local -> remote での名前変換
# LocaltoRemoteTransnameINBOX は offlineimap_utils.py で定義した関数
nametrans = LocalToRemoteTransnameINBOX
# 'Maildir/imap/[Gmail]' は同期しない
folderfilter = lambda folder: not folder.startswith('[Gmail]')
sep = /

[Repository RemoteMain]
type = IMAP
ssl = yes
# certificates がデフォルトで探せない?
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
remotehost = [main server]
# remote{pass,user}eval は offlineimap_utils.py で定義した関数
remoteusereval = get_username("[main server]")
remotepasseval = get_password("[main server]")
maxsize = 2000000000
maxconnections = 5
# remote -> local での名前の変換
# RemotetoLocalTransnameINBOX は offlineimap_utils.py で定義した関数
nametrans = RemoteToLocalTransnameINBOX
# 同期 *しない* フォルダの選択
folderfilter = lambda foldername: foldername not in [
            'INBOX.Drafts',
            'INBOX.Trash',
            'INBOX.archive.zz2006',
            'INBOX.archive.zz2007',
            'INBOX.archive.zz2008',
            'INBOX.archive.zz2009',
            'INBOX.archive.zz2010',
            'INBOX.archive.zz2011',
            'INBOX.archive.zz2012',
            'INBOX.archive.zz2013'
            ]

offlineimap_utils.py の中身は以下の通り:

#!/usr/bin/python
# -*- coding: utf-8
import sys
import re
import os
# # Auth via GnomeKeyring
import gtk
from getpass import getpass
import gnomekeyring as gkey
# Ad hoc fix for Striptime behavior
import locale
locale.setlocale(locale.LC_TIME, 'C')

##################
# Gnome keyring  #
##################

class Keyring(object):
    def __init__(self, name, server, protocol):
        self._name = name
        self._server = server
        self._protocol = protocol
        self._keyring = gkey.get_default_keyring_sync()

    def has_credentials(self):
        try:
            attrs = {"server": self._server, "protocol": self._protocol}
            items = gkey.find_items_sync(gkey.ITEM_NETWORK_PASSWORD, attrs)
            return len(items) > 0
        except gkey.DeniedError:
            return False

    def get_credentials(self):
        attrs = {"server": self._server, "protocol": self._protocol}
        items = gkey.find_items_sync(gkey.ITEM_NETWORK_PASSWORD, attrs)
        return (items[0].attributes["user"], items[0].secret)

    def set_credentials(self, (user, pw)):
        attrs = {
                "user": user,
                "server": self._server,
                "protocol": self._protocol,
            }
        gkey.item_create_sync(gkey.get_default_keyring_sync(),
                gkey.ITEM_NETWORK_PASSWORD, self._name, attrs, pw, True)

def get_username(server):
    keyring = Keyring("offlineimap", server, "imap")
    (username, password) = keyring.get_credentials()
    return username

def get_password(server):
    keyring = Keyring("offlineimap", server, "imap")
    (username, password) = keyring.get_credentials()
    return password

if __name__ == '__main__':
        server = raw_input("server: ")
        user = raw_input("username: ")
        password = getpass("password: ")
        confirm = getpass("confirm password: ")
        if password != confirm:
            print "password doesn't match confirmation!"
            sys.exit(1)
        keyring = Keyring("offlineimap", server, "imap")
        keyring.set_credentials((user, password))

############################
# NameTrans 'INBOX' suffix #
############################
# add 'INBOX' suffix
def LocalToRemoteTransnameINBOX(foldername):
    if (foldername == ""):
        retval = "INBOX"
    else:
        retval = "INBOX." + foldername
    return retval

# remove 'INBOX' suffix
def RemoteToLocalTransnameINBOX(foldername):
    if (foldername == "INBOX"):
        retval = ""
    else:
        retval = re.sub("^INBOX\.", "", foldername)
    return retval

IMAP の名前空間が複雑な(?)場合には, 適宜正規表現を追加すると良い.

_cron での動作

認証に GnomeKeyring を使っているので DBUS_SESSION_BUS_ADDRESS を設定してから 実行するようにする. 先ず ~/bin/export_x_info.sh を以下の内容で用意

#!/bin/bash
sleep 5
# Export the dbus session address on startup so it can be used by cron
touch $HOME/.Xdbus
chmod 600 $HOME/.Xdbus
env | grep DBUS_SESSION_BUS_ADDRESS > $HOME/.Xdbus
echo 'export DBUS_SESSION_BUS_ADDRESS' >> $HOME/.Xdbus

~/.config/autostart 以下に適当な名前で

[Desktop Entry]
Type=Application
Exec=/home/uwabami/.mua/export_x_info.sh
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=Xdbus infomation exporter
Comment=Xdbus infomation exporter

を用意しておく.

あとは cron 実行時に ~/.Xdbus を include するようにしておけば, 必要に応じて GnomeKeyring による認証が行なわれる. cron で実行されるスクリプトは, 例えば

#!/bin/sh
# -*- mode:sh; coding: utf-8-unix; indent-tabs-mode: nil -*-
# $Lastupdate: 2014-07-15 11:52:22$
# Author: Youhei SASAKI <uwabami@gfd-dennou.org>
# License: WTFPL
################################################################################
# for personal settings
OFFLINEIMAP_PID=~/Mail/offlineimap/pid
OFFLINEIMAP_CONF=~/.offlineimaprc
UI=quiet
echo "******************************************************************"
echo `LANG=C date`
echo "******************************************************************"
# for gnome-keyring:
. ${HOME}/.Xdbus
# check network is avaliable
nm-online -x || exit 0
# check another offlineimap
[ $(ps -p `cat $OFFLINEIMAP_PID` -o pid --no-heading) ] \
  || nice -n 19 offlineimap -c $OFFLINEIMAP_CONF -u $UI -o  2>&1

とか.