CID Superfecta is garbled

Please note that we use DeepL translations.

When I use Japanese characters in CID Superfecta, they are garbled as shown in the image.
The characters entered are “テストユーザー架空”.

Only FreePBX Contactmanager is used as the data source, and Display Name is selected as the Return Format.
It appears to be converted to UTF-8, but I think Japanese can be displayed if it is UTF-8.
Why are the characters garbled?

FreePBX version is 16.0.26, Asterisk version is 18.15.0, and CID Superfecta version is 16.0.19.
Thank you in advance for your cooperation.

残念ながら回答や情報を得られなかったため、自身で調べて解決をいたしました。
細かいニュアンスを伝えるのが難しいので、申し訳ないですが日本語のまま書かせていただきます。
昨今はGoogle翻訳やDeepL翻訳が発達していますので、翻訳してお読みいただければと思います。
日本、もしくは非英語圏の方の参考になりましたら幸いです。

結論から申し上げると、原因はCID Superfectaのバグです。
バグというよりは、ISO-8859-1で表現できない文字を考慮していないプログラムです。
このフォーラムでもドイツの方が似たような問題を報告しているのを見掛けました。
ただ、日本語が文字化けする問題とは少々異なりますが…

まず、superfecta_single.php内の64行目でUTF8のデコード処理が行われています。

$caller_id = $this->_utf8_decode($caller_id);

_utf8_decode関数の内容を見ると、html_entity_decodeを通した後にutf8_decodeを通して値を返しているようです。

function _utf8_decode($string) {
	$string = html_entity_decode($string);
	$tmp = $string;
	$count = 0;
	while ($this->isutf8($tmp)) {
		$tmp = utf8_decode($tmp);
		$count++;
	}

	for ($i = 0; $i < $count - 1; $i++) {
		$string = utf8_decode($string);
	}
	return $string;
}

html_entity_decodeを通すのはいいとして、なぜUTF8をISO-8859-1にデコードしているのかよくわかりません。
UTF8のままで問題はないかと思いますし、そもそもutf8_decodeはPHP 8.2.0で非推奨となり、今後使用しないことが強く推奨されています。

そして84行目で結果を表示する際に、わざわざISO-8859-1にデコードした結果を一時的にUTF8へ再エンコードして表示するというまた意味の分からない処理を行っています。
さらにここでもutf8_encodeという将来的に非推奨となる関数が使われています。

print "'" . utf8_encode($caller_id) . "'<br>\nresult <i class=\"fa fa-hand-o-up\"></i> took " . number_format(($this->mctime_float() - $start_time), 4) . " seconds.<br>\n<br>\n";

UTF8で受け取ったデータを変換して内部的にISO-8859-1で保持し、表示の際にわざわざUTF8に直すのであれば、最初からUTF8で持っていればいいように思います…
コメントや仕様書を見たわけではないので正直理解に苦しみますが、このような処理が行われているようです。
そして、当然ですがISO-8859-1は日本語を扱えませんので既にここで文字化けします。

次に、stripAccentsを使って入力された文字列から分音記号を削除する処理が入ります。
具体的にはSuperfecta.class.phpの211行目でif文による分岐が入り、IA5文字コードありと判断された場合、

$callerid = $superfecta->stripAccents($callerid);

このような処理が実行されます。

この処理にも正直何の意味があるのかわかりません。
UTF8であれば分音記号を含む文字を正しく表現できます。
さらに言えば、ISO-8859-1でも分音記号を扱うことができます。
分音記号を表現することに不都合があるのであれば別ですが、ドイツ語を使用しないので分かりかねます…

それはともかく、このisCharSetIA5はIA5の文字コードを含むか含まないかを特に判別していないように見えます。
関数の内部を見ると

protected $charsetIA5 = true;

function isCharSetIA5() {
	return $this->charsetIA5;
}

でtrueが返って来るだけなので、将来的に判別が必要になったときを考慮して処理を入れるようにしたが、現状英語が処理されても問題ないためすべての結果を処理するようになっているのでしょうか。
ただ、当然日本語は問題で、文字コードがおかしなことになっている状態で通されるので更にめちゃくちゃな文字になって帰ってきます。
また、この関数は内部でhtml_entity_decodeを行っています。

function stripAccents($string) {
	$string = html_entity_decode($string);
	$string = strtr($string, "äåéöúûü•µ¿¡¬√ƒ≈∆«»… ÀÃÕŒœ–—“”‘’÷ÿŸ⁄€‹›fl‡·‚„‰ÂÊÁËÈÍÎÏÌÓÔÒÚÛÙıˆ¯˘˙˚¸˝ˇ", "SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy");
	$string = str_replace(chr(160), ' ', $string);
	return $string;
}

