プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Roundcube の 1.5.x -> 1.6.x へのアップデートおよび ident_switch から identy_switch へ移行

こちらの記事の最後で触れたように、 Roundcube のアップデートを 1.5.x -> 1.6.x でやろうとするとなかなか面倒なことがわかりました。

で、時間ができたのを機に、 1.5.x から 1.6.x へアップデートし、 ident_switch も identy_switch へ移行してみたので、その際の作業をメモにしておきます。

準備

まずは、準備作業です。

sqlite のデータを吐き出し

始めに、 Roundcube のコンテナに接続して、 ident_switch のテーブルをダンプしておきます。

[ユーザ名@NAS名 ~]$ docker exec -it myrc /bin/bash
root@67dab0eafe94:/var/www/html# 
root@67dab0eafe94:/var/www/html# cd /var/roundcube/db/
root@67dab0eafe94:/var/roundcube/db# ls
sqlite.db
root@67dab0eafe94:/var/roundcube/db# 
root@67dab0eafe94:/var/roundcube/db# sqlite3 sqlite.db 
SQLite version 3.34.1 2021-01-20 14:10:07
Enter ".help" for usage hints.
sqlite> .output ident_switch.dump.sql
sqlite> .dump ident_switch
sqlite> .quit
root@67dab0eafe94:/var/roundcube/db# ls
ident_switch.dump.sql  sqlite.db
root@67dab0eafe94:/var/roundcube/db# 

ident_switch.dump.sql の中身を見てみると

root@67dab0eafe94:/var/roundcube/db# cat ident_switch.dump.sql 
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE ident_switch
(
        id
                integer
                PRIMARY KEY,
        user_id
                integer
                NOT NULL
                REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
        iid
                integer
                NOT NULL
                REFERENCES identities(identity_id) ON DELETE CASCADE ON UPDATE CASCADE
                UNIQUE,
        username
                varchar(64),
        password
                varchar(64),
        imap_host
                varchar(64),
        imap_port
                integer
                CHECK(imap_port > 0 AND imap_port <= 65535),
        imap_delimiter
                char(1),
        label
                varchar(32),
        flags
                integer
                NOT NULL
                DEFAULT(0),
        smtp_host
                varchar(64),
        smtp_port
                integer
                CHECK(smtp_port > 0 AND smtp_port <= 65535),
        smtp_auth
                smallint
                NOT NULL
                DEFAULT 1,
        drafts_mbox
                varchar(64),
        sent_mbox
                varchar(64),
        junk_mbox
                varchar(64),
        trash_mbox
                varchar(64),
        UNIQUE (user_id, label)
);
INSERT INTO ident_switch VALUES(1,1,2,'mail_address@example.com','encrypted_password','ssl://サーバー名',993,'/',NULL,1,'ssl://SMTPサーバー名',465,1,'Drafts','Sent','Junk E-mail','Trash');
INSERT INTO ident_switch VALUES(2,1,3,'mail_address2@example.com','encrypted_password','ssl://サーバー名',993,NULL,NULL,1,'ssl://SMTPサーバー名',465,1,'INBOX.Draft','INBOX.Sent','INBOX.spam','INBOX.Trash');
(略)
COMMIT;
root@67dab0eafe94:/var/roundcube/db# 

とこんな感じで、確かにスキーマとデータがダンプされていることがわかります。

docker イメージの作成

dockerfile を 1.6.7 ベースにしておきます。あと、 sqlite を操作するので sqlite3 をインストールしておきます。

FROM roundcube/roundcubemail:1.6.7-apache

RUN apt-get update -y \
    && apt-get install -y git \
    && apt-get install -y sqlite3

RUN set -ex; \
    sed -i "s/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/" /etc/ssl/openssl.cnf \
    ;

COPY ./config.docker.add_imap_conn_options.inc.php /var/roundcube/config/

これで、イメージをビルドしておきます。

[ユーザ名@NAS名 myroundcube]$ docker build -t myroundcube -t myroundcube:0.3 .
(略)

Roundcube 1.6.7 で起動

QNAP の管理画面にログインして、 Container Station を立ち上げます。稼働中の Roundcube アプリケーションを選択して、歯車メニューより『再作成』を選択します。

docker-compose.yml を下記のように指定して、再作成します。

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1.23.1
    ports:
      - 'xxxx:80'
      - 'yyyy:443'
    restart: always
    volumes:
      - rcmail_ssl_certs:/var/lib/https-portal
    environment:
      DOMAINS: 'xxxx.yyyy.zzzz -> http://roundcubemail:80'
      STAGE: 'production'
      # FORCE_RENEW: 'true'
      CLIENT_MAX_BODY_SIZE: 0
  
  roundcubemail:
    image: myroundcube:0.3
    container_name: myrc
#    restart: unless-stopped
    volumes:
      - rcmail_www:/var/www/html
      - rcmail_sqlite:/var/roundcube/db
#    ports:
#      - 9001:80
    environment:
      - ROUNDCUBEMAIL_DB_TYPE=sqlite
      - ROUNDCUBEMAIL_SKIN=elastic
      - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://サーバー名
      - ROUNDCUBEMAIL_DEFAULT_PORT=993
      - ROUNDCUBEMAIL_SMTP_SERVER=ssl://サーバー名
      - ROUNDCUBEMAIL_SMTP_PORT=465
      - ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=30M
      - ROUNDCUBEMAIL_PLUGINS=archive,zipdownload,attachment_reminder

volumes:
    rcmail_ssl_certs:
        external: true
    rcmail_www:
        external: true
    rcmail_sqlite:
        external: true

