プログラマーのメモ書き

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

Roundcube の identy_switch がいつのまにやらアップデートされている

先日から、 Roundcube のフォルダ表示がおかしくなってます。

いろいろと試してみたところ、メールアドレス毎に『特殊なフォルダ』に割り当てているフォルダ設定があるのですが、どうもそれがメールアドレス毎ではなく、全体として共通化して使われているような印象です。どういうことだ?

Roundcube で複数のメールアドレスを扱うため、 identy_switch というプラグインを入れているのですが、これが関連している気がしたので、念のため確認してみると、現在のバージョン番号が

1.0.29

となってます。ん?こんなバージョンだったかな?

気になって、 identy_switch をインストールした際の記事(こちらの記事)を確認すると、 1.0.17 になってます。

なんだ?どうやら、いつのまにやらバージョンアップされているみたいです。

このままでは安心して使えないので、いろいろと調べてみたので、その時の経緯をメモっておきます。

問題が起きた原因

正確には覚えてないですが、最近 Roundcube の挙動がおかしいな?と思い始めたのと同じような時期に、 Roundcube の docker コンテナを動かしている QNAP の ファームウェア(QTS と呼んでます)のアップデートを行いました。

更新した日付を確認すると、

  • 2024/6/10, QTS 5.1.7.2770 に更新
  • 2024/6/24 ごろ, Roundcube のコンテナの運用を開始
  • 2024/8/22, QTS 5.2.0.2860 に更新

Roundcube のコンテナの運用を開始した日というのは、 コンテナの作成日が