_utf8_decode関数の内部でhtml_entity_decodeを通して、またここでも通しています。
二重に処理をする意味が分かりませんし、正直無駄な気がします。

そして仕上げに、223行目にてこの壊れた文字データをUTF8に再変換するという処理を行っています。
ここではmb_convert_encodingを使用していますが、問題は入力される文字コードがISO-8859-1と決め打ちされています。
既に分音記号の削除処理がされている時点でまともな文字データにはなっていないのですが、それを更にISO-8859-1としてUTF8に直すという処理が行われています。

まとめると、UTF8のデータをISO-8859-1にわざわざデコードし、ラテン文字しか使用できず他は文字化けするように変換し、ブラウザ上にprintするときは一時的にUTF8でエンコードしてから表示させ、そこから分音記号の削除処理をされ、今度はそれをUTF8に直すというめちゃくちゃな処理を行っています。
これがCID Superfectaが日本語を正しく処理できない原因です。
というより、日本語どころかISO-8859-1で表現できない文字は全て扱えない、文字化けするかと思います。

修正方法ですが、まずは意味がないと思われるUTF8のデコード処理を削除します。
superfecta_single.php内の64行目

$caller_id = $this->_utf8_decode($caller_id);

これをコメントにします。

次に結果を返す84行目の処理にて

utf8_encode($caller_id)

$caller_id

に修正します。

これでまずソースから発見した際に表示される結果の文字化けが解消されます。

ただ、これはsingleの場合で、multiの場合はsuperfecta_multi.phpの修正も必要かもしれません。
singleの64行目と同じ記述がmultiの315行目にありますので自分は念のためそこも削除しています。

次に、分音記号削除処理を削除します。
Superfecta.class.php内の211行目

$callerid = $superfecta->stripAccents($callerid);

これをコメントにします。

最後にSuperfecta.class.php内の223行目

$callerid = mb_convert_encoding($callerid, “UTF-8”, “ISO-8859-1”);

これを

$callerid = mb_convert_encoding($callerid, “UTF-8”);

このように修正します。
文字コードをmb_convert_encodingによって自動認識してもらい、UTF8に直してもらうという感じです。
ここで今まで変換処理をしなかった各種文字コードをUTF8に変換するので、入力がドイツ語でもなんでも最終的には正しく表記されるはずです。

結果は御覧の通り、日本語とドイツ語の分音記号が混在した文字でも正しく処理できました。

問題は、この変更を行うと署名チェックに引っかかり、システムが改竄されていると警告されることです。
気にならない方はそのままでもいいかと思いますが、あまり気持ちのいいものではないのでこちらも対策をします。

まずは下記のWikiに従って、GPG鍵を生成します。
https://wiki.freepbx.org/display/FOP/GPG+Key+Generation+HowTo
手順ではキーサイズが4096と指定されていますが、ウォークスルーでは2048で生成しています。
自分は念のため4096で生成しました。

今回はローカル署名にしますが、Sangoma様に依頼を出して正式にモジュールに署名出来るようにすることもできます。
ただ身分証など、多分英語表記があるパスポートなどの提出が必要かと思いますので、ローカルで十分かと思います。
環境によっては登録されている鍵サーバーが古く、鍵を登録できないためサーバーの変更が必要になることがあるかと思います。
少なくとも公式のDistroはサーバーの変更が必要でした。
手順は上記Wikiの最後の方に記載されています。

鍵が生成できましたら下記に従ってGithubより開発ツールキットをダウンロードします。
https://wiki.freepbx.org/display/FOP/Signing+your+own+modules#Signingyourownmodules-LocalKeyWalkthrough
後はLocal Key Walkthroughに従って署名を行えば、生成した鍵によるローカル署名で改竄警告が消えるかと思います。

この情報が日本の方、ドイツの方、Superfectaの文字化けに悩んでいる非英語圏の方の助けになりましたら幸いです。

これをIssueとして報告することも考えましたが、私の環境ではうまく動いているものの、他の方の環境では正常に動くかわからないこと
処理の流れを完全にトレースしたわけではないので、削除したUTF8のデコードが実は意味のあるものである可能性があること(うちでは問題は出ていませんが…)
この問題を最初にドイツの方がフォーラムに投稿した際に、他の方からバグではないと言われ、それに対して細かく説明するという流れを拝見しましたが
自分はそれを日本語ならまだしも英語で同じように説明し、検証をしてもらうのは正直面倒なので自前で修正して誤魔化すことにしました。

最後に、英語にてこのような長文を正確に表現するスキルがないため、日本語での投稿になったことをお詫びします。