ボリューム rcmail_www および rcmail_sqlite の中身は、 1.5.x の状態のままです。これで起動すると、前回の記事の通り ident_switch に関する警告が出ますが、一応起動します(警告の詳細は下記も参照)。

これで、準備OK です。ここから移行作業を行ってみます。

警告の確認

何かの参考になるかもしれないので、 1.6.7 で起動した際の警告を改めてを確認しておきます。

(略)
Running update script at target...
PHP Warning:  Undefined array key "drafts_mbox_default_iswitch" in /var/www/html/plugins/ident_switch/ident_switch.php on line 47
PHP Warning:  Undefined array key "sent_mbox_default_iswitch" in /var/www/html/plugins/ident_switch/ident_switch.php on line 47
PHP Warning:  Undefined array key "junk_mbox_default_iswitch" in /var/www/html/plugins/ident_switch/ident_switch.php on line 47
PHP Warning:  Undefined array key "trash_mbox_default_iswitch" in /var/www/html/plugins/ident_switch/ident_switch.php on line 47
WARNING: Replaced config options:
(These config options have been replaced or renamed)
- 'default_host' was replaced by 'imap_host'
- 'smtp_server' was replaced by 'smtp_host'
- backing up the current config file(s)...
- writing /var/www/html/config/config.inc.php...
Done.
Your configuration files are now up-to-date!
Executing database schema update.
Updating database schema (2021081000)... [OK]
(略)

ident_switch から identy_switch への移行

Roundcube が 1.6.7 で起動できたので、 ident_switch から identy_switch へ移行したいと思います。

packagist.org

一応、ここに ident_switch からの移行方法も書いてありますが、後述するようにこれでは失敗したため、そのあたりも対応していきます。

identy_switch のインストール

roundcube のコンテナに入った状態でインストールを行います。

root@52cfd961e6e2:/var/www/html# composer require toteph42/identy_switch
Composer could not detect the root package (roundcube/roundcubemail) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
./composer.json has been updated
Composer could not detect the root package (roundcube/roundcubemail) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
Running composer update toteph42/identy_switch
(略)
  - Installing toteph42/identy_switch (1.0.17): Extracting archive
 15/20 [=====================>------]  75%Do you want to activate the plugin identy_switch? [Y|n] y
Updated local config at /var/www/html/config/config.inc.php
Creating package config file
Running database initialization script for identy_switch
Creating database schema... [OK]
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
7 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found.
Using version ^1.0 for toteph42/identy_switch
root@52cfd961e6e2:/var/www/html# 

途中でこのプラグインを有効にするか聞かれるので、yを押しておきます。無事にインストールされたら、

/var/www/html/plugins/identy_switch/SQL に migrate.sql があることを確認します。

ありますね。これが移行スクリプトになるとのことです。

sqlite のテーブルを書き戻す

この時点では、 ident_switch のテーブルが存在していますが、データがすべて消えた状態になっています。なので、移行スクリプトを呼び出す前に、 ident_switch のテーブルを復元します。

まずは現時点の状態を確認します。

root@52cfd961e6e2:/var/www/html# cd /var/roundcube/db/
root@52cfd961e6e2:/var/roundcube/db# ls
ident_switch.dump.sql  sqlite.db
root@52cfd961e6e2:/var/roundcube/db# sqlite3 sqlite.db 
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> 
sqlite> .table
cache                contactgroups        responses          
cache_index          contacts             searches           
cache_messages       dictionary           session            
cache_shared         filestore            system             
cache_thread         ident_switch         users              
collected_addresses  identities         
contactgroupmembers  identy_switch      
sqlite> 
sqlite> select * from ident_switch;
sqlite> 

ident_switch のテーブルが存在しますが、中身は空ですね。一旦、このテーブルを削除しておきます。

sqlite> drop table ident_switch;
sqlite> .table
cache                contactgroupmembers  identy_switch      
cache_index          contactgroups        responses          
cache_messages       contacts             searches           
cache_shared         dictionary           session            
cache_thread         filestore            system             
collected_addresses  identities           users              
sqlite> 

次に、最初に取得したテーブルのダンプファイルを読み込み、テーブルおよびレコードを復元します。

sqlite> .read ident_switch.dump.sql
sqlite> .table
cache                contactgroups        responses          
cache_index          contacts             searches           
cache_messages       dictionary           session            
cache_shared         filestore            system             
cache_thread         ident_switch         users              
collected_addresses  identities         
contactgroupmembers  identy_switch      
sqlite> 

レコードも確認すると、ちゃんと戻っています。

sqlite> select * from ident_switch;
1|1|2|mail_address@example.com|encrypted_password|ssl://サーバー名|993|/||1|ssl://SMTPサーバー名|465|1|Drafts|Sent|Junk E-mail|Trash
2|1|3|mail_address2@example.com|encrypted_password|ssl://サーバー名|993|||1|ssl://SMTPサーバー名|465|1|INBOX.Draft|INBOX.Sent|INBOX.spam|INBOX.Trash
(略)
sqlite> 

移行スクリプトの実行

それでは、移行スクリプトを実行します。

sqlite> .read /var/www/html/plugins/identy_switch/SQL/migrate.sql
Parse error near line 3: no such column: notify_timeout
  host`,     `imap_port`,     `imap_delimiter`,     `notify_timeout`,     `newma
                                      error here ---^
sqlite> 

あれれ?エラーになります。ついでにいうと、この時点で ident_switch テーブルは削除されてしまってます。

migrate.sql の中を見てみると、これ自体はそんなに難しいことをしておらず、 ident_switch の各項目を identy_switch に insert しているだけです。

