プログラマーのメモ書き

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

(Windows) Flutter SDK のアップグレード, 3.7.12 -> 3.22.2

Flutter のプロジェクトを更新するにあたり、 Flutter SDK も更新しました。以前もやってるんですが、まとめたことがなかったので、一応、その時の作業手順をメモっておきます。

更新したい SDK のダウンロード

まずは更新したいバージョンの SDK をダウンロードしてきてから、そちらを使うように設定を変更するという方法を取っています。特に大きな理由もないんですが、アップグレード後にもし問題があったときに確実に戻したいので、 flutter upgrade コマンドを使わないという感じです。

もっとも、素直に fvm (Flutter version manager) を入れてもいいんでしょうが、今のところ Flutter のバージョンを切り替えて作業を行う必要性がないので、こんな風にしています。

というわけで、現時点の最新版をダウンロードしてきます。

ダウンロード後は解凍して、 SDK を保存したいディレクトリに置いておきます。

環境変数の更新

Windows の『設定』->『システム』->『バージョン情報』を開き、『システムの詳細設定』をクリックし、環境変数(ユーザーごとの環境変数)の Path にある Flutter SDK へのパスを、ダウンロードしてきたものに修正します(公式の手順はこちらを参照してください)。

パス設定が正しいことを、コマンドラインでも確認しておきます。

C:\Users\mor>flutter --version
Flutter 3.22.2 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 761747bfc5 (3 weeks ago) • 2024-06-05 22:15:13 +0200
Engine • revision edd8546116
Tools • Dart 3.4.3 • DevTools 2.34.3
The Flutter CLI developer tool uses Google Analytics to report usage and diagnostic
data along with package dependencies, and crash reporting to send basic crash
reports. This data is used to help improve the Dart platform, Flutter framework,
and related tools.

Telemetry is not sent on the very first run. To disable reporting of telemetry,
run this terminal command:

    flutter --disable-analytics

If you opt out of telemetry, an opt-out event will be sent, and then no further
information will be sent. This data is collected in accordance with the Google
Privacy Policy (https://policies.google.com/privacy).


C:\Users\mor>

問題ないですね。

Flutter プロジェクトの設定変更

さて、 Android Studio で既存の Flutter プロジェクトを開くと、 Flutter SDK が古いままになっています。なので、これを更新してやる必要があります。

『File』->『Settings』->『Languages & Frameworks 』->『Flutter』 を開くと、 SDK を指定する箇所があります。

これを新しいバージョンの SDK を指すように変更します。

なお、ここでは、

(SDK インストールディレクトリ)/flutter

を指定します(先ほどの Path は flutter/bin だったので間違えないように気をつけます)。

設定を確実にするため、一度、 Android Studio を終了させて、再度プロジェクトを開きます。

SDK 変更による影響のチェック

プロジェクトを開きなおしたら、 build ディレクトリをいったん削除してから、再度ビルドします(実機で実行させます)。 これで問題なく動作できれば完了だったんですが、今回は一部 Breaking Changes があったようで、エラーとなりました。

なお、 Flutter SDK 3.7.12 は Dart 2.19.6 になります。一方、 3.22.2 は Dart 3.4.3 になります。 Flutter SDK 3.7.12 の時点で null safety には対応していたので、こんなアップグレードのやり方をしています。

さて、問題があったところを地道に修正してもいいんですが、 dart fix コマンドを利用すると、ある程度自動的に修正してくれるそうです。Android Studio でも下記のように dart fix の存在を教えてくれています。

早速やってみます。最初は一気に修正するのではなく --dry-run をつけて、様子をみるのが無難そうです。

D:\work\sample_project>dart fix --dry-run
The Dart CLI developer tool uses Google Analytics to report usage and diagnostic
data along with package dependencies, and crash reporting to send basic crash
reports. This data is used to help improve the Dart platform, Flutter framework,
and related tools.

Telemetry is not sent on the very first run. To disable reporting of telemetry,
run this terminal command:

    dart --disable-analytics

If you opt out of telemetry, an opt-out event will be sent, and then no further
information will be sent. This data is collected in accordance with the Google
Privacy Policy (https://policies.google.com/privacy).

Computing fixes in sample_project (dry run)...

36 proposed fixes in 17 files.

lib\feature\common.dart
  unnecessary_cast • 2 fixes
(中略)
To fix an individual diagnostic, run one of:
  dart fix --apply --code=invalid_null_aware_operator
  dart fix --apply --code=undefined_getter
  dart fix --apply --code=unnecessary_cast
  dart fix --apply --code=unnecessary_null_comparison
  dart fix --apply --code=unused_catch_clause
  dart fix --apply --code=unused_element
  dart fix --apply --code=unused_import

To fix all diagnostics, run:
  dart fix --apply

D:\work\sample_project>

指摘されたもののうち、特定のもののみを修正できるようです。

D:\work\sample_project>dart fix --apply --code=undefined_getter
Computing fixes in sample_project...
Applying fixes...

lib\feature2\common2.dart
  undefined_getter • 2 fixes
(略)
10 fixes made in 5 files.

D:\work\sample_project>

これを使えば、少しずつ修正内容を確認しながら手直しできるので、いいですね。

パッケージのアップグレード

自分のアプリ側の修正だけで終わればよかったのですが、依存しているライブラリで問題があるものがありました。

なので、これらも新しいものに替えておきます。

で、最終的にビルドが通るようになれば、完了です。

おまけ

ちなみに、最初は Flutter SDK をダウンロードするというやり方だったのを忘れていて、いきなり flutter upgrade やってしまいました。このとき、

C:\Users\mor>flutter upgrade
Your flutter checkout has local changes that would be erased by upgrading. If you want to keep these changes, it is
recommended that you stash them via "git stash" or else commit the changes to a local branch. If it is okay to remove
local changes, then re-run this command with "--force".

C:\Users\mor>

のように警告が出てきて、 upgrade に失敗しました。似たようなことで困っている人は結構いるようで、ネットにもいろいろと落ちてました。

実際に、 SDK のディレクトリに移動して、 git status を見ると

C:\Users\mor\bin\flutter_windows_3.7.12-stable\flutter>git status
On branch stable
Your branch and 'origin/stable' have diverged,
and have 53 and 7731 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .pub-preload-cache/

nothing added to commit but untracked files present (use "git add" to track)

C:\Users\mor\bin\flutter_windows_3.7.12-stable\flutter>

.pub-preload-cache 以下が未登録になってます。なるほど、これでアップグレードできなかったようですね。

ご参考までに。

まとめ

若干手間取りましたが、無事に Flutter SDK を更新できました。まあ、Flutter 3.7.12 (Dart 2.19.6) の時に Dart 3 の null safety への対応を一通りやっておいたため、比較的簡単だったんでしょうね。

とりあえず、これで(ほぼ)最新の環境で開発を進められます。

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;[mor9admin1@nas01 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系の最新版にしたので、当面はこれでいいかなと思ってます。