[ユーザ名@NAS名 ~]$ docker inspect myrc
[
    {
        "Id": "30ca2b407e25bd387b616a5ce0504260a5699e51e9a6ef73746b4519f570ea0f",
        "Created": "2024-06-24T04:36:57.43125077Z",
        "Path": "/docker-entrypoint.sh",
(略)

と、 2024/6/24 だったことから推測しました。

その後、 先日の QNAP のファームウェア 5.2.0 の更新時まで、 QNAP 側のログを確認してみると QNAP 自体の再起動はなかったようです。

QNAPのファームウェアを更新したとき(QNAP を再起動したとき)って、 Rooundcube のコンテナが起動しないため、 QNAP の Container Station から再起動を行っています。

ということは QNAP の再起動による Roundcube の再起動がないとすると、特に何かトラブルがあった記憶もないので、 Roundcube も動きっぱなしだった可能性が高いです。

ということで、ファームウェアのアップデートに伴う Roundcube の再起動時に何か起きた可能性があるんじゃないかな?と推測されますね。

確認作業

Roundcube のコンテナの再起動が絡んでそうなので、もう一度 Roundcube のコンテナを再起動して、画面のログを確認して、何かアップデート処理についてのヒントがないか確認してみることにします。

実際に試してみると、なんと、 identy_switch のバージョンが 1.0.30 になってました! (画面のスクリーンショット取り忘れてます)

Roundcube のコンテナのログ画面を見ると、

identy_switch のバージョンが 1.0.29 -> 1.0.30 にアップデートされている様子が出力されています。詳しい挙動は不明ですが、 Roundcube の(再)起動時にプラグインのアップデートが動いているのは間違いないようですね。

なお、一応、 identy_switch のバージョン履歴を見てみると、

  • 2024/5/25, 1.0.17
  • 2024/6/30, 1.0.18
  • 2024/8/4, 1.0.29
  • 2024/8/24, 1.0.30

のようになってました。

ということで、 Roundcube に最初に identy_switch をインストールしたタイミングでは 1.0.17 になっていて、その後そのままで稼働していましたが、 QNAP のファームウェアアップデートのタイミングで 1.0.29 になって、上記のテスト(2024/8/24 に実施)で 1.0.30 になったという流れのようです。

こういう挙動だということがわかったので、 Roundcube 関連で何か見つからないかと調べてみると、再起動時の問題についての記事もありました。

My custom soundfile for the newmail_notifier plugin gets overwritten

コンテナ再起動したら、プラグインがアップデートするなんて気づかないですよね。

ちなみに、 identy_switch の 1.0.30 にアップデート後は、ずっと『再表示中』となって、そのうち接続が切れるという状態になっていしまって、まともにメールのチェックもできなくなってしまってました。

アップデートの詳細

実際にどのような経緯でプラグインのアップデートが呼ばれているのか確認してみました。先ほどの画面に出力されているログの文言を grepにかけて追いかけてみると、下記のように呼び出されていることがわかりました。

Roundcube の dockerfile に書かれている ENTRYPOINT が /docker-entrypoint.sh になっています。

このため、コンテナの実行時に /docker-entrypoint.sh が実行されます。/docker-entrypoint.sh の中では、

  # update Roundcube in docroot
  else
    INSTALLDIR=`pwd`
    echo >&2 "roundcubemail found in $INSTALLDIR - installing update..."
    (cd /usr/src/roundcubemail && bin/installto.sh -y $INSTALLDIR)
    composer update --no-dev
  fi

という感じで、 bin/installto.sh を呼び出しています。

この中身は /var/www/html 以下にもあるのでこれを確認すると、 installto.sh は処理の最後付近で、

    echo "Running update script at target...\n";
    system("cd $target_dir && bin/update.sh --version=$oldversion" . ($accept ? ' -y' : ''));
    echo "All done.\n";

このようにさらに bin/update.sh を呼び出しています。で、この update.sh は内部で、

            if ($composer_bin = find_composer()) {
                echo "Executing " . $composer_bin . " to update dependencies...\n";
                echo system("$composer_bin update -d " . escapeshellarg(INSTALL_PATH) . " --no-dev", $exit_code);
            }

という形で、 composer update が呼ばれていました。

ついでに、改めて、最初の docker-entrypoint.sh を見ると、bin/installto.sh の次の行でも composer update が再度呼ばれています(実際にログをみると、 composer のアップデートが2回呼び出されている形跡が見られます)。

Roundcube コンテナの実行時に確実に composer の更新が呼ばれるようになってるみたいですね。

composer.json の内容

ということで、Roundcube のコンテナを(再)起動すると composer update が実行されることはわかりました。次に、更新対象が書かれている composer.json ファイルの中身を確認してみます。

/var/www/html/composer.json を見てみると、

{
    "name": "roundcube/roundcubemail",
    "description": "The Roundcube Webmail suite",
    "license": "GPL-3.0-or-later",
    "repositories": [
        {
            "type": "composer",
            "url": "https://plugins.roundcube.net"
        }
    ],
    "require": {
        "php": ">=7.3.0",
        "pear/pear-core-minimal": "~1.10.1",
        "pear/auth_sasl": "~1.1.0",
        "pear/mail_mime": "~1.10.0",
        "pear/net_smtp": "~1.10.0",
        "pear/crypt_gpg": "~1.6.3",
        "pear/net_sieve": "~1.4.5",
        "roundcube/plugin-installer": "~0.3.1",
        "roundcube/rtf-html-php": "~2.1",
        "masterminds/html5": "~2.7.0",
        "guzzlehttp/guzzle": "^7.3.0",
        "kolab/net_ldap3": "~1.1.1",
        "bacon/bacon-qr-code": "^2.0.0",
        "toteph42/identy_switch": "^1.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6 || ^7"
    },
    "suggest": {
        "bjeavons/zxcvbn-php": "^1.0 required for Zxcvbn password strength driver"
    },
    "config": {
        "allow-plugins": {
            "roundcube/plugin-installer": true
        }
    }
}

identy_switch については "^1.0" となってます。つまり、マイナーバージョンアップは自動的に反映されてしまいます。

最初の identy_switch のインストール時に

root@52cfd961e6e2:/var/www/html# composer require toteph42/identy_switch

とだけ指定したのが、 composer.json がこの形になった原因ですね、きっと。

このため、 composer update により(勝手に)バージョンが上がったようです。

対処

このままではメールの送受信が非常に不便なので、取り急ぎ使えるように identy_switch を少なくとも実用に耐えていた 1.0.17 にダウングレードして戻したいと思います。

SQLite のスキーマの確認

作業を始める前に、 identy_switch に関連するテーブルのスキーマに変更がないか見ておきます。 Roundcube コンテナに入って、 sqlite3 コマンドでスキーマを確認すると、幸い以前のものと変更はないようです(以前のスキーマはこちらの記事に残ってました)。

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>

バックアップ

次に、下記などを参考にして、データボリュームのバックアップを取っておきます。

【図解付き】Docker Data Volumeのバックアップ・リストア方法 #MySQL - Qiita

[ユーザ名@NAS名 20240825]$ docker run --rm --volumes-from myrc -v `pwd`:/backup busybox tar cvf /backup/backup_rcmail_www.tar /var/www/html
[ユーザ名@NAS名 20240825]$ docker run --rm --volumes-from myrc -v `pwd`:/backup busybox tar cvf /backup/backup_rcmail_sqlite.tar /var/roundcube/db

ダウングレード

Roundcube コンテナにログインして、 composer で identy_switch をダウングレードしてみます。

よく使うcomposerコマンドとバージョン指定方法の備忘録 | tanden techblog

root@30ca2b407e25:/var/www/html# composer require toteph42/identy_switch:"1.0.17"
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
Loading composer repositories with package information
Updating dependencies
Lock file operations: 0 installs, 1 update, 0 removals
  - Downgrading toteph42/identy_switch (1.0.30 => 1.0.17)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 19 installs, 1 update, 0 removals
  - 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)
  - Downloading toteph42/identy_switch (1.0.17)
  - 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
  - Downgrading toteph42/identy_switch (1.0.30 => 1.0.17): Extracting archive
 15/20 [=====================>------]  75%Restored identy_switch/config.inc.php
Updating database schema for identy_switch
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@30ca2b407e25:/var/www/html# 

これでアクセスしてみると、おぉ、問題なく使えるようになりました。

composer.json の確認

ダウングレード後、 composer.json を見てみると

{
    "name": "roundcube/roundcubemail",
    "description": "The Roundcube Webmail suite",
    "license": "GPL-3.0-or-later",
    "repositories": [
        {
            "type": "composer",
            "url": "https://plugins.roundcube.net"
        }
    ],
    "require": {
        "php": ">=7.3.0",
        "pear/pear-core-minimal": "~1.10.1",
        "pear/auth_sasl": "~1.1.0",
        "pear/mail_mime": "~1.10.0",
        "pear/net_smtp": "~1.10.0",
        "pear/crypt_gpg": "~1.6.3",
        "pear/net_sieve": "~1.4.5",
        "roundcube/plugin-installer": "~0.3.1",
        "roundcube/rtf-html-php": "~2.1",
        "masterminds/html5": "~2.7.0",
        "guzzlehttp/guzzle": "^7.3.0",
        "kolab/net_ldap3": "~1.1.1",
        "bacon/bacon-qr-code": "^2.0.0",
        "toteph42/identy_switch": "1.0.17"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6 || ^7"
    },
    "suggest": {
        "bjeavons/zxcvbn-php": "^1.0 required for Zxcvbn password strength driver"
    },
    "config": {
        "allow-plugins": {
            "roundcube/plugin-installer": true
        }
    }
}

のように、バージョン番号が入っています。

これで、いつのまにやらアップデートされているということがなくなりますね。

まとめ

Roundcube で composer を使ったプラグインについて、再起動時に自動的にアップデートするとは思ってもみませんでした。

identy_switch のバージョンアップが早いのはメンテナンスされないよりは全然ありがたいんだけど、結構頻繁に更新するのもどうなんだろうか?と思ってしまいます。安定板と開発版とかに分けてくれると助かるんですがね。

なんにしても、思わぬ形でアップデートにより挙動が変わると驚きますね。