ここで、2つのテーブルのスキーマを見比べてみると、(少なくとも現時点の) ident_switch には

  • notify_timeout
  • newmail_check

の項目がないのにかかわらず、 migrate.sql の select のフィールドとして含まれてしまってます。これがエラーの原因ですね。

ということで、移行スクリプトを修正してやります。まず、

[ユーザ名@NAS名 docker]$ docker cp myrc:/var/www/html/plugins/identy_switch/SQL/migrate.sql .

こんな感じで、コンテナからホスト持ってきて、編集します。

[ユーザ名@NAS名 docker]$ cat migrate.sql
--  Created with phpmyadmin

INSERT INTO identy_switch(
    `id`,
    `user_id`,
    `iid`,
    `label`,
    `flags`,
    `imap_user`,
    `imap_pwd`,
    `imap_host`,
    `imap_port`,
    `imap_delim`,
    `smtp_host`,
    `smtp_port`,
    `drafts`,
    `sent`,
    `junk`,
    `trash`
)
SELECT
    `id`,
    `user_id`,
    `iid`,
    `label`,
    `flags`,
    `username`,
    `password`,
    `imap_host`,
    `imap_port`,
    `imap_delimiter`,
    `smtp_host`,
    `smtp_port`,
    `drafts_mbox`,
    `sent_mbox`,
    `junk_mbox`,
    `trash_mbox`
FROM
    ident_switch;
DROP TABLE IF EXISTS ident_switch;[ユーザ名@NAS名 docker]$ 
[ユーザ名@NAS名 docker]$ 

INSERT の対象フィールドからさきほどの2つのフィールドを削除しただけです。 identy_switch のスキーマを確認すると、

sqlite> .schema identy_switch
CREATE TABLE IF NOT EXISTS "identy_switch"(
        "id" INTEGER  NOT NULL ,
        "user_id" INTEGER  NOT NULL REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
        "iid" INTEGER  NOT NULL REFERENCES identities(identity_id) ON DELETE CASCADE ON UPDATE CASCADE UNIQUE,
        "label" TEXT,
        "flags" INT NOT NULL DEFAULT 0,
        "imap_user" TEXT,
        "imap_pwd" TEXT,
        "imap_host" TEXT,
        "imap_port" SMALLINT DEFAULT 0,
        "imap_delim" CHAR(1),
        "newmail_check" SMALLINT DEFAULT 300,
        "notify_timeout" SMALLINT DEFAULT 10,
        "smtp_host" TEXT,
        "smtp_port" SMALLINT DEFAULT 0,
        "drafts" TEXT DEFAULT '',
        "sent" TEXT DEFAULT '',
        "junk" TEXT DEFAULT '',
        "trash" TEXT DEFAULT '',
        UNIQUE (user_id, label)
);
CREATE INDEX IX_identy_switch_user_id ON identy_switch(user_id);
CREATE INDEX IX_identy_switch_iid on identy_switch(iid);
sqlite> 

のようになっていて、 newmail_check も notify_timeout もデフォルト値が設定されているので大きな問題にはならないと予想されます。

この修正した migrate.sql を roundcube のコンテナ内にコピーしてやります。

[ユーザ名@NAS名 docker]$ docker cp migrate.sql myrc:/var/www/html/plugins/identy_switch/SQL/

コピーできたか確認しておきます。

[ユーザ名@NAS名 docker]$ docker exec -it myrc /bin/bash
root@52cfd961e6e2:/var/www/html# cd plugins/identy_switch/SQL
root@52cfd961e6e2:/var/www/html/plugins/identy_switch/SQL# ls
migrate.sql  migrate.sql.org  mysql.initial.sql  postgres.initial.sql  sqlite.initial.sql
root@52cfd961e6e2:/var/www/html/plugins/identy_switch/SQL# 

ちゃんとありますね。さきほどエラーが起きたときに、既存の ident_switch テーブルは削除されてしまったので、作業前に、もう一度戻します。

root@52cfd961e6e2:/var/www/html/plugins/identy_switch/SQL# cd /var/roundcube/db/
root@52cfd961e6e2:/var/roundcube/db# sqlite3 sqlite.db 
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> 
sqlite> .read ident_switch.dump.sql

やっと、修正した移行スクリプトの実行です。

sqlite> .read /var/www/html/plugins/identy_switch/SQL/migrate.sql
sqlite> 

今度はエラーが起きません。テーブルの中身を確認してみると、

sqlite> select * from identy_switch;
1|1|2||1|mail_address@example.com|encrypted_password|ssl://サーバー名|993|/|300|10|ssl://SMTPサーバー名|465|Drafts|Sent|Junk E-mail|Trash
2|1|3||1|mail_address2@example.com|encrypted_password|ssl://サーバー名|993||300|10|ssl://SMTPサーバー名|465|INBOX.Draft|INBOX.Sent|INBOX.spam|INBOX.Trash
sqlite>

おぉ、ちゃんとコピーされてますね。

動作確認

ここまでできたら、一度 roundcube にアクセスしてみて、動作確認をしてみます。Roundcube 自体は問題ないですね。メールアドレスの選択部分を見ると、 ident_switch とは少し異なりますが、

のようになっているので動作しているっぽいです。メールアカウントを切り替えてみようとすると、

あれ?変ですね。メールアドレスの選択肢がありません。

メールアドレス表示の不具合へ対応

試しにこの空欄を選択してみると、あれ?アカウントが切り替わりました。ただし、切り替え用のアカウントは7~8個設定していますが、そのうちの一つだけに切り替えできました。ということは identy_switch 自体も動作はしてるっぽいですが、表示周りの問題のようですね。

仕方ないので、『設定』->『識別情報』と進んで、デフォルト以外のアカウントを選択して内容を確認すると、設定はコピーされているようです。どういうことだろうか・・・。

ん?設定内容をよく見てみると、

こんな感じに Label の中身は何も設定されていないのが引っかかりました。 ident_switch の時は未設定でもメールアドレスが替わりに表示されていたので、特に設定を行っていませんでした。なので、すべて空白になっています。

ひょっとしてこれじゃないかな?と思い、試に文字列を設定してみると、今度はメールアドレスの選択がちゃんと表示されました。

切り替えも正しくできます。どうもこれが原因だったようです。少なくとも Label を設定してやれば問題なく表示できますね。

フォルダ階層区切り文字を設定

じゃあ、ということでこれを設定済みのメールアドレスについて全部設定してやろうとしたら、一部

というエラーが出るところがありました。 IMAP のフォルダ階層区切り文字を設定しろ、ということのようです。

ident_switch の時は、こちらの記事に書いたように、『.』(ピリオド)以外の区切り文字については設定する必要があったのですが、『.』(ピリオド)は未設定でも動作していました。ですが、 identy_switch は必須項目になっているようです(先ほどみたスキーマには not null 制約はついていなかったので、保存時にチェックしているんでしょうね)。

ということで、 Label と併せて、フォルダ階層区切り文字も設定しておきます。

後始末

この状態だと、 ident_switch と identy_switch の両方が有効になっているので、 ident_switch を無効にします。composer でインストールしたので、 composer でのアンインストール方法を調べると、

などが見つかったので、まんま、これをやってみます。

root@b428ce2626f6:/var/www/html# composer remove boressoft/ident_switch --no-update
root@b428ce2626f6:/var/www/html# composer update --dry-run
Composer could not detect the root package (roundcube/roundcubemail) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 0 updates, 1 removal
  - Removing boressoft/ident_switch (4.4.2)
Installing dependencies from lock file (including require-dev)
Package operations: 19 installs, 0 updates, 1 removal
  - Removing boressoft/ident_switch (4.4.2)
  - Installing symfony/polyfill-ctype (v1.30.0)
  - Installing phpdocumentor/reflection-docblock (2.0.5)
  - Installing phpunit/php-token-stream (1.4.12)
  - Installing symfony/yaml (v3.4.47)
  - Installing sebastian/version (1.0.6)
  - Installing sebastian/global-state (1.1.1)
  - Installing sebastian/recursion-context (1.0.5)
  - Installing sebastian/exporter (1.2.2)
  - Installing sebastian/environment (1.3.7)
  - Installing sebastian/diff (1.4.1)
  - Installing sebastian/comparator (1.2.4)
  - Installing phpunit/php-text-template (1.2.1)
  - Installing doctrine/instantiator (1.5.0)
  - Installing phpunit/phpunit-mock-objects (2.3.8)
  - Installing phpunit/php-timer (1.0.8)
  - Installing phpunit/php-file-iterator (1.4.5)
  - Installing phpunit/php-code-coverage (2.2.4)
  - Installing phpspec/prophecy (v1.5.0)
  - Installing phpunit/phpunit (4.8.36)
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.
4 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found.
root@b428ce2626f6:/var/www/html# 

実際に削除します。

root@b428ce2626f6:/var/www/html# composer update boressoft/ident_switch
Composer could not detect the root package (roundcube/roundcubemail) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 0 updates, 1 removal
  - Removing boressoft/ident_switch (4.4.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 19 installs, 0 updates, 1 removal
  - Downloading symfony/polyfill-ctype (v1.30.0)
  - Downloading phpdocumentor/reflection-docblock (2.0.5)
  - Downloading phpunit/php-token-stream (1.4.12)
  - Downloading symfony/yaml (v3.4.47)
  - Downloading sebastian/version (1.0.6)
  - Downloading sebastian/global-state (1.1.1)
  - Downloading sebastian/recursion-context (1.0.5)
  - Downloading sebastian/exporter (1.2.2)
  - Downloading sebastian/environment (1.3.7)
  - Downloading sebastian/diff (1.4.1)
  - Downloading sebastian/comparator (1.2.4)
  - Downloading phpunit/php-text-template (1.2.1)
  - Downloading doctrine/instantiator (1.5.0)
  - Downloading phpunit/phpunit-mock-objects (2.3.8)
  - Downloading phpunit/php-timer (1.0.8)
  - Downloading phpunit/php-file-iterator (1.4.5)
  - Downloading phpunit/php-code-coverage (2.2.4)
  - Downloading phpspec/prophecy (v1.5.0)
  - Downloading phpunit/phpunit (4.8.36)
  - Removing boressoft/ident_switch (4.4.2)
  - Installing symfony/polyfill-ctype (v1.30.0): Extracting archive
  - Installing phpdocumentor/reflection-docblock (2.0.5): Extracting archive
  - Installing phpunit/php-token-stream (1.4.12): Extracting archive
  - Installing symfony/yaml (v3.4.47): Extracting archive
  - Installing sebastian/version (1.0.6): Extracting archive
  - Installing sebastian/global-state (1.1.1): Extracting archive
  - Installing sebastian/recursion-context (1.0.5): Extracting archive
  - Installing sebastian/exporter (1.2.2): Extracting archive
  - Installing sebastian/environment (1.3.7): Extracting archive
  - Installing sebastian/diff (1.4.1): Extracting archive
  - Installing sebastian/comparator (1.2.4): Extracting archive
  - Installing phpunit/php-text-template (1.2.1): Extracting archive
  - Installing doctrine/instantiator (1.5.0): Extracting archive
  - Installing phpunit/phpunit-mock-objects (2.3.8): Extracting archive
  - Installing phpunit/php-timer (1.0.8): Extracting archive
  - Installing phpunit/php-file-iterator (1.4.5): Extracting archive
  - Installing phpunit/php-code-coverage (2.2.4): Extracting archive
  - Installing phpspec/prophecy (v1.5.0): Extracting archive
  - Installing phpunit/phpunit (4.8.36): Extracting archive
Updated local config at /var/www/html/config/config.inc.php
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Package phpunit/phpunit-mock-objects is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
7 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found.
root@b428ce2626f6:/var/www/html# 

/var/www/html/config/config.inc.php に active なプラグインが記述されているのですが、上記操作のあとは

// ----------------------------------
// PLUGINS
// ----------------------------------
// List of active plugins (in plugins/ directory)
$config['plugins'] = [
        'archive',
        'zipdownload',
        'attachment_reminder',
        'identy_switch',
];

のように、 ident_switch の記述が消えてました。

また、 Roundcube にログインして、『このプログラムについて』を表示させても

のように、リストから ident_switch が消えていました。無事アンインストールできたようです。

なお、 ident_switch と identy_switch が両方有効な状態のままの時、一部の特殊なフォルダの表示がおかしくなっていましたが、上記の通りアンインストールしたら、問題なく表示できるようになりました。

まとめ

なんとか 1.5.x から 1.6.x にアップデートして、 ident_switch も identy_switch に移行することができました。

identy_switch の設定内容が、若干変わっているようなので、そのあたりは追って調査と設定を行いたいと思います。何はともあれ、これで一段落です。

2024/7/19 追記

さて、上記で移行作業も終わりと思っていたところ、 追加したメールアドレスから送信しようとしたところ、

のようなエラーが表示され、送信できないという状態になっていることに気づきました(普段は受信ばっかりのメールアドレスのため、気が付いていませんでした)。

設定内容をよく見ると、 SMTP 設定の Authorization のデフォルト値が None となっています。

これを、『As IMAP』に変更することで、問題なく送信できるようになりました。

データを移行したはずなのに、送信ができなかったのはなんでなんだろうか?

ちょっと調べた感じの推測なのですが、Roundcube を試用したときの記事にたまたま ident_switch の設定画面の画像があったので、それをみると、 ident_switch のデフォルト値は『As IMAP』になっているようです。

一方、 identy_switch では、デフォルト値が『None』になっています。

ということで、設定値が同じ値(デフォルト値)であっても、両プラグインでの解釈が異なっているため、このようなことが起きているのではないかな?と推測されます。設定変更すると、 identy_switch テーブルの flags フィールドが変化するのはわかったのですが、これをどう解釈しているかは、コードまで追いかけてないので確実なこと言えないですが、無きにしも非ずかな?

ま、うまく送信できない場合は、参考にしてみてください。

Roundcube のアップデート(1.5.3 -> 1.5.6)と attachment_reminder を有効化

こちらの記事で書いたように、 Roundcube のアップデートをおこなおうと思っています。とりあえず今回は、既存のバージョンである 1.5.3 から現時点の docker image の最新版である 1.5.6 へアップデートします(Roundcube 自体は 1.5.7 が出てるんですが docker image がまだでした)。

この作業を行った際の手順をメモにしておきます。

docker イメージの作成

以前、 Roundcube を立ち上げたときの記事に書いたように、今は Roundcube のコンテナイメージを元に一部設定を追加した docker イメージを作成して使っています。なので、まずはこれを再作成します。

dockerfile を編集します。

FROM roundcube/roundcubemail:1.5.6-apache

RUN apt-get update -y \
    && apt-get install -y git \
    && apt-get install -y sqlite3

RUN set -ex; \
    sed -i "s/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/" /etc/ssl/openssl.cnf \
    ;

COPY ./config.docker.add_imap_conn_options.inc.php /var/roundcube/config/

ベースイメージを Roundcube の 1.5.6 にしています。あと、 git と sqlite3 をインストールしているのは、最後に触れているように 1.6.x 系へのアップデートのテストも兼ねていたためです。1.5系で使うだけなら不要です。

今度はタグを2つつけてビルドします。

docker build — Docker-docs-ja 24.0 ドキュメント

[ユーザ名@NAS名 myroundcube]$ docker build -t myroundcube -t myroundcube:0.2 .
Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM roundcube/roundcubemail:1.5.6-apache
(略)
Successfully tagged myroundcube:latest
Successfully tagged myroundcube:0.2
[ユーザ名@NAS名 myroundcube]$ 

無事できました。

新しいコンテナで起動

docker-compose.yml を次のようにします。

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1.23.1
    ports:
      - 'xxxx:80'
      - 'xxxx:443'
    restart: always
    volumes:
      - rcmail_ssl_certs:/var/lib/https-portal
    environment:
      DOMAINS: 'xxxx.yyyy.zzzz -> http://roundcubemail:80'
      STAGE: 'production'
      # FORCE_RENEW: 'true'
      CLIENT_MAX_BODY_SIZE: 0
  
  roundcubemail:
    image: myroundcube:0.2
    container_name: myrc
#    restart: unless-stopped
    volumes:
      - rcmail_www:/var/www/html
      - rcmail_sqlite:/var/roundcube/db
#    ports:
#      - 9001:80
    environment:
      - ROUNDCUBEMAIL_DB_TYPE=sqlite
      - ROUNDCUBEMAIL_SKIN=elastic
      - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://サーバー名
      - ROUNDCUBEMAIL_DEFAULT_PORT=993
      - ROUNDCUBEMAIL_SMTP_SERVER=ssl://サーバー名
      - ROUNDCUBEMAIL_SMTP_PORT=465
      - ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=30M
      - ROUNDCUBEMAIL_PLUGINS=archive,zipdownload,attachment_reminder

volumes:
    rcmail_ssl_certs:
        external: true
    rcmail_www:
        external: true
    rcmail_sqlite:
        external: true

Roundcube のイメージとしてさきほど作成したものを指定します。あと、添付ファイルのつけ忘れプラグイン attachment_reminder は最初から Roundcube に埋め込まれているので、上記で有効なプラグインとして記述するだけで使えるようになるみたいです。あとは、今のアプリケーション(コンテナ)で使っているのと同じボリュームを、上記の docker-compose でも再指定しておくこで、データをそのまま引きつぐようにします

なお、本来は、アップデート作業の前には必ずバックアップを取っておいた方が良いと思います。今回は Roundcube のストレージをバインドマウントからボリュームに変更する作業と同じタイミングで行ったので、バックアップは取得済みでしたので省略しています。

この docker-compose.yml を QNAP の Container Station から既存のアプリケーションの再作成で指定してやります。

これでアップデート完了です。

確認

Roundcubeにログインします。左側のメニューから、『このプログラムについて』を表示すると

のようにバージョンが上がっていることがわかります。また、プラグインの一覧に attachment_reminder が入っていることもわかります。

attachment_reminder を使ってみる

せっかく有効にしたので、早速 attachment_reminder を使ってみます。

ログイン後の画面の右側のメニューより、『設定』->『設定』->『メッセージの作成』を選択し、『基本的な設定』のなかにある『添付ファイルのつけ忘れを確認』を有効にします。

これで、保存を押せば設定完了です。

早速、試します。適当なテストメールを作って、本文中に『添付ファイル』と入れておきます。もちろん、添付ファイルはつけていません。この状態で送信ボタンを押すと

おぉ、警告してくれましたね。もちろん、添付ファイルがあれば、警告なしでそのまま送信されます。

なお、下記の記事によれば、添付以外にもいくつかのキーワードで警告してくれるようです。

Roundcubeのプラグイン〜添付ファイル〜 | OSSのデージーネット

これはずいぶんと役立ちそうです。

まとめ

最初は 1.5.3 から一足飛びにいまの最新版の 1.6.7 にアップデートしようと思って作業したのですが、

  • 1.6.x 系にアップデートすると ident_switch 関連で Roundube コンテナのコンソールに warning が表示される( 1.6.3 と 1.6.7 で試した)
  • そのうえ、データベース内の ident_switch テーブルの中身がすべて消えてしまう
  • ident_switch の後続になりそうな identy_switch は Roundcube 1.6 以降に対応(PHP 8 以降が必要)

ということがあり、なかなか一筋縄ではアップデートできなさそうな印象です。なので、これについてはまた改めて作業したいと思います。

とりあえずは 1.5系の最新版にしたので、当面はこれでいいかなと思ってます。

Roundcube を動かしている docker コンテナのストレージ設定を変更

docker のコンテナで動かしている Roundcube ですが、先日メールを送る際に、添付ファイルをつけ忘れるというあるあるをやってしまいました。メールのありがちなミスですね。

で、調べてみると Roundcube のプラグインで添付ファイル忘れを警告してくれるもの( attachment_reminder )があるので、それを導入することにしました。

Roundcubeのプラグイン〜添付ファイル〜 | OSSのデージーネット

せっかくなので、ついでに、 Roundcube のバージョンも上げたいと思います。ということで、その作業に先立ち、まずは Roundcube のコンテナの永続データの持ち方を見直してみようと思いましたので、その際の作業をメモっておきます。

なんで永続データの持ち方を変えるのか

いま動いている Roundcube のコンテナの設定内容を確認してみます。こちらの記事で紹介したように docker-compose.yml は

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1
    volumes:
      - ./data/ssl_certs:/var/lib/https-portal
    (略)
  
  roundcubemail:
    image: myroundcube:latest
    container_name: myrc
    volumes:
      - ./www:/var/www/html
      - ./db/sqlite:/var/roundcube/db
    (略)

となっており、2つのコンテナで3つのボリューム(厳密にはバインドマウント)を指定しています。

これらは、 QNAP 上のファイルシステムの特定のフォルダを、docker 側に割り当てる方法になっています。実際のデータは、 QNAP 側の Container Station 用のデータ領域下にある application/(アプリケーション名)/ 以下に、 www や data といったフォルダが作られ,、その中に保存されています。

普段だと、コンテナが使っているデータを直接見ることができて、それなりに便利だし、なによりわかりやすいと思います。が、気をつけないといけないのが、

  • QNAP でアプリケーションを削除すると、このフォルダ(アプリケーション名のフォルダ)は消されてしまう

という点です(QNAP でというのがポイントです)。

今回調べて初めて知ったのですが、 docker の場合、起動したコンテナに対して、元にしたイメージを最新版に更新するには、一度コンテナを削除して再度立ち上げる必要がある、という挙動になるそうです。ちなみに、 QNAP の Container Station には再作成というのがありますが、やってることは一度削除して同じ名前で作っているだけですので、本質的には同じですね。

となると、せっかくデータを残したいと思って、わざわざフォルダを指定しているのに、QNAP でコンテナを作り直すと、そこのデータが消えてしまうことになります(もっとも、アプリケーション名の外部のフォルダを最初から指定しておけば、消えることはないですけどね)。

ということで、QNAP 上でコンテナ(アプリケーション)を消しても、消えてしまわないように、永続データを持ち方を変更することにします(一応書いておくと、 QNAP 上から操作しないのであれば不要な作業になります)。

データの保存方法

docker でのデータの保存方法を調べてみると、下記のドキュメントにあるように3つ存在するようです。

Docker でのデータ管理 — Docker-docs-ja 24.0 ドキュメント

しかし、 tmpfs マウントはメモリ上にしか作られないので、ホスト側にファイルを作るものとしては、バインドマウントとボリュームになります。ボリュームにも、名前付きボリュームと匿名ボリュームがあるそうです。今回は、 docker が管理する名前付きボリュームを使ってみようと思います。

ちなみに、ネットを調べていると、この『ボリューム』という用語が、上記のドキュメントにある docker が管理する領域を利用する方法を指す場合と、バインドマウントを含めてホストとファイルを共有する方法という2つの意味で使われているため、なかなかややこしいですね。

永続化したいデータを名前付きボリュームへ移動

ということで、バインドマウントではなく、名前付きボリュームを作成して、そちらにデータを持たせるようにします。今回は名前付きボリュームが、既に動かしているアプリケーションのデータを移行する移行先になるため、手作業で名前付きボリュームを作りたいと思います。

名前付きボリュームの作成

作業前の名前付きボリュームはこんな感じになってました。

[ユーザ名@NAS名 ~]$ docker volume ls
DRIVER    VOLUME NAME
local     2f40159df0420ac580e90daf4ab80d6d7e6254702ccdc2dee6e1c919fe4e38ca
local     89150fae22bbd638b52d990488969696fedf86e9c9c82b51c3e5980634f21bdf
local     186999773b4d354046bcefbfcfc9fad5fb034ecf97d21a0eddde94e92c4cf710
local     c30c7cc6ef15fe2f9ace1a799d4afa1a0e1935eb0ebcb745f87e48aa261e9cfe
local     wp_db_data
local     wp_www
[ユーザ名@NAS名 ~]$ 

まずは、名前付きボリュームを作ります。今回の Roundcube アプリケーションでは3つのボリュームを使っているので、3つ作ります。

[ユーザ名@NAS名 ~]$ docker volume create rcmail_ssl_certs
rcmail_ssl_certs
[ユーザ名@NAS名 ~]$ docker volume create rcmail_www
rcmail_www
[ユーザ名@NAS名 ~]$ docker volume create rcmail_sqlite
rcmail_sqlite
[ユーザ名@NAS名 ~]$ docker volume ls
DRIVER    VOLUME NAME
local     2f40159df0420ac580e90daf4ab80d6d7e6254702ccdc2dee6e1c919fe4e38ca
local     89150fae22bbd638b52d990488969696fedf86e9c9c82b51c3e5980634f21bdf
local     186999773b4d354046bcefbfcfc9fad5fb034ecf97d21a0eddde94e92c4cf710
local     c30c7cc6ef15fe2f9ace1a799d4afa1a0e1935eb0ebcb745f87e48aa261e9cfe
local     rcmail_sqlite
local     rcmail_ssl_certs
local     rcmail_www
local     wp_db_data
local     wp_www
[ユーザ名@NAS名 ~]$ 

データの移行作業

次に、今のバインドマウントにあるデータを作成した名前付きボリュームに移行させます。念のため、一度アプリケーションを停止させ、作業前にアプリケーションフォルダ以下にあるデータのバックアップを取っておきます。

バックアップが取れたら、次にデータを名前付きボリュームにコピーします。名前付きボリュームへデータをコピーするには、一時的な作業用のコンテナを立ち上げて、それに対してファイルをコピーするのが一番簡単で確実なようです。

一時的な作業用コンテナは busybox を利用しています。立ち上げ時に -v で名前付きボリュームをコンテナ内のフォルダにマウントしておきます。

[ユーザ名@NAS名 アプリケーション名]$ ls
data/  db/  docker-compose.resource.yml  docker-compose.yml  qnap.json  www/
[ユーザ名@NAS名 アプリケーション名]$ 
[ユーザ名@NAS名 アプリケーション名]$ docker run -d --name tmp_container -v rcmail_ssl_certs:/data busybox tail -F /dev/null
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
ec562eabd705: Pull complete 
Digest: sha256:9ae97d36d26566ff84e8893c64a6dc4fe8ca6d1144bf5b87b2b85a32def253c7
Status: Downloaded newer image for busybox:latest
c764efa16e97b60f0e23d8a807db5405855123b3b88efdcf8a7bffaec5e8315c
[ユーザ名@NAS名 アプリケーション名]$ 

ここで tail -F /dev/null を指定しておくことで、なにもせず停止もしないコンテナを立ち上げることができます。なお、今回は始めて busybox を使ったのでイメージのダウンロードとかも行われました。

コンテナ(tmp_container)が起動したら docker cp でファイルをコピーします。

[ユーザ名@NAS名 アプリケーション名]$ docker cp data/ssl_certs/. tmp_container:/data/
ERRO[0000] Can't add file /(フルパス)/(アプリケーション名)/data/ssl_certs/default_server/default_server.key to tar: open /(フルパス)/(アプリケーション名)/data/ssl_certs/default_server/default_server.key: permission denied 
ERRO[0000] Can't add file /(フルパス)/(アプリケーション名)/data/ssl_certs/dhparam.pem to tar: archive/tar: missed writing 1704 bytes 
(略)
Error response from daemon: Error processing tar file(exit status 1): unexpected EOF
[ユーザ名@NAS名 アプリケーション名]$ 

これでうまくいくはんずだったんですが、証明書関係のファイルについてはコピー時にパーミッションで怒られてしまいました。なので、sudoをつけて再実行してみます。

[ユーザ名@NAS名 アプリケーション名]$ sudo docker cp data/ssl_certs/. tmp_container:/data/
Password: 
[ユーザ名@NAS名 アプリケーション名]$ 

問題なくコピーできました。作業が終われば、このコンテナは削除しておきます。

[ユーザ名@NAS名 アプリケーション名]$ docker stop tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ docker rm tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ 

同様に、他の2つの名前付きボリュームにもコピーしておきます

[ユーザ名@NAS名 アプリケーション名]$ docker run -d --name tmp_container -v rcmail_www:/data busybox tail -F /dev/null
af2454e5010061c4d4fae5ff27d8ac7245a78716f3e8a72abf94b078b2ab3e28
[ユーザ名@NAS名 アプリケーション名]$ docker cp www/. tmp_container:/data/
[ユーザ名@NAS名 アプリケーション名]$ docker stop tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ docker rm tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ 
[ユーザ名@NAS名 アプリケーション名]$ docker run -d --name tmp_container -v rcmail_sqlite:/data busybox tail -F /dev/null
e5326f49e14e4a2847611ed41c4d796181a975d8cd2414defe69adf6fe0e544e
[ユーザ名@NAS名 アプリケーション名]$ docker cp db/sqlite/. tmp_container:/data/
[ユーザ名@NAS名 アプリケーション名]$ docker stop tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ docker rm tmp_container
tmp_container
[ユーザ名@NAS名 アプリケーション名]$ 

なお、バインドマウントの data と db については、そのサブフォルダをコピーしている点、ご注意ください。

アプリケーションの再起動

ここまでできたら、docker-compose.yml を変更して、名前付きボリュームを使って起動するようにしてみます。

コンテナイメージにタグ付け

その前に、いまのコンテナイメージにタグ付けを行っておきます。この後 Roundcube のアップデートなどをやり始めると、コンテナイメージをいろいろと変更することになるので、現状動作しているイメージを見失わないようにするためです。

【Docker】イメージ名とタグ名を変更する方法 | エンジニアの眠れない夜

まずは、対象となるイメージのイメージ ID を調べます。

[ユーザ名@NAS名 アプリケーション名]$ docker images
REPOSITORY                  TAG                         IMAGE ID       CREATED         SIZE
myroundcube                 latest                      664cbf63c985   10 months ago   596MB
[ユーザ名@NAS名 アプリケーション名]$ 

イメージIDがわかりましたのでタグをつけます。

[ユーザ名@NAS名 アプリケーション名]$ docker tag 664cbf63c985 myroundcube:0.1

タグが変わったことを確認します。

[ユーザ名@NAS名 アプリケーション名]$ docker images
REPOSITORY                  TAG                         IMAGE ID       CREATED         SIZE
myroundcube                 0.1                         664cbf63c985   10 months ago   596MB
myroundcube                 latest                      664cbf63c985   10 months ago   596MB
[ユーザ名@NAS名 アプリケーション名]$ 

無事変わったというか、タグが増えてますね。

アプリケーションの起動

QNAP の Container Station から『アプリケーション』を選択し、いま動いているアプリケーションを選びます。歯車アイコンをクリックし、『再作成』を選びます。

docker-compose.yml を入力する画面が表示されるので、下記のようにします。

version: '3'

services:
  https-portal:
    image: steveltn/https-portal:1.23.1
    ports:
      - 'xxxx:80'
      - 'yyyy:443'
    restart: always
    volumes:
      - rcmail_ssl_certs:/var/lib/https-portal
    environment:
      DOMAINS: 'xxxx.yyyy.zzzz -> http://roundcubemail:80'
      STAGE: 'production'
      # FORCE_RENEW: 'true'
      CLIENT_MAX_BODY_SIZE: 0
  
  roundcubemail:
    image: myroundcube:0.1
    container_name: myrc
#    restart: unless-stopped
    volumes:
      - rcmail_www:/var/www/html
      - rcmail_sqlite:/var/roundcube/db
#    ports:
#      - 9001:80
    environment:
      - ROUNDCUBEMAIL_DB_TYPE=sqlite
      - ROUNDCUBEMAIL_SKIN=elastic
      - ROUNDCUBEMAIL_DEFAULT_HOST=ssl://メールサーバー名
      - ROUNDCUBEMAIL_DEFAULT_PORT=ポート番号
      - ROUNDCUBEMAIL_SMTP_SERVER=ssl://メールサーバー名
      - ROUNDCUBEMAIL_SMTP_PORT=ポート番号
      - ROUNDCUBEMAIL_UPLOAD_MAX_FILESIZE=30M

volumes:
    rcmail_ssl_certs:
        external: true
    rcmail_www:
        external: true
    rcmail_sqlite:
        external: true

以前のものと比較して

  • バインドマウントではなく、ボリュームを指定した
  • アプリケーションのイメージのタグをバージョン番号まで含めて指定した

という点が主に異なる点になります。

入力が終わったら、『更新』ボタンを押します。

しばらく待つと、既存のアプリケーション(およびコンテナ)の削除が行われ、続いて作成が行われます。なお、画面上の進捗表示がうまく更新されないことがあるようで、しばらく待っても変化がないような場合は、 ContainerStation 自体を一度閉じてから再度開くと進展がある場合があります。

エラー等がなく無事に起動したら完了です。 あとは、 Roundcube にログインして、問題なく動作するか確認しておきます。特に問題もなく無事に動いていました。

まとめ

これで、永続データを(狭義の)ボリュームに移行できたので、次は Roundcube 自体のアップデートを行ってみたいと思います。